blob: 4c3fb9bf655cc927f1d1ab7923ba7258a9838b9e [file] [log] [blame]
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#include <cstdlib>
#include "vm/isolate.h"
#include "vm/json_stream.h"
#include "vm/object.h"
#include "vm/thread.h"
#include "vm/timeline.h"
namespace dart {
DEFINE_FLAG(bool, trace_timeline, false, "Timeline trace");
TimelineEvent::TimelineEvent()
: timestamp0_(0),
timestamp1_(0),
arguments_(NULL),
arguments_length_(0),
state_(0),
label_(NULL),
stream_(NULL),
thread_(NULL) {
}
TimelineEvent::~TimelineEvent() {
Reset();
}
void TimelineEvent::Reset() {
set_event_type(kNone);
thread_ = NULL;
stream_ = NULL;
label_ = NULL;
FreeArguments();
}
int64_t TimelineEvent::AsyncBegin(const char* label) {
Init(kAsyncBegin, label);
timestamp0_ = OS::GetCurrentTimeMicros();
ASSERT(stream_ != NULL);
int64_t async_id = stream_->GetNextSeq();
// Overload timestamp1_ with the async_id.
timestamp1_ = async_id;
return async_id;
}
void TimelineEvent::AsyncInstant(const char* label,
int64_t async_id) {
Init(kAsyncInstant, label);
timestamp0_ = OS::GetCurrentTimeMicros();
// Overload timestamp1_ with the async_id.
timestamp1_ = async_id;
}
void TimelineEvent::AsyncEnd(const char* label,
int64_t async_id) {
Init(kAsyncEnd, label);
timestamp0_ = OS::GetCurrentTimeMicros();
// Overload timestamp1_ with the async_id.
timestamp1_ = async_id;
}
void TimelineEvent::DurationBegin(const char* label) {
Init(kDuration, label);
timestamp0_ = OS::GetCurrentTimeMicros();
}
void TimelineEvent::DurationEnd() {
timestamp1_ = OS::GetCurrentTimeMicros();
}
void TimelineEvent::Instant(const char* label) {
Init(kInstant, label);
timestamp0_ = OS::GetCurrentTimeMicros();
}
void TimelineEvent::Duration(const char* label,
int64_t start_micros,
int64_t end_micros) {
Init(kDuration, label);
timestamp0_ = start_micros;
timestamp1_ = end_micros;
}
void TimelineEvent::SetNumArguments(intptr_t length) {
// Cannot call this twice.
ASSERT(arguments_ == NULL);
ASSERT(arguments_length_ == 0);
arguments_length_ = length;
arguments_ = reinterpret_cast<TimelineEventArgument*>(
calloc(sizeof(TimelineEventArgument), length));
}
void TimelineEvent::SetArgument(intptr_t i, const char* name, char* argument) {
ASSERT(i >= 0);
ASSERT(i < arguments_length_);
arguments_[i].name = name;
arguments_[i].value = argument;
}
void TimelineEvent::FormatArgument(intptr_t i, const char* name,
const char* fmt, ...) {
ASSERT(i >= 0);
ASSERT(i < arguments_length_);
va_list args;
va_start(args, fmt);
intptr_t len = OS::VSNPrint(NULL, 0, fmt, args);
va_end(args);
char* buffer = reinterpret_cast<char*>(malloc(len + 1));
va_list args2;
va_start(args2, fmt);
OS::VSNPrint(buffer, (len + 1), fmt, args2);
va_end(args2);
SetArgument(i, name, buffer);
}
void TimelineEvent::CopyArgument(intptr_t i,
const char* name,
const char* argument) {
SetArgument(i, name, strdup(argument));
}
void TimelineEvent::Complete() {
stream_->CompleteEvent(this);
}
void TimelineEvent::FreeArguments() {
if (arguments_ == NULL) {
return;
}
for (intptr_t i = 0; i < arguments_length_; i++) {
free(arguments_[i].value);
}
free(arguments_);
arguments_ = NULL;
arguments_length_ = 0;
}
void TimelineEvent::StreamInit(TimelineStream* stream) {
ASSERT(stream != NULL);
stream_ = stream;
}
void TimelineEvent::Init(EventType event_type,
const char* label) {
ASSERT(label != NULL);
set_event_type(event_type);
timestamp0_ = 0;
timestamp1_ = 0;
thread_ = Thread::Current();
label_ = label;
FreeArguments();
}
static int64_t GetPid(Isolate* isolate) {
// Some mapping from Isolate* to an integer process id.
// TODO(Cutch): Investigate if process ids can be strings.
return static_cast<int64_t>(reinterpret_cast<uintptr_t>(isolate));
}
static int64_t GetTid(Thread* thread) {
// Some mapping from Thread* to an integer thread id.
// TODO(Cutch): Investigate if process ids can be strings.
return static_cast<int64_t>(reinterpret_cast<uintptr_t>(thread));
}
void TimelineEvent::PrintJSON(JSONStream* stream) const {
JSONObject obj(stream);
int64_t pid = GetPid(Isolate::Current());
int64_t tid = GetTid(thread_);
obj.AddProperty("name", label_);
obj.AddProperty("cat", stream_->name());
obj.AddProperty64("tid", tid);
obj.AddProperty64("pid", pid);
obj.AddPropertyTimeMillis("ts", TimeOrigin());
switch (event_type()) {
case kDuration: {
obj.AddProperty("ph", "X");
obj.AddPropertyTimeMillis("dur", TimeDuration());
}
break;
case kInstant: {
obj.AddProperty("ph", "i");
obj.AddProperty("s", "p");
}
break;
case kAsyncBegin: {
obj.AddProperty("ph", "b");
obj.AddPropertyF("id", "%" Px64 "", AsyncId());
}
break;
case kAsyncInstant: {
obj.AddProperty("ph", "n");
obj.AddPropertyF("id", "%" Px64 "", AsyncId());
}
break;
case kAsyncEnd: {
obj.AddProperty("ph", "e");
obj.AddPropertyF("id", "%" Px64 "", AsyncId());
}
break;
default:
UNIMPLEMENTED();
}
{
JSONObject args(&obj, "args");
for (intptr_t i = 0; i < arguments_length_; i++) {
const TimelineEventArgument& arg = arguments_[i];
args.AddProperty(arg.name, arg.value);
}
}
}
int64_t TimelineEvent::TimeOrigin() const {
return timestamp0_;
}
int64_t TimelineEvent::AsyncId() const {
return timestamp1_;
}
int64_t TimelineEvent::TimeDuration() const {
if (timestamp1_ == 0) {
// This duration is still open, use current time as end.
return OS::GetCurrentTimeMicros() - timestamp0_;
}
return timestamp1_ - timestamp0_;
}
TimelineStream::TimelineStream()
: recorder_(NULL),
name_(NULL),
enabled_(false),
seq_(0) {
}
void TimelineStream::Init(const char* name, bool enabled) {
name_ = name;
enabled_ = enabled;
}
TimelineEvent* TimelineStream::StartEvent(const Object& obj) {
if (!enabled_ || (recorder_ == NULL)) {
return NULL;
}
ASSERT(name_ != NULL);
ASSERT(recorder_ != NULL);
TimelineEvent* event = recorder_->StartEvent(obj);
if (event != NULL) {
event->StreamInit(this);
}
return event;
}
TimelineEvent* TimelineStream::StartEvent() {
if (!enabled_ || (recorder_ == NULL)) {
return NULL;
}
ASSERT(name_ != NULL);
TimelineEvent* event = recorder_->StartEvent();
if (event != NULL) {
event->StreamInit(this);
}
return event;
}
void TimelineStream::CompleteEvent(TimelineEvent* event) {
if (!enabled_ || (recorder_ == NULL)) {
return;
}
recorder_->CompleteEvent(event);
}
int64_t TimelineStream::GetNextSeq() {
seq_++;
if (seq_ < 0) {
seq_ = 0;
}
return seq_;
}
void TimelineDurationScope::FormatArgument(intptr_t i,
const char* name,
const char* fmt, ...) {
if (event_ == NULL) {
return;
}
va_list args;
va_start(args, fmt);
intptr_t len = OS::VSNPrint(NULL, 0, fmt, args);
va_end(args);
char* buffer = reinterpret_cast<char*>(malloc(len + 1));
va_list args2;
va_start(args2, fmt);
OS::VSNPrint(buffer, (len + 1), fmt, args2);
va_end(args2);
event_->SetArgument(i, name, buffer);
}
TimelineEventRecorder::TimelineEventRecorder() {
}
void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const {
Isolate* isolate = Isolate::Current();
JSONObject obj(events);
int64_t pid = GetPid(isolate);
obj.AddProperty("ph", "M");
obj.AddProperty64("pid", pid);
obj.AddProperty("name", "process_name");
{
JSONObject args(&obj, "args");
args.AddProperty("name", isolate->debugger_name());
}
}
void TimelineEventRecorder::WriteTo(const char* directory) {
Isolate* isolate = Isolate::Current();
Dart_FileOpenCallback file_open = Isolate::file_open_callback();
Dart_FileWriteCallback file_write = Isolate::file_write_callback();
Dart_FileCloseCallback file_close = Isolate::file_close_callback();
if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) {
return;
}
JSONStream js;
PrintJSON(&js);
const char* format = "%s/dart-timeline-%" Pd "-%" Pd ".json";
intptr_t pid = OS::ProcessId();
intptr_t len = OS::SNPrint(NULL, 0, format,
directory, pid, isolate->main_port());
char* filename = Thread::Current()->zone()->Alloc<char>(len + 1);
OS::SNPrint(filename, len + 1, format,
directory, pid, isolate->main_port());
void* file = (*file_open)(filename, true);
if (file == NULL) {
OS::Print("Failed to write timeline file: %s\n", filename);
return;
}
(*file_write)(js.buffer()->buf(), js.buffer()->length(), file);
(*file_close)(file);
}
intptr_t TimelineEventRingRecorder::SizeForCapacity(intptr_t capacity) {
return sizeof(TimelineEvent) * capacity;
}
TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity)
: events_(NULL),
event_objects_(Array::null()),
cursor_(0),
capacity_(capacity) {
if (FLAG_trace_timeline) {
// 32-bit: 262,144 bytes per isolate.
// 64-bit: 393,216 bytes per isolate.
// NOTE: Internal isolates (vm and service) do not have a timeline
// event buffer.
OS::Print("TimelineEventRingRecorder is %" Pd " bytes (%" Pd " events)\n",
SizeForCapacity(capacity),
capacity);
}
events_ =
reinterpret_cast<TimelineEvent*>(calloc(capacity, sizeof(TimelineEvent)));
const Array& array = Array::Handle(Array::New(capacity, Heap::kOld));
event_objects_ = array.raw();
}
TimelineEventRingRecorder::~TimelineEventRingRecorder() {
for (intptr_t i = 0; i < capacity_; i++) {
// Clear any extra data.
events_[i].Reset();
}
free(events_);
event_objects_ = Array::null();
}
void TimelineEventRingRecorder::PrintJSONEvents(JSONArray* events) const {
for (intptr_t i = 0; i < capacity_; i++) {
if (events_[i].IsValid()) {
events->AddValue(&events_[i]);
}
}
}
void TimelineEventRingRecorder::PrintJSON(JSONStream* js) const {
JSONObject topLevel(js);
topLevel.AddProperty("type", "_Timeline");
{
JSONArray events(&topLevel, "traceEvents");
PrintJSONMeta(&events);
PrintJSONEvents(&events);
}
}
intptr_t TimelineEventRingRecorder::GetNextIndex() {
uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_);
return cursor % capacity_;
}
void TimelineEventRingRecorder::VisitObjectPointers(
ObjectPointerVisitor* visitor) {
visitor->VisitPointer(reinterpret_cast<RawObject**>(&event_objects_));
}
TimelineEvent* TimelineEventRingRecorder::StartEvent(const Object& obj) {
ASSERT(events_ != NULL);
uintptr_t index = GetNextIndex();
const Array& event_objects = Array::Handle(event_objects_);
event_objects.SetAt(index, obj);
return &events_[index];
}
TimelineEvent* TimelineEventRingRecorder::StartEvent() {
ASSERT(events_ != NULL);
uintptr_t index = GetNextIndex();
return &events_[index];
}
void TimelineEventRingRecorder::CompleteEvent(TimelineEvent* event) {
ASSERT(events_ != NULL);
// no-op.
}
TimelineEventStreamingRecorder::TimelineEventStreamingRecorder() {
}
TimelineEventStreamingRecorder::~TimelineEventStreamingRecorder() {
}
void TimelineEventStreamingRecorder::PrintJSON(JSONStream* js) const {
JSONObject topLevel(js);
topLevel.AddProperty("type", "_Timeline");
{
JSONArray events(&topLevel, "traceEvents");
PrintJSONMeta(&events);
}
}
void TimelineEventStreamingRecorder::VisitObjectPointers(
ObjectPointerVisitor* visitor) {
// no-op.
}
TimelineEvent* TimelineEventStreamingRecorder::StartEvent(
const Object& object) {
// The streaming recorder does not track Dart objects.
return StartEvent();
}
TimelineEvent* TimelineEventStreamingRecorder::StartEvent() {
TimelineEvent* event = new TimelineEvent();
return event;
}
void TimelineEventStreamingRecorder::CompleteEvent(TimelineEvent* event) {
StreamEvent(event);
delete event;
}
} // namespace dart