| Index: runtime/vm/timeline.cc
|
| diff --git a/runtime/vm/timeline.cc b/runtime/vm/timeline.cc
|
| index 595c0fe135eb5224007e17d95bb8550b6b885175..48a67201a4a37bf0f694df7ce72283b372041e2a 100644
|
| --- a/runtime/vm/timeline.cc
|
| +++ b/runtime/vm/timeline.cc
|
| @@ -332,6 +332,25 @@ void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const {
|
| }
|
|
|
|
|
| +TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() {
|
| + // Grab the thread's timeline event block.
|
| + Thread* thread = Thread::Current();
|
| + TimelineEventBlock* thread_block = thread->timeline_block();
|
| + if ((thread_block == NULL) || thread_block->IsFull()) {
|
| + // If it is full, request a new block.
|
| + thread_block = GetNewBlock();
|
| + thread->set_timeline_block(thread_block);
|
| + }
|
| + if (thread_block == NULL) {
|
| + // Could not allocate block.
|
| + return NULL;
|
| + }
|
| + ASSERT(thread_block != NULL);
|
| + ASSERT(!thread_block->IsFull());
|
| + return thread_block->StartEvent();
|
| +}
|
| +
|
| +
|
| void TimelineEventRecorder::WriteTo(const char* directory) {
|
| Isolate* isolate = Isolate::Current();
|
|
|
| @@ -362,52 +381,65 @@ void TimelineEventRecorder::WriteTo(const char* directory) {
|
| }
|
|
|
|
|
| -intptr_t TimelineEventRingRecorder::SizeForCapacity(intptr_t capacity) {
|
| - return sizeof(TimelineEvent) * capacity;
|
| -}
|
| -
|
|
|
| TimelineEventRingRecorder::TimelineEventRingRecorder(intptr_t capacity)
|
| - : events_(NULL),
|
| + : blocks_(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)));
|
| + capacity_(capacity),
|
| + num_blocks_(0),
|
| + block_cursor_(0) {
|
| + // Capacity must be a multiple of TimelineEventBlock::kBlockSize
|
| + ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0);
|
| + // Allocate blocks array.
|
| + num_blocks_ = capacity / TimelineEventBlock::kBlockSize;
|
| + blocks_ =
|
| + reinterpret_cast<TimelineEventBlock**>(
|
| + calloc(num_blocks_, sizeof(TimelineEventBlock*)));
|
| + // Allocate each block.
|
| + for (intptr_t i = 0; i < num_blocks_; i++) {
|
| + blocks_[i] = new TimelineEventBlock(i);
|
| + }
|
| + // Chain blocks together.
|
| + for (intptr_t i = 0; i < num_blocks_ - 1; i++) {
|
| + blocks_[i]->set_next(blocks_[i + 1]);
|
| + }
|
| 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();
|
| + // Delete all blocks.
|
| + for (intptr_t i = 0; i < num_blocks_; i++) {
|
| + TimelineEventBlock* block = blocks_[i];
|
| + delete block;
|
| }
|
| - free(events_);
|
| + free(blocks_);
|
| 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]);
|
| + // TODO(johnmccutchan): This output needs to start with the oldest block
|
| + // first.
|
| + for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) {
|
| + TimelineEventBlock* block = blocks_[block_idx];
|
| + if (block->IsEmpty()) {
|
| + // Skip empty blocks.
|
| + continue;
|
| + }
|
| + for (intptr_t event_idx = 0; event_idx < block->length(); event_idx++) {
|
| + TimelineEvent* event = block->At(event_idx);
|
| + if (event->IsValid()) {
|
| + events->AddValue(event);
|
| + }
|
| }
|
| }
|
| }
|
|
|
|
|
| void TimelineEventRingRecorder::PrintJSON(JSONStream* js) {
|
| + MutexLocker ml(&lock_);
|
| JSONObject topLevel(js);
|
| topLevel.AddProperty("type", "_Timeline");
|
| {
|
| @@ -418,9 +450,19 @@ void TimelineEventRingRecorder::PrintJSON(JSONStream* js) {
|
| }
|
|
|
|
|
| -intptr_t TimelineEventRingRecorder::GetNextIndex() {
|
| - uintptr_t cursor = AtomicOperations::FetchAndIncrement(&cursor_);
|
| - return cursor % capacity_;
|
| +TimelineEventBlock* TimelineEventRingRecorder::GetNewBlock() {
|
| + MutexLocker ml(&lock_);
|
| + return GetNewBlockLocked();
|
| +}
|
| +
|
| +
|
| +TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() {
|
| + if (block_cursor_ == num_blocks_) {
|
| + block_cursor_ = 0;
|
| + }
|
| + TimelineEventBlock* block = blocks_[block_cursor_++];
|
| + block->Reset();
|
| + return block;
|
| }
|
|
|
|
|
| @@ -431,22 +473,30 @@ void TimelineEventRingRecorder::VisitObjectPointers(
|
|
|
|
|
| TimelineEvent* TimelineEventRingRecorder::StartEvent(const Object& obj) {
|
| - ASSERT(events_ != NULL);
|
| - uintptr_t index = GetNextIndex();
|
| + TimelineEvent* event = StartEvent();
|
| + if (event == NULL) {
|
| + return NULL;
|
| + }
|
| + // Grab the thread's timeline event block which contains |event|.
|
| + Thread* thread = Thread::Current();
|
| + TimelineEventBlock* thread_block = thread->timeline_block();
|
| + ASSERT(thread_block != NULL);
|
| + ASSERT(thread_block->length() > 0);
|
| + const intptr_t block_index = thread_block->block_index();
|
| + const intptr_t event_objects_index =
|
| + block_index * TimelineEventBlock::kBlockSize + thread_block->length() - 1;
|
| const Array& event_objects = Array::Handle(event_objects_);
|
| - event_objects.SetAt(index, obj);
|
| - return &events_[index];
|
| + event_objects.SetAt(event_objects_index, obj);
|
| + return event;
|
| }
|
|
|
|
|
| TimelineEvent* TimelineEventRingRecorder::StartEvent() {
|
| - ASSERT(events_ != NULL);
|
| - uintptr_t index = GetNextIndex();
|
| - return &events_[index];
|
| + return ThreadBlockStartEvent();
|
| }
|
|
|
| +
|
| void TimelineEventRingRecorder::CompleteEvent(TimelineEvent* event) {
|
| - ASSERT(events_ != NULL);
|
| // no-op.
|
| }
|
|
|
| @@ -468,6 +518,7 @@ void TimelineEventStreamingRecorder::PrintJSON(JSONStream* js) {
|
| }
|
| }
|
|
|
| +
|
| void TimelineEventStreamingRecorder::VisitObjectPointers(
|
| ObjectPointerVisitor* visitor) {
|
| // no-op.
|
| @@ -494,7 +545,8 @@ void TimelineEventStreamingRecorder::CompleteEvent(TimelineEvent* event) {
|
|
|
|
|
| TimelineEventEndlessRecorder::TimelineEventEndlessRecorder()
|
| - : head_(NULL) {
|
| + : head_(NULL),
|
| + block_index_(0) {
|
| GetNewBlock();
|
| }
|
|
|
| @@ -529,20 +581,7 @@ TimelineEvent* TimelineEventEndlessRecorder::StartEvent(const Object& object) {
|
|
|
|
|
| TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
|
| - // Grab the thread's timeline event block.
|
| - Thread* thread = Thread::Current();
|
| - TimelineEventBlock* thread_block = thread->timeline_block();
|
| - if (thread_block == NULL) {
|
| - return NULL;
|
| - }
|
| - ASSERT(thread_block != NULL);
|
| - if (thread_block->IsFull()) {
|
| - // If it is full, request a new block.
|
| - thread_block = GetNewBlock();
|
| - thread->set_timeline_block(thread_block);
|
| - }
|
| - ASSERT(!thread_block->IsFull());
|
| - return thread_block->StartEvent();
|
| + return ThreadBlockStartEvent();
|
| }
|
|
|
|
|
| @@ -552,7 +591,7 @@ void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) {
|
|
|
|
|
| TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() {
|
| - TimelineEventBlock* block = new TimelineEventBlock();
|
| + TimelineEventBlock* block = new TimelineEventBlock(block_index_++);
|
| block->set_next(head_);
|
| head_ = block;
|
| return head_;
|
| @@ -575,9 +614,15 @@ void TimelineEventEndlessRecorder::PrintJSONEvents(JSONArray* events) const {
|
| }
|
|
|
|
|
| -TimelineEventBlock::TimelineEventBlock()
|
| +TimelineEventBlock::TimelineEventBlock(intptr_t block_index)
|
| : next_(NULL),
|
| - length_(0) {
|
| + length_(0),
|
| + block_index_(block_index) {
|
| +}
|
| +
|
| +
|
| +TimelineEventBlock::~TimelineEventBlock() {
|
| + Reset();
|
| }
|
|
|
|
|
| @@ -625,6 +670,15 @@ bool TimelineEventBlock::CheckBlock() {
|
| }
|
|
|
|
|
| +void TimelineEventBlock::Reset() {
|
| + for (intptr_t i = 0; i < kBlockSize; i++) {
|
| + // Clear any extra data.
|
| + events_[i].Reset();
|
| + }
|
| + length_ = 0;
|
| +}
|
| +
|
| +
|
| TimelineEventBlockIterator::TimelineEventBlockIterator(
|
| TimelineEventEndlessRecorder* recorder)
|
| : current_(NULL),
|
|
|