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 "platform/globals.h" | 5 #include "platform/globals.h" |
6 #ifndef PRODUCT | 6 #ifndef PRODUCT |
7 | 7 |
| 8 #include "vm/timeline.h" |
| 9 |
8 #include <errno.h> | 10 #include <errno.h> |
9 #include <fcntl.h> | 11 #include <fcntl.h> |
10 #include <cstdlib> | 12 #include <cstdlib> |
11 | 13 |
12 #if defined(HOST_OS_FUCHSIA) | |
13 #include "apps/tracing/lib/trace/cwriter.h" | |
14 #include "apps/tracing/lib/trace/event.h" | |
15 #endif | |
16 | |
17 #include "vm/atomic.h" | 14 #include "vm/atomic.h" |
18 #include "vm/isolate.h" | 15 #include "vm/isolate.h" |
19 #include "vm/json_stream.h" | 16 #include "vm/json_stream.h" |
20 #include "vm/lockers.h" | 17 #include "vm/lockers.h" |
21 #include "vm/log.h" | 18 #include "vm/log.h" |
22 #include "vm/object.h" | 19 #include "vm/object.h" |
23 #include "vm/service_event.h" | 20 #include "vm/service_event.h" |
24 #include "vm/thread.h" | 21 #include "vm/thread.h" |
25 #include "vm/timeline.h" | |
26 | 22 |
27 namespace dart { | 23 namespace dart { |
28 | 24 |
29 DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline"); | 25 DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline"); |
30 DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline"); | 26 DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline"); |
31 DEFINE_FLAG( | 27 DEFINE_FLAG( |
32 bool, | 28 bool, |
33 systrace_timeline, | 29 systrace_timeline, |
34 false, | 30 false, |
35 "Record the timeline to the platform's tracing service if there is one"); | 31 "Record the timeline to the platform's tracing service if there is one"); |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 const bool use_startup_recorder = FLAG_startup_timeline; | 105 const bool use_startup_recorder = FLAG_startup_timeline; |
110 const bool use_systrace_recorder = FLAG_systrace_timeline; | 106 const bool use_systrace_recorder = FLAG_systrace_timeline; |
111 | 107 |
112 const char* flag = FLAG_timeline_recorder; | 108 const char* flag = FLAG_timeline_recorder; |
113 | 109 |
114 if (use_systrace_recorder || (flag != NULL)) { | 110 if (use_systrace_recorder || (flag != NULL)) { |
115 if (use_systrace_recorder || (strcmp("systrace", flag) == 0)) { | 111 if (use_systrace_recorder || (strcmp("systrace", flag) == 0)) { |
116 if (FLAG_trace_timeline) { | 112 if (FLAG_trace_timeline) { |
117 THR_Print("Using the Systrace timeline recorder.\n"); | 113 THR_Print("Using the Systrace timeline recorder.\n"); |
118 } | 114 } |
119 #if defined(HOST_OS_FUCHSIA) | 115 return TimelineEventPlatformRecorder::CreatePlatformRecorder(); |
120 return new TimelineEventFuchsiaRecorder(); | |
121 #else | |
122 return new TimelineEventSystraceRecorder(); | |
123 #endif | |
124 } | 116 } |
125 } | 117 } |
126 | 118 |
127 if (use_endless_recorder || (flag != NULL)) { | 119 if (use_endless_recorder || (flag != NULL)) { |
128 if (use_endless_recorder || (strcmp("endless", flag) == 0)) { | 120 if (use_endless_recorder || (strcmp("endless", flag) == 0)) { |
129 if (FLAG_trace_timeline) { | 121 if (FLAG_trace_timeline) { |
130 THR_Print("Using the endless timeline recorder.\n"); | 122 THR_Print("Using the endless timeline recorder.\n"); |
131 } | 123 } |
132 return new TimelineEventEndlessRecorder(); | 124 return new TimelineEventEndlessRecorder(); |
133 } | 125 } |
(...skipping 384 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
518 const char* argument) { | 510 const char* argument) { |
519 SetArgument(i, name, strdup(argument)); | 511 SetArgument(i, name, strdup(argument)); |
520 } | 512 } |
521 | 513 |
522 void TimelineEvent::StealArguments(intptr_t arguments_length, | 514 void TimelineEvent::StealArguments(intptr_t arguments_length, |
523 TimelineEventArgument* arguments) { | 515 TimelineEventArgument* arguments) { |
524 arguments_length_ = arguments_length; | 516 arguments_length_ = arguments_length; |
525 arguments_ = arguments; | 517 arguments_ = arguments; |
526 } | 518 } |
527 | 519 |
528 #if defined(HOST_OS_FUCHSIA) | |
529 // TODO(zra): The functions below emit Dart's timeline events all as category | |
530 // "dart". Instead, we could have finer-grained categories that make use of | |
531 // the name of the timeline stream, e.g. "VM", "GC", etc. | |
532 | |
533 void TimelineEvent::EmitFuchsiaEvent() { | |
534 if (!ctrace_is_enabled()) { | |
535 return; | |
536 } | |
537 auto writer = ctrace_writer_acquire(); | |
538 | |
539 // XXX: use ctrace_register_category_string(); | |
540 ctrace_stringref_t category; | |
541 ctrace_register_string(writer, "dart", &category); | |
542 | |
543 ctrace_stringref_t name; | |
544 ctrace_register_string(writer, label_, &name); | |
545 | |
546 ctrace_threadref_t thread; | |
547 ctrace_register_current_thread(writer, &thread); | |
548 | |
549 ctrace_argspec_t args[2]; | |
550 ctrace_arglist_t arglist = {0, args}; | |
551 | |
552 if (arguments_length_ >= 1) { | |
553 args[0].type = CTRACE_ARGUMENT_STRING; | |
554 args[0].name = arguments_[0].name; | |
555 args[0].u.s = arguments_[0].value; | |
556 arglist.n_args += 1; | |
557 } | |
558 if (arguments_length_ >= 2) { | |
559 args[1].type = CTRACE_ARGUMENT_STRING; | |
560 args[1].name = arguments_[1].name; | |
561 args[1].u.s = arguments_[1].value; | |
562 arglist.n_args += 1; | |
563 } | |
564 | |
565 const uint64_t time_scale = mx_ticks_per_second() / kMicrosecondsPerSecond; | |
566 const uint64_t start_time = LowTime() * time_scale; | |
567 const uint64_t end_time = HighTime() * time_scale; | |
568 | |
569 switch (event_type()) { | |
570 case kBegin: | |
571 ctrace_write_duration_begin_event_record(writer, start_time, &thread, | |
572 &category, &name, &arglist); | |
573 break; | |
574 case kEnd: | |
575 ctrace_write_duration_end_event_record(writer, end_time, &thread, | |
576 &category, &name, &arglist); | |
577 break; | |
578 case kInstant: | |
579 ctrace_write_instant_event_record(writer, start_time, &thread, &category, | |
580 &name, CTRACE_SCOPE_THREAD, &arglist); | |
581 break; | |
582 case kAsyncBegin: | |
583 ctrace_write_async_begin_event_record( | |
584 writer, start_time, &thread, &category, &name, AsyncId(), &arglist); | |
585 break; | |
586 case kAsyncEnd: | |
587 ctrace_write_async_end_event_record(writer, end_time, &thread, &category, | |
588 &name, AsyncId(), &arglist); | |
589 break; | |
590 case kAsyncInstant: | |
591 ctrace_write_async_instant_event_record( | |
592 writer, start_time, &thread, &category, &name, AsyncId(), &arglist); | |
593 break; | |
594 case kDuration: | |
595 ctrace_write_duration_event_record(writer, start_time, end_time, &thread, | |
596 &category, &name, &arglist); | |
597 break; | |
598 case kFlowBegin: | |
599 ctrace_write_flow_begin_event_record( | |
600 writer, start_time, &thread, &category, &name, AsyncId(), &arglist); | |
601 break; | |
602 case kFlowStep: | |
603 ctrace_write_flow_step_event_record( | |
604 writer, start_time, &thread, &category, &name, AsyncId(), &arglist); | |
605 break; | |
606 case kFlowEnd: | |
607 ctrace_write_flow_end_event_record(writer, start_time, &thread, &category, | |
608 &name, AsyncId(), &arglist); | |
609 break; | |
610 default: | |
611 // TODO(zra): Figure out what to do with kCounter and kMetadata. | |
612 break; | |
613 } | |
614 ctrace_writer_release(writer); | |
615 } | |
616 #endif | |
617 | |
618 intptr_t TimelineEvent::PrintSystrace(char* buffer, intptr_t buffer_size) { | |
619 ASSERT(buffer != NULL); | |
620 ASSERT(buffer_size > 0); | |
621 buffer[0] = '\0'; | |
622 intptr_t length = 0; | |
623 int64_t pid = OS::ProcessId(); | |
624 switch (event_type()) { | |
625 case kBegin: { | |
626 length = OS::SNPrint(buffer, buffer_size, "B|%" Pd64 "|%s", pid, label()); | |
627 } break; | |
628 case kEnd: { | |
629 length = OS::SNPrint(buffer, buffer_size, "E"); | |
630 } break; | |
631 case kCounter: { | |
632 if (arguments_length_ > 0) { | |
633 // We only report the first counter value. | |
634 length = OS::SNPrint(buffer, buffer_size, "C|%" Pd64 "|%s|%s", pid, | |
635 label(), arguments_[0].value); | |
636 } | |
637 } | |
638 default: | |
639 // Ignore event types that we cannot serialize to the Systrace format. | |
640 break; | |
641 } | |
642 return length; | |
643 } | |
644 | |
645 void TimelineEvent::Complete() { | 520 void TimelineEvent::Complete() { |
646 TimelineEventRecorder* recorder = Timeline::recorder(); | 521 TimelineEventRecorder* recorder = Timeline::recorder(); |
647 if (recorder != NULL) { | 522 if (recorder != NULL) { |
648 recorder->CompleteEvent(this); | 523 recorder->CompleteEvent(this); |
649 } | 524 } |
650 } | 525 } |
651 | 526 |
652 void TimelineEvent::FreeArguments() { | 527 void TimelineEvent::FreeArguments() { |
653 if (arguments_ == NULL) { | 528 if (arguments_ == NULL) { |
654 return; | 529 return; |
(...skipping 762 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1417 return ThreadBlockStartEvent(); | 1292 return ThreadBlockStartEvent(); |
1418 } | 1293 } |
1419 | 1294 |
1420 void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) { | 1295 void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) { |
1421 if (event == NULL) { | 1296 if (event == NULL) { |
1422 return; | 1297 return; |
1423 } | 1298 } |
1424 ThreadBlockCompleteEvent(event); | 1299 ThreadBlockCompleteEvent(event); |
1425 } | 1300 } |
1426 | 1301 |
1427 TimelineEventSystraceRecorder::TimelineEventSystraceRecorder(intptr_t capacity) | |
1428 : TimelineEventFixedBufferRecorder(capacity), systrace_fd_(-1) { | |
1429 #if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) | |
1430 const char* kSystracePath = "/sys/kernel/debug/tracing/trace_marker"; | |
1431 systrace_fd_ = open(kSystracePath, O_WRONLY); | |
1432 if ((systrace_fd_ < 0) && FLAG_trace_timeline) { | |
1433 OS::PrintErr("TimelineEventSystraceRecorder: Could not open `%s`\n", | |
1434 kSystracePath); | |
1435 } | |
1436 #else | |
1437 OS::PrintErr( | |
1438 "Warning: The systrace timeline recorder is equivalent to the" | |
1439 "ring recorder on this platform."); | |
1440 #endif | |
1441 } | |
1442 | |
1443 TimelineEventSystraceRecorder::~TimelineEventSystraceRecorder() { | |
1444 #if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) | |
1445 if (systrace_fd_ >= 0) { | |
1446 close(systrace_fd_); | |
1447 } | |
1448 #endif | |
1449 } | |
1450 | |
1451 TimelineEventBlock* TimelineEventSystraceRecorder::GetNewBlockLocked() { | |
1452 // TODO(johnmccutchan): This function should only hand out blocks | |
1453 // which have been marked as finished. | |
1454 if (block_cursor_ == num_blocks_) { | |
1455 block_cursor_ = 0; | |
1456 } | |
1457 TimelineEventBlock* block = blocks_[block_cursor_++]; | |
1458 block->Reset(); | |
1459 block->Open(); | |
1460 return block; | |
1461 } | |
1462 | |
1463 void TimelineEventSystraceRecorder::CompleteEvent(TimelineEvent* event) { | |
1464 if (event == NULL) { | |
1465 return; | |
1466 } | |
1467 #if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) | |
1468 if (systrace_fd_ >= 0) { | |
1469 // Serialize to the systrace format. | |
1470 const intptr_t kBufferLength = 1024; | |
1471 char buffer[kBufferLength]; | |
1472 const intptr_t event_length = | |
1473 event->PrintSystrace(&buffer[0], kBufferLength); | |
1474 if (event_length > 0) { | |
1475 ssize_t __result; | |
1476 // Repeatedly attempt the write while we are being interrupted. | |
1477 do { | |
1478 __result = write(systrace_fd_, buffer, event_length); | |
1479 } while ((__result == -1L) && (errno == EINTR)); | |
1480 } | |
1481 } | |
1482 #endif | |
1483 ThreadBlockCompleteEvent(event); | |
1484 } | |
1485 | |
1486 #if defined(HOST_OS_FUCHSIA) | |
1487 TimelineEventFuchsiaRecorder::TimelineEventFuchsiaRecorder(intptr_t capacity) | |
1488 : TimelineEventFixedBufferRecorder(capacity) {} | |
1489 | |
1490 | |
1491 TimelineEventBlock* TimelineEventFuchsiaRecorder::GetNewBlockLocked() { | |
1492 // TODO(johnmccutchan): This function should only hand out blocks | |
1493 // which have been marked as finished. | |
1494 if (block_cursor_ == num_blocks_) { | |
1495 block_cursor_ = 0; | |
1496 } | |
1497 TimelineEventBlock* block = blocks_[block_cursor_++]; | |
1498 block->Reset(); | |
1499 block->Open(); | |
1500 return block; | |
1501 } | |
1502 | |
1503 void TimelineEventFuchsiaRecorder::CompleteEvent(TimelineEvent* event) { | |
1504 if (event == NULL) { | |
1505 return; | |
1506 } | |
1507 event->EmitFuchsiaEvent(); | |
1508 ThreadBlockCompleteEvent(event); | |
1509 } | |
1510 #endif // defined(HOST_OS_FUCHSIA) | |
1511 | |
1512 TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() { | 1302 TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() { |
1513 // TODO(johnmccutchan): This function should only hand out blocks | 1303 // TODO(johnmccutchan): This function should only hand out blocks |
1514 // which have been marked as finished. | 1304 // which have been marked as finished. |
1515 if (block_cursor_ == num_blocks_) { | 1305 if (block_cursor_ == num_blocks_) { |
1516 block_cursor_ = 0; | 1306 block_cursor_ = 0; |
1517 } | 1307 } |
1518 TimelineEventBlock* block = blocks_[block_cursor_++]; | 1308 TimelineEventBlock* block = blocks_[block_cursor_++]; |
1519 block->Reset(); | 1309 block->Reset(); |
1520 block->Open(); | 1310 block->Open(); |
1521 return block; | 1311 return block; |
(...skipping 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1815 return current_ != NULL; | 1605 return current_ != NULL; |
1816 } | 1606 } |
1817 | 1607 |
1818 TimelineEventBlock* TimelineEventBlockIterator::Next() { | 1608 TimelineEventBlock* TimelineEventBlockIterator::Next() { |
1819 ASSERT(current_ != NULL); | 1609 ASSERT(current_ != NULL); |
1820 TimelineEventBlock* r = current_; | 1610 TimelineEventBlock* r = current_; |
1821 current_ = current_->next(); | 1611 current_ = current_->next(); |
1822 return r; | 1612 return r; |
1823 } | 1613 } |
1824 | 1614 |
| 1615 void DartCommonTimelineEventHelpers::ReportTaskEvent(Thread* thread, |
| 1616 Zone* zone, |
| 1617 TimelineEvent* event, |
| 1618 int64_t start, |
| 1619 int64_t id, |
| 1620 const char* phase, |
| 1621 const char* category, |
| 1622 const char* name, |
| 1623 const char* args) { |
| 1624 const int64_t pid = OS::ProcessId(); |
| 1625 OSThread* os_thread = thread->os_thread(); |
| 1626 ASSERT(os_thread != NULL); |
| 1627 const int64_t tid = OSThread::ThreadIdToIntPtr(os_thread->trace_id()); |
| 1628 ASSERT(phase != NULL); |
| 1629 ASSERT((phase[0] == 'n') || (phase[0] == 'b') || (phase[0] == 'e')); |
| 1630 ASSERT(phase[1] == '\0'); |
| 1631 char* json = OS::SCreate( |
| 1632 zone, |
| 1633 "{\"name\":\"%s\",\"cat\":\"%s\",\"tid\":%" Pd64 ",\"pid\":%" Pd64 |
| 1634 "," |
| 1635 "\"ts\":%" Pd64 ",\"ph\":\"%s\",\"id\":%" Pd64 ", \"args\":%s}", |
| 1636 name, category, tid, pid, start, phase, id, args); |
| 1637 switch (phase[0]) { |
| 1638 case 'n': |
| 1639 event->AsyncInstant("", id, start); |
| 1640 break; |
| 1641 case 'b': |
| 1642 event->AsyncBegin("", id, start); |
| 1643 break; |
| 1644 case 'e': |
| 1645 event->AsyncEnd("", id, start); |
| 1646 break; |
| 1647 default: |
| 1648 UNREACHABLE(); |
| 1649 } |
| 1650 |
| 1651 // json was allocated in the zone and a copy will be stored in event. |
| 1652 event->CompleteWithPreSerializedJSON(json); |
| 1653 } |
| 1654 |
| 1655 void DartCommonTimelineEventHelpers::ReportCompleteEvent(Thread* thread, |
| 1656 Zone* zone, |
| 1657 TimelineEvent* event, |
| 1658 int64_t start, |
| 1659 int64_t start_cpu, |
| 1660 const char* category, |
| 1661 const char* name, |
| 1662 const char* args) { |
| 1663 const int64_t end = OS::GetCurrentMonotonicMicros(); |
| 1664 const int64_t end_cpu = OS::GetCurrentThreadCPUMicros(); |
| 1665 const int64_t duration = end - start; |
| 1666 const int64_t duration_cpu = end_cpu - start_cpu; |
| 1667 const int64_t pid = OS::ProcessId(); |
| 1668 OSThread* os_thread = thread->os_thread(); |
| 1669 ASSERT(os_thread != NULL); |
| 1670 const int64_t tid = OSThread::ThreadIdToIntPtr(os_thread->trace_id()); |
| 1671 |
| 1672 char* json = NULL; |
| 1673 if ((start_cpu != -1) && (end_cpu != -1)) { |
| 1674 json = OS::SCreate( |
| 1675 zone, |
| 1676 "{\"name\":\"%s\",\"cat\":\"%s\",\"tid\":%" Pd64 ",\"pid\":%" Pd64 |
| 1677 "," |
| 1678 "\"ts\":%" Pd64 ",\"ph\":\"X\",\"dur\":%" Pd64 |
| 1679 "," |
| 1680 "\"tdur\":%" Pd64 ",\"args\":%s}", |
| 1681 name, category, tid, pid, start, duration, duration_cpu, args); |
| 1682 } else { |
| 1683 json = OS::SCreate( |
| 1684 zone, |
| 1685 "{\"name\":\"%s\",\"cat\":\"%s\",\"tid\":%" Pd64 ",\"pid\":%" Pd64 |
| 1686 "," |
| 1687 "\"ts\":%" Pd64 ",\"ph\":\"X\",\"dur\":%" Pd64 ",\"args\":%s}", |
| 1688 name, category, tid, pid, start, duration, args); |
| 1689 } |
| 1690 ASSERT(json != NULL); |
| 1691 |
| 1692 event->Duration("", start, end, start_cpu, end_cpu); |
| 1693 // json was allocated in the zone and a copy will be stored in event. |
| 1694 event->CompleteWithPreSerializedJSON(json); |
| 1695 } |
| 1696 |
| 1697 void DartCommonTimelineEventHelpers::ReportInstantEvent(Thread* thread, |
| 1698 Zone* zone, |
| 1699 TimelineEvent* event, |
| 1700 int64_t start, |
| 1701 const char* category, |
| 1702 const char* name, |
| 1703 const char* args) { |
| 1704 const int64_t pid = OS::ProcessId(); |
| 1705 OSThread* os_thread = thread->os_thread(); |
| 1706 ASSERT(os_thread != NULL); |
| 1707 const int64_t tid = OSThread::ThreadIdToIntPtr(os_thread->trace_id()); |
| 1708 |
| 1709 char* json = OS::SCreate(zone, |
| 1710 "{\"name\":\"%s\",\"cat\":\"%s\",\"tid\":%" Pd64 |
| 1711 ",\"pid\":%" Pd64 |
| 1712 "," |
| 1713 "\"ts\":%" Pd64 ",\"ph\":\"I\",\"args\":%s}", |
| 1714 name, category, tid, pid, start, args); |
| 1715 |
| 1716 event->Instant("", start); |
| 1717 // json was allocated in the zone and a copy will be stored in event. |
| 1718 event->CompleteWithPreSerializedJSON(json); |
| 1719 } |
| 1720 |
1825 } // namespace dart | 1721 } // namespace dart |
1826 | 1722 |
1827 #endif // !PRODUCT | 1723 #endif // !PRODUCT |
OLD | NEW |