mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 01:30:32 +00:00
[vm] Add Dart_SetTimelineRecorderCallback.
TEST=ci Bug: b/245563515 Change-Id: I5f8ada1854be9de5bf08f4492c3166826722dbf3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/258180 Commit-Queue: Ryan Macnak <rmacnak@google.com> Reviewed-by: Ben Konyi <bkonyi@google.com>
This commit is contained in:
parent
f58e9abf12
commit
79afcf9c7d
|
@ -132,7 +132,7 @@ typedef struct {
|
|||
} Dart_EmbedderInformation;
|
||||
|
||||
/**
|
||||
* Callback provided by the embedder that is used by the vm to request
|
||||
* Callback provided by the embedder that is used by the VM to request
|
||||
* information.
|
||||
*
|
||||
* \return Returns a pointer to a Dart_EmbedderInformation structure.
|
||||
|
@ -152,8 +152,8 @@ typedef void (*Dart_EmbedderInformationCallback)(
|
|||
* \param callback The callback to invoke.
|
||||
* \param user_data The user data passed to the callback.
|
||||
*
|
||||
* NOTE: If multiple callbacks with the same name are registered, only
|
||||
* the last callback registered will be remembered.
|
||||
* NOTE: If multiple callbacks are registered, only the last callback registered
|
||||
* will be remembered.
|
||||
*/
|
||||
DART_EXPORT void Dart_SetEmbedderInformationCallback(
|
||||
Dart_EmbedderInformationCallback callback);
|
||||
|
@ -443,6 +443,82 @@ DART_EXPORT void Dart_TimelineEvent(const char* label,
|
|||
*/
|
||||
DART_EXPORT void Dart_SetThreadName(const char* name);
|
||||
|
||||
typedef struct {
|
||||
const char* name;
|
||||
const char* value;
|
||||
} Dart_TimelineRecorderEvent_Argument;
|
||||
|
||||
#define DART_TIMELINE_RECORDER_CURRENT_VERSION (0x00000001)
|
||||
|
||||
typedef struct {
|
||||
/* Set to DART_TIMELINE_RECORDER_CURRENT_VERSION */
|
||||
int32_t version;
|
||||
|
||||
/* The event's type / phase. */
|
||||
Dart_Timeline_Event_Type type;
|
||||
|
||||
/* The event's timestamp according to the same clock as
|
||||
* Dart_TimelineGetMicros. For a duration event, this is the beginning time.
|
||||
*/
|
||||
int64_t timestamp0;
|
||||
|
||||
/* For a duration event, this is the end time. For an async event, this is the
|
||||
* async id. */
|
||||
int64_t timestamp1_or_async_id;
|
||||
|
||||
/* The current isolate of the event, as if by Dart_GetMainPortId, or
|
||||
* ILLEGAL_PORT if the event had no current isolate. */
|
||||
Dart_Port isolate;
|
||||
|
||||
/* The current isolate group of the event, as if by
|
||||
* Dart_CurrentIsolateGroupId, or ILLEGAL_PORT if the event had no current
|
||||
* isolate group. */
|
||||
Dart_IsolateGroupId isolate_group;
|
||||
|
||||
/* The name / label of the event. */
|
||||
const char* label;
|
||||
|
||||
/* The stream / category of the event. */
|
||||
const char* stream;
|
||||
|
||||
intptr_t argument_count;
|
||||
Dart_TimelineRecorderEvent_Argument* arguments;
|
||||
} Dart_TimelineRecorderEvent;
|
||||
|
||||
/**
|
||||
* Callback provided by the embedder to handle the completion of timeline
|
||||
* events.
|
||||
*
|
||||
* \param event A timeline event that has just been completed. The VM keeps
|
||||
* ownership of the event and any field in it (i.e., the embedder should copy
|
||||
* any values it needs after the callback returns).
|
||||
*/
|
||||
typedef void (*Dart_TimelineRecorderCallback)(
|
||||
Dart_TimelineRecorderEvent* event);
|
||||
|
||||
/**
|
||||
* Register a `Dart_TimelineRecorderCallback` to be called as timeline events
|
||||
* are completed.
|
||||
*
|
||||
* The callback will be invoked without a current isolate.
|
||||
*
|
||||
* The callback will be invoked on the thread completing the event. Because
|
||||
* `Dart_TimelineEvent` may be called by any thread, the callback may be called
|
||||
* on any thread.
|
||||
*
|
||||
* The callback may be invoked at any time after `Dart_Initialize` is called and
|
||||
* before `Dart_Cleanup` returns.
|
||||
*
|
||||
* If multiple callbacks are registered, only the last callback registered
|
||||
* will be remembered. Providing a NULL callback will clear the registration
|
||||
* (i.e., a NULL callback produced a no-op instead of a crash).
|
||||
*
|
||||
* Setting a callback is insuffient to receive events through the callback. The
|
||||
* VM flag `timeline_recorder` must also be set to `callback`.
|
||||
*/
|
||||
DART_EXPORT void Dart_SetTimelineRecorderCallback(
|
||||
Dart_TimelineRecorderCallback callback);
|
||||
|
||||
/*
|
||||
* =======
|
||||
* Metrics
|
||||
|
|
|
@ -6424,6 +6424,13 @@ DART_EXPORT void Dart_TimelineEvent(const char* label,
|
|||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT void Dart_SetTimelineRecorderCallback(
|
||||
Dart_TimelineRecorderCallback callback) {
|
||||
#if defined(SUPPORT_TIMELINE)
|
||||
Timeline::set_callback(callback);
|
||||
#endif
|
||||
}
|
||||
|
||||
DART_EXPORT void Dart_SetThreadName(const char* name) {
|
||||
OSThread* thread = OSThread::Current();
|
||||
if (thread == NULL) {
|
||||
|
|
|
@ -47,7 +47,7 @@ DEFINE_FLAG(charp,
|
|||
timeline_recorder,
|
||||
"ring",
|
||||
"Select the timeline recorder used. "
|
||||
"Valid values: ring, endless, startup, and systrace.")
|
||||
"Valid values: ring, endless, startup, systrace, file, callback.")
|
||||
|
||||
// Implementation notes:
|
||||
//
|
||||
|
@ -142,6 +142,10 @@ static TimelineEventRecorder* CreateTimelineRecorder() {
|
|||
return new TimelineEventFileRecorder(&flag[5]);
|
||||
}
|
||||
|
||||
if (strcmp("callback", flag) == 0) {
|
||||
return new TimelineEventEmbedderCallbackRecorder();
|
||||
}
|
||||
|
||||
// Always fall back to the ring recorder.
|
||||
return new TimelineEventRingRecorder();
|
||||
}
|
||||
|
@ -206,6 +210,7 @@ void Timeline::Init() {
|
|||
stream_##name##_.set_enabled(HasStream(enabled_streams_, #name));
|
||||
TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT)
|
||||
#undef TIMELINE_STREAM_FLAG_DEFAULT
|
||||
RecorderLock::Init();
|
||||
}
|
||||
|
||||
void Timeline::Cleanup() {
|
||||
|
@ -399,6 +404,7 @@ void TimelineEventArguments::Free() {
|
|||
}
|
||||
|
||||
TimelineEventRecorder* Timeline::recorder_ = NULL;
|
||||
Dart_TimelineRecorderCallback Timeline::callback_ = NULL;
|
||||
MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL;
|
||||
bool Timeline::recorder_discards_clock_values_ = false;
|
||||
|
||||
|
@ -1326,6 +1332,67 @@ void TimelineEventCallbackRecorder::CompleteEvent(TimelineEvent* event) {
|
|||
delete event;
|
||||
}
|
||||
|
||||
void TimelineEventEmbedderCallbackRecorder::OnEvent(TimelineEvent* event) {
|
||||
Dart_TimelineRecorderCallback callback = Timeline::callback();
|
||||
if (callback == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
Dart_TimelineRecorderEvent recorder_event;
|
||||
recorder_event.version = DART_TIMELINE_RECORDER_CURRENT_VERSION;
|
||||
switch (event->event_type()) {
|
||||
case TimelineEvent::kBegin:
|
||||
recorder_event.type = Dart_Timeline_Event_Begin;
|
||||
break;
|
||||
case TimelineEvent::kEnd:
|
||||
recorder_event.type = Dart_Timeline_Event_End;
|
||||
break;
|
||||
case TimelineEvent::kInstant:
|
||||
recorder_event.type = Dart_Timeline_Event_Instant;
|
||||
break;
|
||||
case TimelineEvent::kDuration:
|
||||
recorder_event.type = Dart_Timeline_Event_Duration;
|
||||
break;
|
||||
case TimelineEvent::kAsyncBegin:
|
||||
recorder_event.type = Dart_Timeline_Event_Async_Begin;
|
||||
break;
|
||||
case TimelineEvent::kAsyncEnd:
|
||||
recorder_event.type = Dart_Timeline_Event_Async_End;
|
||||
break;
|
||||
case TimelineEvent::kAsyncInstant:
|
||||
recorder_event.type = Dart_Timeline_Event_Async_Instant;
|
||||
break;
|
||||
case TimelineEvent::kCounter:
|
||||
recorder_event.type = Dart_Timeline_Event_Counter;
|
||||
break;
|
||||
case TimelineEvent::kFlowBegin:
|
||||
recorder_event.type = Dart_Timeline_Event_Flow_Begin;
|
||||
break;
|
||||
case TimelineEvent::kFlowStep:
|
||||
recorder_event.type = Dart_Timeline_Event_Flow_Step;
|
||||
break;
|
||||
case TimelineEvent::kFlowEnd:
|
||||
recorder_event.type = Dart_Timeline_Event_Flow_End;
|
||||
break;
|
||||
default:
|
||||
// Type not expressible as Dart_Timeline_Event_Type: drop event.
|
||||
return;
|
||||
}
|
||||
recorder_event.timestamp0 = event->timestamp0();
|
||||
recorder_event.timestamp1_or_async_id = event->timestamp1();
|
||||
recorder_event.isolate = event->isolate_id();
|
||||
recorder_event.isolate_group = event->isolate_group_id();
|
||||
recorder_event.label = event->label();
|
||||
recorder_event.stream = event->stream()->name();
|
||||
recorder_event.argument_count = event->GetNumArguments();
|
||||
recorder_event.arguments =
|
||||
reinterpret_cast<Dart_TimelineRecorderEvent_Argument*>(
|
||||
event->arguments());
|
||||
|
||||
NoActiveIsolateScope no_active_isolate_scope;
|
||||
callback(&recorder_event);
|
||||
}
|
||||
|
||||
TimelineEventPlatformRecorder::TimelineEventPlatformRecorder() {}
|
||||
|
||||
TimelineEventPlatformRecorder::~TimelineEventPlatformRecorder() {}
|
||||
|
|
|
@ -130,6 +130,11 @@ class TimelineStream {
|
|||
|
||||
class RecorderLock : public AllStatic {
|
||||
public:
|
||||
static void Init() {
|
||||
outstanding_event_writes_.store(0);
|
||||
shutdown_lock_.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
static void EnterLock() {
|
||||
outstanding_event_writes_.fetch_add(1, std::memory_order_acquire);
|
||||
}
|
||||
|
@ -188,6 +193,11 @@ class Timeline : public AllStatic {
|
|||
recorder_discards_clock_values_ = value;
|
||||
}
|
||||
|
||||
static Dart_TimelineRecorderCallback callback() { return callback_; }
|
||||
static void set_callback(Dart_TimelineRecorderCallback callback) {
|
||||
callback_ = callback;
|
||||
}
|
||||
|
||||
// Reclaim all |TimelineEventBlocks|s that are cached by threads.
|
||||
static void ReclaimCachedBlocksFromThreads();
|
||||
|
||||
|
@ -218,6 +228,7 @@ class Timeline : public AllStatic {
|
|||
static void ReclaimCachedBlocksFromThreadsUnsafe();
|
||||
|
||||
static TimelineEventRecorder* recorder_;
|
||||
static Dart_TimelineRecorderCallback callback_;
|
||||
static MallocGrowableArray<char*>* enabled_streams_;
|
||||
static bool recorder_discards_clock_values_;
|
||||
|
||||
|
@ -390,6 +401,8 @@ class TimelineEvent {
|
|||
|
||||
EventType event_type() const { return EventTypeField::decode(state_); }
|
||||
|
||||
TimelineStream* stream() const { return stream_; }
|
||||
|
||||
bool IsFinishedDuration() const {
|
||||
return (event_type() == kDuration) && (timestamp1_ > timestamp0_);
|
||||
}
|
||||
|
@ -409,6 +422,9 @@ class TimelineEvent {
|
|||
return timestamp1_;
|
||||
}
|
||||
|
||||
int64_t timestamp0() const { return timestamp0_; }
|
||||
int64_t timestamp1() const { return timestamp1_; }
|
||||
|
||||
// The lowest time value stored in this event.
|
||||
int64_t LowTime() const;
|
||||
// The highest time value stored in this event.
|
||||
|
@ -917,6 +933,9 @@ class TimelineEventCallbackRecorder : public TimelineEventRecorder {
|
|||
virtual void OnEvent(TimelineEvent* event) = 0;
|
||||
|
||||
const char* name() const { return CALLBACK_RECORDER_NAME; }
|
||||
intptr_t Size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
TimelineEventBlock* GetNewBlockLocked() { return NULL; }
|
||||
|
@ -926,6 +945,17 @@ class TimelineEventCallbackRecorder : public TimelineEventRecorder {
|
|||
void CompleteEvent(TimelineEvent* event);
|
||||
};
|
||||
|
||||
// A recorder that forwards completed events to the callback provided by
|
||||
// Dart_SetTimelineRecorderCallback.
|
||||
class TimelineEventEmbedderCallbackRecorder
|
||||
: public TimelineEventCallbackRecorder {
|
||||
public:
|
||||
TimelineEventEmbedderCallbackRecorder() {}
|
||||
virtual ~TimelineEventEmbedderCallbackRecorder() {}
|
||||
|
||||
virtual void OnEvent(TimelineEvent* event);
|
||||
};
|
||||
|
||||
// A recorder that stores events in chains of blocks of events.
|
||||
// NOTE: This recorder will continue to allocate blocks until it exhausts
|
||||
// memory.
|
||||
|
|
|
@ -279,8 +279,6 @@ class EventCounterRecorder : public TimelineEventCallbackRecorder {
|
|||
|
||||
intptr_t CountFor(TimelineEvent::EventType type) { return counts_[type]; }
|
||||
|
||||
intptr_t Size() { return -1; }
|
||||
|
||||
private:
|
||||
intptr_t counts_[TimelineEvent::kNumEventTypes];
|
||||
};
|
||||
|
@ -377,4 +375,94 @@ TEST_CASE(TimelineRingRecorderJSONOrder) {
|
|||
|
||||
#endif // !PRODUCT
|
||||
|
||||
#if defined(SUPPORT_TIMELINE)
|
||||
|
||||
static Dart_Port expected_isolate;
|
||||
static Dart_IsolateGroupId expected_isolate_group;
|
||||
static bool saw_begin;
|
||||
static bool saw_end;
|
||||
|
||||
static void TestTimelineRecorderCallback(Dart_TimelineRecorderEvent* event) {
|
||||
EXPECT_EQ(DART_TIMELINE_RECORDER_CURRENT_VERSION, event->version);
|
||||
|
||||
if ((event->type == Dart_Timeline_Event_Begin) &&
|
||||
(strcmp(event->label, "TestEvent") == 0)) {
|
||||
saw_begin = true;
|
||||
EXPECT_NE(0, event->timestamp0);
|
||||
EXPECT_EQ(expected_isolate, event->isolate);
|
||||
EXPECT_EQ(expected_isolate_group, event->isolate_group);
|
||||
EXPECT_STREQ("Dart", event->stream);
|
||||
EXPECT_EQ(1, event->argument_count);
|
||||
EXPECT_STREQ("Dart Arguments", event->arguments[0].name);
|
||||
EXPECT_STREQ("{\"key\":\"value\"}", event->arguments[0].value);
|
||||
}
|
||||
|
||||
if ((event->type == Dart_Timeline_Event_End) &&
|
||||
(strcmp(event->label, "TestEvent") == 0)) {
|
||||
saw_end = true;
|
||||
EXPECT_NE(0, event->timestamp0);
|
||||
EXPECT_EQ(expected_isolate, event->isolate);
|
||||
EXPECT_EQ(expected_isolate_group, event->isolate_group);
|
||||
EXPECT_STREQ("Dart", event->stream);
|
||||
EXPECT_EQ(1, event->argument_count);
|
||||
EXPECT_STREQ("Dart Arguments", event->arguments[0].name);
|
||||
EXPECT_STREQ("{\"key\":\"value\"}", event->arguments[0].value);
|
||||
}
|
||||
}
|
||||
|
||||
UNIT_TEST_CASE(DartAPI_SetTimelineRecorderCallback) {
|
||||
int argc = TesterState::argc + 2;
|
||||
const char** argv = new const char*[argc];
|
||||
for (intptr_t i = 0; i < argc - 2; i++) {
|
||||
argv[i] = TesterState::argv[i];
|
||||
}
|
||||
argv[argc - 2] = "--timeline_recorder=callback";
|
||||
argv[argc - 1] = "--timeline_streams=Dart";
|
||||
|
||||
Dart_SetTimelineRecorderCallback(TestTimelineRecorderCallback);
|
||||
|
||||
EXPECT(Dart_SetVMFlags(argc, argv) == NULL);
|
||||
Dart_InitializeParams params;
|
||||
memset(¶ms, 0, sizeof(Dart_InitializeParams));
|
||||
params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION;
|
||||
params.vm_snapshot_data = TesterState::vm_snapshot_data;
|
||||
params.create_group = TesterState::create_callback;
|
||||
params.shutdown_isolate = TesterState::shutdown_callback;
|
||||
params.cleanup_group = TesterState::group_cleanup_callback;
|
||||
params.start_kernel_isolate = true;
|
||||
|
||||
EXPECT(Dart_Initialize(¶ms) == NULL);
|
||||
{
|
||||
TestIsolateScope scope;
|
||||
const char* kScriptChars =
|
||||
"import 'dart:developer';\n"
|
||||
"main() {\n"
|
||||
" Timeline.startSync('TestEvent', arguments: {'key':'value'});\n"
|
||||
" Timeline.finishSync();\n"
|
||||
"}\n";
|
||||
Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
|
||||
EXPECT_VALID(lib);
|
||||
|
||||
expected_isolate = Dart_GetMainPortId();
|
||||
EXPECT_NE(ILLEGAL_PORT, expected_isolate);
|
||||
expected_isolate_group = Dart_CurrentIsolateGroupId();
|
||||
EXPECT_NE(ILLEGAL_PORT, expected_isolate_group);
|
||||
saw_begin = false;
|
||||
saw_end = false;
|
||||
|
||||
Dart_Handle result = Dart_Invoke(lib, NewString("main"), 0, NULL);
|
||||
EXPECT_VALID(result);
|
||||
|
||||
EXPECT(saw_begin);
|
||||
EXPECT(saw_end);
|
||||
}
|
||||
EXPECT(Dart_Cleanup() == NULL);
|
||||
|
||||
Dart_SetTimelineRecorderCallback(NULL);
|
||||
|
||||
delete[] argv;
|
||||
}
|
||||
|
||||
#endif // defined(SUPPORT_TIMELINE)
|
||||
|
||||
} // namespace dart
|
||||
|
|
Loading…
Reference in a new issue