| Index: runtime/vm/timeline.cc
|
| diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..51fcd71dcbb12f29393ff1591bc83b534cbd8e5d
|
| --- /dev/null
|
| +++ b/runtime/vm/timeline.cc
|
| @@ -0,0 +1,443 @@
|
| +// 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(TimelineStream* stream, const char* label) {
|
| + Init(kAsyncBegin, stream, label);
|
| + timestamp0_ = OS::GetCurrentTimeMicros();
|
| + int64_t async_id = stream->GetNextSeq();
|
| + // Overload timestamp1_ with the async_id.
|
| + timestamp1_ = async_id;
|
| + return async_id;
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::AsyncInstant(TimelineStream* stream,
|
| + const char* label,
|
| + int64_t async_id) {
|
| + Init(kAsyncInstant, stream, label);
|
| + timestamp0_ = OS::GetCurrentTimeMicros();
|
| + // Overload timestamp1_ with the async_id.
|
| + timestamp1_ = async_id;
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::AsyncEnd(TimelineStream* stream,
|
| + const char* label,
|
| + int64_t async_id) {
|
| + Init(kAsyncEnd, stream, label);
|
| + timestamp0_ = OS::GetCurrentTimeMicros();
|
| + // Overload timestamp1_ with the async_id.
|
| + timestamp1_ = async_id;
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::DurationBegin(TimelineStream* stream, const char* label) {
|
| + Init(kDuration, stream, label);
|
| + timestamp0_ = OS::GetCurrentTimeMicros();
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::DurationEnd() {
|
| + timestamp1_ = OS::GetCurrentTimeMicros();
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::Instant(TimelineStream* stream,
|
| + const char* label) {
|
| + Init(kInstant, stream, label);
|
| + timestamp0_ = OS::GetCurrentTimeMicros();
|
| +}
|
| +
|
| +
|
| +void TimelineEvent::Duration(TimelineStream* stream,
|
| + const char* label,
|
| + int64_t start_micros,
|
| + int64_t end_micros) {
|
| + Init(kDuration, stream, 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::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::Init(EventType event_type,
|
| + TimelineStream* stream,
|
| + const char* label) {
|
| + ASSERT(stream != NULL);
|
| + ASSERT(label != NULL);
|
| + set_event_type(event_type);
|
| + timestamp0_ = 0;
|
| + timestamp1_ = 0;
|
| + thread_ = Thread::Current();
|
| + stream_ = stream;
|
| + 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.AddProperty("ts", static_cast<double>(TimeOrigin()));
|
| +
|
| + switch (event_type()) {
|
| + case kDuration: {
|
| + obj.AddProperty("ph", "X");
|
| + obj.AddProperty("dur", static_cast<double>(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()
|
| + : buffer_(NULL),
|
| + name_(NULL),
|
| + enabled_(false),
|
| + seq_(0) {
|
| +}
|
| +
|
| +
|
| +void TimelineStream::Init(const char* name, bool enabled) {
|
| + name_ = name;
|
| + enabled_ = enabled;
|
| +}
|
| +
|
| +
|
| +TimelineEvent* TimelineStream::RecordEvent(const Object& obj) {
|
| + if (!enabled_ || (buffer_ == NULL)) {
|
| + return NULL;
|
| + }
|
| + ASSERT(name_ != NULL);
|
| + ASSERT(buffer_ != NULL);
|
| + return buffer_->RecordEvent(obj);
|
| +}
|
| +
|
| +
|
| +TimelineEvent* TimelineStream::RecordEvent() {
|
| + if (!enabled_ || (buffer_ == NULL)) {
|
| + return NULL;
|
| + }
|
| + ASSERT(name_ != NULL);
|
| + return buffer_->RecordEvent();
|
| +}
|
| +
|
| +
|
| +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);
|
| +}
|
| +
|
| +
|
| +intptr_t TimelineEventBuffer::SizeForCapacity(intptr_t capacity) {
|
| + return sizeof(TimelineEvent) * capacity;
|
| +}
|
| +
|
| +
|
| +TimelineEventBuffer::TimelineEventBuffer(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("TimelineEventBuffer 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();
|
| +}
|
| +
|
| +
|
| +TimelineEventBuffer::~TimelineEventBuffer() {
|
| + for (intptr_t i = 0; i < capacity_; i++) {
|
| + // Clear any extra data.
|
| + events_[i].Reset();
|
| + }
|
| + free(events_);
|
| + event_objects_ = Array::null();
|
| +}
|
| +
|
| +
|
| +void TimelineEventBuffer::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 TimelineEventBuffer::PrintJSONEvents(JSONArray* events) const {
|
| + for (intptr_t i = 0; i < capacity_; i++) {
|
| + if (events_[i].IsValid()) {
|
| + events->AddValue(&events_[i]);
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| +void TimelineEventBuffer::PrintJSON(JSONStream* js) const {
|
| + JSONObject topLevel(js);
|
| + topLevel.AddProperty("type", "_Timeline");
|
| + {
|
| + JSONArray events(&topLevel, "traceEvents");
|
| + PrintJSONMeta(&events);
|
| + PrintJSONEvents(&events);
|
| + }
|
| +}
|
| +
|
| +
|
| +void TimelineEventBuffer::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 = isolate->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 TimelineEventBuffer::GetNextIndex() {
|
| + uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_);
|
| + return cursor % capacity_;
|
| +}
|
| +
|
| +
|
| +void TimelineEventBuffer::VisitObjectPointers(ObjectPointerVisitor* visitor) {
|
| + visitor->VisitPointer(reinterpret_cast<RawObject**>(&event_objects_));
|
| +}
|
| +
|
| +
|
| +TimelineEvent* TimelineEventBuffer::RecordEvent(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* TimelineEventBuffer::RecordEvent() {
|
| + ASSERT(events_ != NULL);
|
| + uintptr_t index = GetNextIndex();
|
| + return &events_[index];
|
| +}
|
| +
|
| +} // namespace dart
|
|
|