| 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 |