Chromium Code Reviews| Index: runtime/vm/timeline.cc |
| diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..79b5417cd18fb319f0de0606439d650ab376d40b |
| --- /dev/null |
| +++ b/runtime/vm/timeline.cc |
| @@ -0,0 +1,242 @@ |
| +// 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"); |
| + |
| + |
| +void TimelineEvent::Reset() { |
| + set_event_type(kNone); |
| + thread_ = NULL; |
| + stream_ = NULL; |
| + label_ = NULL; |
| + arg_name_ = NULL; |
| + if (arg_value_ != NULL) { |
| + free(const_cast<char*>(arg_value_)); |
| + } |
| +} |
| + |
| + |
| +void TimelineEvent::DurationBegin(TimelineStream* stream, const char* label) { |
| + Init(kDuration, stream, label); |
| +} |
| + |
| + |
| +void TimelineEvent::DurationEnd() { |
| + timestamp1_ = OS::GetCurrentTimeMicros(); |
| +} |
| + |
| + |
| +void TimelineEvent::Instant(TimelineStream* stream, const char* label) { |
| + Init(kInstant, stream, label); |
| +} |
| + |
| + |
| +void TimelineEvent::SetArgument(const char* name, const char* value) { |
| + arg_name_ = name; |
| + arg_value_ = strdup(value); |
| +} |
| + |
| +void TimelineEvent::Init(EventType event_type, |
| + TimelineStream* stream, |
| + const char* label) { |
| + set_event_type(event_type); |
| + timestamp0_ = OS::GetCurrentTimeMicros(); |
| + thread_ = Thread::Current(); |
| + stream_ = stream; |
| + label_ = label; |
| + arg_name_ = NULL; |
| + if (arg_value_ != NULL) { |
| + free(const_cast<char*>(arg_value_)); |
| + } |
| +} |
| + |
| + |
| +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) { |
| + 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>(timestamp0_)); |
| + |
| + switch (event_type()) { |
| + case kDuration: { |
| + obj.AddProperty("ph", "X"); |
|
rmacnak
2015/06/05 21:22:58
ph?
Cutch
2015/06/05 21:34:42
https://docs.google.com/document/d/1CvAClvFfyA5R-P
|
| + obj.AddProperty("dur", static_cast<double>(timestamp1_ - timestamp0_)); |
| + } |
| + break; |
| + case kInstant: { |
| + UNIMPLEMENTED(); |
| + } |
| + break; |
| + default: |
| + UNIMPLEMENTED(); |
| + } |
| + |
| + if (arg_name_ != NULL) { |
| + JSONObject args(&obj, "args"); |
| + args.AddProperty(arg_name_, arg_value_); |
| + } |
| +} |
| + |
| + |
| +TimelineStream::TimelineStream() |
| + : buffer_(NULL), |
| + name_(NULL), |
| + enabled_(false) { |
| +} |
| + |
| + |
| +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(); |
| +} |
| + |
| + |
| +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::DumpMeta(JSONArray* events) { |
| + 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::DumpEvents(JSONArray* events) { |
| + for (intptr_t i = 0; i < capacity_; i++) { |
| + if (events_[i].IsValid()) { |
| + events->AddValue(&events_[i]); |
| + } |
| + } |
| +} |
| + |
| + |
| +void TimelineEventBuffer::Dump() { |
| + JSONStream js; |
| + { |
| + JSONObject topLevel(&js); |
| + topLevel.AddProperty("type", "Timeline"); |
| + { |
| + JSONArray events(&topLevel, "traceEvents"); |
| + DumpMeta(&events); |
| + DumpEvents(&events); |
| + } |
| + } |
| + OS::Print("%s\n", js.ToCString()); |
| +} |
| + |
| + |
| +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 |