| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include <cstdlib> | 5 #include <cstdlib> |
| 6 | 6 |
| 7 #include "vm/atomic.h" | 7 #include "vm/atomic.h" |
| 8 #include "vm/isolate.h" | 8 #include "vm/isolate.h" |
| 9 #include "vm/json_stream.h" | 9 #include "vm/json_stream.h" |
| 10 #include "vm/lockers.h" | 10 #include "vm/lockers.h" |
| (...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 int64_t micros) { | 271 int64_t micros) { |
| 272 Init(kEnd, label); | 272 Init(kEnd, label); |
| 273 timestamp0_ = micros; | 273 timestamp0_ = micros; |
| 274 } | 274 } |
| 275 | 275 |
| 276 | 276 |
| 277 void TimelineEvent::SetNumArguments(intptr_t length) { | 277 void TimelineEvent::SetNumArguments(intptr_t length) { |
| 278 // Cannot call this twice. | 278 // Cannot call this twice. |
| 279 ASSERT(arguments_ == NULL); | 279 ASSERT(arguments_ == NULL); |
| 280 ASSERT(arguments_length_ == 0); | 280 ASSERT(arguments_length_ == 0); |
| 281 if (length == 0) { |
| 282 return; |
| 283 } |
| 281 arguments_length_ = length; | 284 arguments_length_ = length; |
| 282 arguments_ = reinterpret_cast<TimelineEventArgument*>( | 285 arguments_ = reinterpret_cast<TimelineEventArgument*>( |
| 283 calloc(sizeof(TimelineEventArgument), length)); | 286 calloc(sizeof(TimelineEventArgument), length)); |
| 284 } | 287 } |
| 285 | 288 |
| 286 | 289 |
| 287 void TimelineEvent::SetArgument(intptr_t i, const char* name, char* argument) { | 290 void TimelineEvent::SetArgument(intptr_t i, const char* name, char* argument) { |
| 288 ASSERT(i >= 0); | 291 ASSERT(i >= 0); |
| 289 ASSERT(i < arguments_length_); | 292 ASSERT(i < arguments_length_); |
| 290 arguments_[i].name = name; | 293 arguments_[i].name = name; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 311 } | 314 } |
| 312 | 315 |
| 313 | 316 |
| 314 void TimelineEvent::CopyArgument(intptr_t i, | 317 void TimelineEvent::CopyArgument(intptr_t i, |
| 315 const char* name, | 318 const char* name, |
| 316 const char* argument) { | 319 const char* argument) { |
| 317 SetArgument(i, name, strdup(argument)); | 320 SetArgument(i, name, strdup(argument)); |
| 318 } | 321 } |
| 319 | 322 |
| 320 | 323 |
| 324 void TimelineEvent::StealArguments(intptr_t arguments_length, |
| 325 TimelineEventArgument* arguments) { |
| 326 arguments_length_ = arguments_length; |
| 327 arguments_ = arguments; |
| 328 } |
| 329 |
| 330 |
| 321 void TimelineEvent::Complete() { | 331 void TimelineEvent::Complete() { |
| 322 TimelineEventRecorder* recorder = Timeline::recorder(); | 332 TimelineEventRecorder* recorder = Timeline::recorder(); |
| 323 if (recorder != NULL) { | 333 if (recorder != NULL) { |
| 324 recorder->CompleteEvent(this); | 334 recorder->CompleteEvent(this); |
| 325 } | 335 } |
| 326 } | 336 } |
| 327 | 337 |
| 328 | 338 |
| 329 void TimelineEvent::FreeArguments() { | 339 void TimelineEvent::FreeArguments() { |
| 330 if (arguments_ == NULL) { | 340 if (arguments_ == NULL) { |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 460 } | 470 } |
| 461 ASSERT(name_ != NULL); | 471 ASSERT(name_ != NULL); |
| 462 TimelineEvent* event = recorder->StartEvent(); | 472 TimelineEvent* event = recorder->StartEvent(); |
| 463 if (event != NULL) { | 473 if (event != NULL) { |
| 464 event->StreamInit(this); | 474 event->StreamInit(this); |
| 465 } | 475 } |
| 466 return event; | 476 return event; |
| 467 } | 477 } |
| 468 | 478 |
| 469 | 479 |
| 480 TimelineDurationScope::TimelineDurationScope(TimelineStream* stream, |
| 481 const char* label) |
| 482 : StackResource(reinterpret_cast<Thread*>(NULL)), |
| 483 timestamp_(0), |
| 484 stream_(stream), |
| 485 label_(label), |
| 486 arguments_(NULL), |
| 487 arguments_length_(0), |
| 488 enabled_(false) { |
| 489 Init(); |
| 490 } |
| 491 |
| 492 |
| 493 TimelineDurationScope::TimelineDurationScope(Thread* thread, |
| 494 TimelineStream* stream, |
| 495 const char* label) |
| 496 : StackResource(thread), |
| 497 timestamp_(0), |
| 498 stream_(stream), |
| 499 label_(label), |
| 500 arguments_(NULL), |
| 501 arguments_length_(0), |
| 502 enabled_(false) { |
| 503 ASSERT(thread != NULL); |
| 504 Init(); |
| 505 } |
| 506 |
| 507 |
| 508 TimelineDurationScope::~TimelineDurationScope() { |
| 509 if (!enabled_) { |
| 510 FreeArguments(); |
| 511 return; |
| 512 } |
| 513 TimelineEvent* event = stream_->StartEvent(); |
| 514 ASSERT(event != NULL); |
| 515 event->Duration(label_, timestamp_, OS::GetCurrentTraceMicros()); |
| 516 event->StealArguments(arguments_length_, arguments_); |
| 517 event->Complete(); |
| 518 arguments_length_ = 0; |
| 519 arguments_ = NULL; |
| 520 } |
| 521 |
| 522 |
| 523 void TimelineDurationScope::Init() { |
| 524 ASSERT(enabled_ == false); |
| 525 ASSERT(label_ != NULL); |
| 526 ASSERT(stream_ != NULL); |
| 527 if (!stream_->Enabled()) { |
| 528 // Stream is not enabled, do nothing. |
| 529 return; |
| 530 } |
| 531 timestamp_ = OS::GetCurrentTraceMicros(); |
| 532 enabled_ = true; |
| 533 } |
| 534 |
| 535 |
| 536 void TimelineDurationScope::SetNumArguments(intptr_t length) { |
| 537 if (!enabled()) { |
| 538 return; |
| 539 } |
| 540 ASSERT(arguments_ == NULL); |
| 541 ASSERT(arguments_length_ == 0); |
| 542 arguments_length_ = length; |
| 543 if (arguments_length_ == 0) { |
| 544 return; |
| 545 } |
| 546 arguments_ = reinterpret_cast<TimelineEventArgument*>( |
| 547 calloc(sizeof(TimelineEventArgument), length)); |
| 548 } |
| 549 |
| 550 |
| 551 // |name| must be a compile time constant. Takes ownership of |argumentp|. |
| 552 void TimelineDurationScope::SetArgument(intptr_t i, |
| 553 const char* name, |
| 554 char* argument) { |
| 555 if (!enabled()) { |
| 556 return; |
| 557 } |
| 558 ASSERT(i >= 0); |
| 559 ASSERT(i < arguments_length_); |
| 560 arguments_[i].name = name; |
| 561 arguments_[i].value = argument; |
| 562 } |
| 563 |
| 564 |
| 565 // |name| must be a compile time constant. Copies |argument|. |
| 566 void TimelineDurationScope::CopyArgument(intptr_t i, |
| 567 const char* name, |
| 568 const char* argument) { |
| 569 if (!enabled()) { |
| 570 return; |
| 571 } |
| 572 SetArgument(i, name, strdup(argument)); |
| 573 } |
| 574 |
| 575 |
| 470 void TimelineDurationScope::FormatArgument(intptr_t i, | 576 void TimelineDurationScope::FormatArgument(intptr_t i, |
| 471 const char* name, | 577 const char* name, |
| 472 const char* fmt, ...) { | 578 const char* fmt, ...) { |
| 473 if (event_ == NULL) { | 579 if (!enabled()) { |
| 474 return; | 580 return; |
| 475 } | 581 } |
| 476 va_list args; | 582 va_list args; |
| 477 va_start(args, fmt); | 583 va_start(args, fmt); |
| 478 intptr_t len = OS::VSNPrint(NULL, 0, fmt, args); | 584 intptr_t len = OS::VSNPrint(NULL, 0, fmt, args); |
| 479 va_end(args); | 585 va_end(args); |
| 480 | 586 |
| 481 char* buffer = reinterpret_cast<char*>(malloc(len + 1)); | 587 char* buffer = reinterpret_cast<char*>(malloc(len + 1)); |
| 482 va_list args2; | 588 va_list args2; |
| 483 va_start(args2, fmt); | 589 va_start(args2, fmt); |
| 484 OS::VSNPrint(buffer, (len + 1), fmt, args2); | 590 OS::VSNPrint(buffer, (len + 1), fmt, args2); |
| 485 va_end(args2); | 591 va_end(args2); |
| 486 | 592 |
| 487 event_->SetArgument(i, name, buffer); | 593 SetArgument(i, name, buffer); |
| 488 } | 594 } |
| 489 | 595 |
| 490 | 596 |
| 597 void TimelineDurationScope::FreeArguments() { |
| 598 if (arguments_ == NULL) { |
| 599 return; |
| 600 } |
| 601 for (intptr_t i = 0; i < arguments_length_; i++) { |
| 602 free(arguments_[i].value); |
| 603 } |
| 604 free(arguments_); |
| 605 arguments_ = NULL; |
| 606 arguments_length_ = 0; |
| 607 } |
| 608 |
| 609 |
| 491 TimelineEventFilter::TimelineEventFilter() { | 610 TimelineEventFilter::TimelineEventFilter() { |
| 492 } | 611 } |
| 493 | 612 |
| 494 | 613 |
| 495 TimelineEventFilter::~TimelineEventFilter() { | 614 TimelineEventFilter::~TimelineEventFilter() { |
| 496 } | 615 } |
| 497 | 616 |
| 498 | 617 |
| 499 IsolateTimelineEventFilter::IsolateTimelineEventFilter(Isolate* isolate) | 618 IsolateTimelineEventFilter::IsolateTimelineEventFilter(Isolate* isolate) |
| 500 : isolate_(isolate) { | 619 : isolate_(isolate) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 538 | 657 |
| 539 | 658 |
| 540 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { | 659 void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { |
| 541 } | 660 } |
| 542 | 661 |
| 543 | 662 |
| 544 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { | 663 TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { |
| 545 // Grab the current thread. | 664 // Grab the current thread. |
| 546 Thread* thread = Thread::Current(); | 665 Thread* thread = Thread::Current(); |
| 547 ASSERT(thread != NULL); | 666 ASSERT(thread != NULL); |
| 548 // We are accessing the thread's timeline block- so take the lock. | 667 ASSERT(thread->isolate() != NULL); |
| 549 MutexLocker ml(thread->timeline_block_lock()); | 668 Mutex* thread_block_lock = thread->timeline_block_lock(); |
| 550 | 669 ASSERT(thread_block_lock != NULL); |
| 551 if (thread->isolate() == NULL) { | 670 // We are accessing the thread's timeline block- so take the lock here. |
| 552 // Non-isolate thread case. This should be infrequent. | 671 // This lock will be held until the call to |CompleteEvent| is made. |
| 553 return GlobalBlockStartEvent(); | 672 thread_block_lock->Lock(); |
| 554 } | |
| 555 | 673 |
| 556 TimelineEventBlock* thread_block = thread->timeline_block(); | 674 TimelineEventBlock* thread_block = thread->timeline_block(); |
| 557 | 675 |
| 558 if ((thread_block != NULL) && thread_block->IsFull()) { | 676 if ((thread_block != NULL) && thread_block->IsFull()) { |
| 559 MutexLocker ml(&lock_); | 677 MutexLocker ml(&lock_); |
| 560 // Thread has a block and it is full: | 678 // Thread has a block and it is full: |
| 561 // 1) Mark it as finished. | 679 // 1) Mark it as finished. |
| 562 thread_block->Finish(); | 680 thread_block->Finish(); |
| 563 // 2) Allocate a new block. | 681 // 2) Allocate a new block. |
| 564 thread_block = GetNewBlockLocked(thread->isolate()); | 682 thread_block = GetNewBlockLocked(thread->isolate()); |
| 565 thread->set_timeline_block(thread_block); | 683 thread->set_timeline_block(thread_block); |
| 566 } else if (thread_block == NULL) { | 684 } else if (thread_block == NULL) { |
| 567 MutexLocker ml(&lock_); | 685 MutexLocker ml(&lock_); |
| 568 // Thread has no block. Attempt to allocate one. | 686 // Thread has no block. Attempt to allocate one. |
| 569 thread_block = GetNewBlockLocked(thread->isolate()); | 687 thread_block = GetNewBlockLocked(thread->isolate()); |
| 570 thread->set_timeline_block(thread_block); | 688 thread->set_timeline_block(thread_block); |
| 571 } | 689 } |
| 572 if (thread_block != NULL) { | 690 if (thread_block != NULL) { |
| 691 // NOTE: We are exiting this function with the thread's block lock held. |
| 573 ASSERT(!thread_block->IsFull()); | 692 ASSERT(!thread_block->IsFull()); |
| 574 return thread_block->StartEvent(); | 693 TimelineEvent* event = thread_block->StartEvent(); |
| 694 if (event != NULL) { |
| 695 event->set_global_block(false); |
| 696 } |
| 697 return event; |
| 575 } | 698 } |
| 699 // Drop lock here as no event is being handed out. |
| 700 thread_block_lock->Unlock(); |
| 576 return NULL; | 701 return NULL; |
| 577 } | 702 } |
| 578 | 703 |
| 579 | 704 |
| 580 | |
| 581 TimelineEvent* TimelineEventRecorder::GlobalBlockStartEvent() { | 705 TimelineEvent* TimelineEventRecorder::GlobalBlockStartEvent() { |
| 582 MutexLocker ml(&lock_); | 706 // Take recorder lock. This lock will be held until the call to |
| 707 // |CompleteEvent| is made. |
| 708 lock_.Lock(); |
| 583 if (FLAG_trace_timeline) { | 709 if (FLAG_trace_timeline) { |
| 584 OS::Print("GlobalBlockStartEvent in block %p for thread %" Px "\n", | 710 OS::Print("GlobalBlockStartEvent in block %p for thread %" Px "\n", |
| 585 global_block_, OSThread::CurrentCurrentThreadIdAsIntPtr()); | 711 global_block_, OSThread::CurrentCurrentThreadIdAsIntPtr()); |
| 586 } | 712 } |
| 587 if ((global_block_ != NULL) && global_block_->IsFull()) { | 713 if ((global_block_ != NULL) && global_block_->IsFull()) { |
| 588 // Global block is full. | 714 // Global block is full. |
| 589 global_block_->Finish(); | 715 global_block_->Finish(); |
| 590 global_block_ = NULL; | 716 global_block_ = NULL; |
| 591 } | 717 } |
| 592 if (global_block_ == NULL) { | 718 if (global_block_ == NULL) { |
| 593 // Allocate a new block. | 719 // Allocate a new block. |
| 594 global_block_ = GetNewBlockLocked(NULL); | 720 global_block_ = GetNewBlockLocked(NULL); |
| 595 ASSERT(global_block_ != NULL); | 721 ASSERT(global_block_ != NULL); |
| 596 } | 722 } |
| 597 if (global_block_ != NULL) { | 723 if (global_block_ != NULL) { |
| 724 // NOTE: We are exiting this function with the recorder's lock held. |
| 598 ASSERT(!global_block_->IsFull()); | 725 ASSERT(!global_block_->IsFull()); |
| 599 return global_block_->StartEvent(); | 726 TimelineEvent* event = global_block_->StartEvent(); |
| 727 if (event != NULL) { |
| 728 event->set_global_block(true); |
| 729 } |
| 730 return event; |
| 600 } | 731 } |
| 732 // Drop lock here as no event is being handed out. |
| 733 lock_.Unlock(); |
| 601 return NULL; | 734 return NULL; |
| 602 } | 735 } |
| 603 | 736 |
| 604 | 737 |
| 738 void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) { |
| 739 if (event == NULL) { |
| 740 return; |
| 741 } |
| 742 ASSERT(!event->global_block()); |
| 743 // Grab the current thread. |
| 744 Thread* thread = Thread::Current(); |
| 745 ASSERT(thread != NULL); |
| 746 ASSERT(thread->isolate() != NULL); |
| 747 // This event came from the isolate's thread local block. Unlock the |
| 748 // thread's block lock. |
| 749 Mutex* thread_block_lock = thread->timeline_block_lock(); |
| 750 ASSERT(thread_block_lock != NULL); |
| 751 thread_block_lock->Unlock(); |
| 752 } |
| 753 |
| 754 |
| 755 void TimelineEventRecorder::GlobalBlockCompleteEvent(TimelineEvent* event) { |
| 756 if (event == NULL) { |
| 757 return; |
| 758 } |
| 759 ASSERT(event->global_block()); |
| 760 // This event came from the global block, unlock the recorder's lock now |
| 761 // that the event is filled. |
| 762 lock_.Unlock(); |
| 763 } |
| 764 |
| 765 |
| 605 // Trims the ']' character. | 766 // Trims the ']' character. |
| 606 static void TrimOutput(char* output, | 767 static void TrimOutput(char* output, |
| 607 intptr_t* output_length) { | 768 intptr_t* output_length) { |
| 608 ASSERT(output != NULL); | 769 ASSERT(output != NULL); |
| 609 ASSERT(output_length != NULL); | 770 ASSERT(output_length != NULL); |
| 610 ASSERT(*output_length >= 2); | 771 ASSERT(*output_length >= 2); |
| 611 // We expect the first character to be the opening of an array. | 772 // We expect the first character to be the opening of an array. |
| 612 ASSERT(output[0] == '['); | 773 ASSERT(output[0] == '['); |
| 613 // We expect the last character to be the closing of an array. | 774 // We expect the last character to be the closing of an array. |
| 614 ASSERT(output[*output_length - 1] == ']'); | 775 ASSERT(output[*output_length - 1] == ']'); |
| (...skipping 247 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 862 if (block->LowerTimeBound() < earliest_time) { | 1023 if (block->LowerTimeBound() < earliest_time) { |
| 863 earliest_time = block->LowerTimeBound(); | 1024 earliest_time = block->LowerTimeBound(); |
| 864 earliest_index = block_idx; | 1025 earliest_index = block_idx; |
| 865 } | 1026 } |
| 866 } | 1027 } |
| 867 return earliest_index; | 1028 return earliest_index; |
| 868 } | 1029 } |
| 869 | 1030 |
| 870 | 1031 |
| 871 TimelineEvent* TimelineEventRingRecorder::StartEvent() { | 1032 TimelineEvent* TimelineEventRingRecorder::StartEvent() { |
| 1033 // Grab the current thread. |
| 1034 Thread* thread = Thread::Current(); |
| 1035 ASSERT(thread != NULL); |
| 1036 if (thread->isolate() == NULL) { |
| 1037 // Non-isolate thread case. This should be infrequent. |
| 1038 return GlobalBlockStartEvent(); |
| 1039 } |
| 872 return ThreadBlockStartEvent(); | 1040 return ThreadBlockStartEvent(); |
| 873 } | 1041 } |
| 874 | 1042 |
| 875 | 1043 |
| 876 void TimelineEventRingRecorder::CompleteEvent(TimelineEvent* event) { | 1044 void TimelineEventRingRecorder::CompleteEvent(TimelineEvent* event) { |
| 877 // no-op. | 1045 if (event == NULL) { |
| 1046 return; |
| 1047 } |
| 1048 if (event->global_block()) { |
| 1049 GlobalBlockCompleteEvent(event); |
| 1050 } else { |
| 1051 ThreadBlockCompleteEvent(event); |
| 1052 } |
| 878 } | 1053 } |
| 879 | 1054 |
| 880 | 1055 |
| 881 TimelineEventStreamingRecorder::TimelineEventStreamingRecorder() { | 1056 TimelineEventStreamingRecorder::TimelineEventStreamingRecorder() { |
| 882 } | 1057 } |
| 883 | 1058 |
| 884 | 1059 |
| 885 TimelineEventStreamingRecorder::~TimelineEventStreamingRecorder() { | 1060 TimelineEventStreamingRecorder::~TimelineEventStreamingRecorder() { |
| 886 } | 1061 } |
| 887 | 1062 |
| (...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 989 dart_events_[dart_events_cursor_++] = dart_event; | 1164 dart_events_[dart_events_cursor_++] = dart_event; |
| 990 } | 1165 } |
| 991 | 1166 |
| 992 | 1167 |
| 993 TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() { | 1168 TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() { |
| 994 return head_; | 1169 return head_; |
| 995 } | 1170 } |
| 996 | 1171 |
| 997 | 1172 |
| 998 TimelineEvent* TimelineEventEndlessRecorder::StartEvent() { | 1173 TimelineEvent* TimelineEventEndlessRecorder::StartEvent() { |
| 1174 // Grab the current thread. |
| 1175 Thread* thread = Thread::Current(); |
| 1176 ASSERT(thread != NULL); |
| 1177 if (thread->isolate() == NULL) { |
| 1178 // Non-isolate thread case. This should be infrequent. |
| 1179 return GlobalBlockStartEvent(); |
| 1180 } |
| 999 return ThreadBlockStartEvent(); | 1181 return ThreadBlockStartEvent(); |
| 1000 } | 1182 } |
| 1001 | 1183 |
| 1002 | 1184 |
| 1003 void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) { | 1185 void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) { |
| 1004 // no-op. | 1186 if (event == NULL) { |
| 1187 return; |
| 1188 } |
| 1189 if (event->global_block()) { |
| 1190 GlobalBlockCompleteEvent(event); |
| 1191 } else { |
| 1192 ThreadBlockCompleteEvent(event); |
| 1193 } |
| 1005 } | 1194 } |
| 1006 | 1195 |
| 1007 | 1196 |
| 1008 TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked( | 1197 TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked( |
| 1009 Isolate* isolate) { | 1198 Isolate* isolate) { |
| 1010 TimelineEventBlock* block = new TimelineEventBlock(block_index_++); | 1199 TimelineEventBlock* block = new TimelineEventBlock(block_index_++); |
| 1011 block->set_next(head_); | 1200 block->set_next(head_); |
| 1012 block->Open(isolate); | 1201 block->Open(isolate); |
| 1013 head_ = block; | 1202 head_ = block; |
| 1014 if (FLAG_trace_timeline) { | 1203 if (FLAG_trace_timeline) { |
| (...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1273 // If an isolate was specified, skip events from other isolates. | 1462 // If an isolate was specified, skip events from other isolates. |
| 1274 continue; | 1463 continue; |
| 1275 } | 1464 } |
| 1276 ASSERT(event->event_as_json() != NULL); | 1465 ASSERT(event->event_as_json() != NULL); |
| 1277 result = zone->ConcatStrings(result, event->event_as_json()); | 1466 result = zone->ConcatStrings(result, event->event_as_json()); |
| 1278 } | 1467 } |
| 1279 return result; | 1468 return result; |
| 1280 } | 1469 } |
| 1281 | 1470 |
| 1282 } // namespace dart | 1471 } // namespace dart |
| OLD | NEW |