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 |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #include "vm/globals.h" |
6 | #if defined(SUPPORT_TIMELINE) |
7 | |
8 | #include "vm/timeline.h" |
9 | |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | |
13 | #include <cstdlib> |
14 | |
15 | #include "platform/atomic.h" |
16 | #include "vm/isolate.h" |
17 | #include "vm/json_stream.h" |
18 | #include "vm/lockers.h" |
19 | #include "vm/log.h" |
20 | #include "vm/object.h" |
21 | #include "vm/service.h" |
22 | #include "vm/service_event.h" |
23 | #include "vm/thread.h" |
24 | |
25 | namespace dart { |
26 | |
27 | DEFINE_FLAG(bool, complete_timeline, false, "Record the complete timeline" ); |
28 | DEFINE_FLAG(bool, startup_timeline, false, "Record the startup timeline" ); |
29 | DEFINE_FLAG( |
30 | bool, |
31 | systrace_timeline, |
32 | false, |
33 | "Record the timeline to the platform's tracing service if there is one" ); |
34 | DEFINE_FLAG(bool, trace_timeline, false, "Trace timeline backend" ); |
35 | DEFINE_FLAG(bool, |
36 | trace_timeline_analysis, |
37 | false, |
38 | "Trace timeline analysis backend" ); |
39 | DEFINE_FLAG(bool, |
40 | timing, |
41 | false, |
42 | "Dump isolate timing information from timeline." ); |
43 | DEFINE_FLAG(charp, |
44 | timeline_dir, |
45 | NULL, |
46 | "Enable all timeline trace streams and output VM global trace " |
47 | "into specified directory." ); |
48 | DEFINE_FLAG(charp, |
49 | timeline_streams, |
50 | NULL, |
51 | "Comma separated list of timeline streams to record. " |
52 | "Valid values: all, API, Compiler, CompilerVerbose, Dart, " |
53 | "Debugger, Embedder, GC, Isolate, and VM." ); |
54 | DEFINE_FLAG(charp, |
55 | timeline_recorder, |
56 | "ring" , |
57 | "Select the timeline recorder used. " |
58 | "Valid values: ring, endless, startup, and systrace." ) |
59 | |
60 | // Implementation notes: |
61 | // |
62 | // Writing events: |
63 | // |TimelineEvent|s are written into |TimelineEventBlock|s. Each |Thread| caches |
64 | // a |TimelineEventBlock| object so that it can write events without |
65 | // synchronizing with other threads in the system. Even though the |Thread| owns |
66 | // the |TimelineEventBlock| the block may need to be reclaimed by the reporting |
67 | // system. To support that, a |Thread| must hold its |timeline_block_lock_| |
68 | // when operating on the |TimelineEventBlock|. This lock will only ever be |
69 | // busy if blocks are being reclaimed by the reporting system. |
70 | // |
71 | // Reporting: |
72 | // When requested, the timeline is serialized in the trace-event format |
73 | // (https://goo.gl/hDZw5M). The request can be for a VM-wide timeline or an |
74 | // isolate specific timeline. In both cases it may be that a thread has |
75 | // a |TimelineEventBlock| cached in TLS partially filled with events. In order |
76 | // to report a complete timeline the cached |TimelineEventBlock|s need to be |
77 | // reclaimed. |
78 | // |
79 | // Reclaiming open |TimelineEventBlock|s from threads: |
80 | // |
81 | // Each |Thread| can have one |TimelineEventBlock| cached in it. |
82 | // |
83 | // To reclaim blocks, we iterate over all threads and remove the cached |
84 | // |TimelineEventBlock| from each thread. This is safe because we hold the |
85 | // |Thread|'s |timeline_block_lock_| meaning the block can't be being modified. |
86 | // |
87 | // Locking notes: |
88 | // The following locks are used by the timeline system: |
89 | // - |TimelineEventRecorder::lock_| This lock is held whenever a |
90 | // |TimelineEventBlock| is being requested or reclaimed. |
91 | // - |Thread::timeline_block_lock_| This lock is held whenever a |Thread|'s |
92 | // cached block is being operated on. |
93 | // - |Thread::thread_list_lock_| This lock is held when iterating over |
94 | // |Thread|s. |
95 | // |
96 | // Locks must always be taken in the following order: |
97 | // |Thread::thread_list_lock_| |
98 | // |Thread::timeline_block_lock_| |
99 | // |TimelineEventRecorder::lock_| |
100 | // |
101 | |
102 | static TimelineEventRecorder* CreateTimelineRecorder() { |
103 | // Some flags require that we use the endless recorder. |
104 | const bool use_endless_recorder = |
105 | (FLAG_timeline_dir != NULL) || FLAG_timing || FLAG_complete_timeline; |
106 | |
107 | const bool use_startup_recorder = FLAG_startup_timeline; |
108 | const bool use_systrace_recorder = FLAG_systrace_timeline; |
109 | const char* flag = FLAG_timeline_recorder; |
110 | |
111 | if (use_systrace_recorder || (flag != NULL)) { |
112 | if (use_systrace_recorder || (strcmp("systrace" , flag) == 0)) { |
113 | if (FLAG_trace_timeline) { |
114 | THR_Print("Using the Systrace timeline recorder.\n" ); |
115 | } |
116 | |
117 | #if defined(HOST_OS_LINUX) || defined(HOST_OS_ANDROID) |
118 | return new TimelineEventSystraceRecorder(); |
119 | #elif defined(HOST_OS_MACOS) |
120 | if (__builtin_available(iOS 12.0, macOS 10.14, *)) { |
121 | return new TimelineEventMacosRecorder(); |
122 | } |
123 | #elif defined(HOST_OS_FUCHSIA) |
124 | return new TimelineEventFuchsiaRecorder(); |
125 | #else |
126 | OS::PrintErr( |
127 | "Warning: The systrace timeline recorder is equivalent to the" |
128 | "ring recorder on this platform." ); |
129 | return new TimelineEventRingRecorder(); |
130 | #endif |
131 | } |
132 | } |
133 | |
134 | if (use_endless_recorder || (flag != NULL)) { |
135 | if (use_endless_recorder || (strcmp("endless" , flag) == 0)) { |
136 | if (FLAG_trace_timeline) { |
137 | THR_Print("Using the endless timeline recorder.\n" ); |
138 | } |
139 | return new TimelineEventEndlessRecorder(); |
140 | } |
141 | } |
142 | |
143 | if (use_startup_recorder || (flag != NULL)) { |
144 | if (use_startup_recorder || (strcmp("startup" , flag) == 0)) { |
145 | if (FLAG_trace_timeline) { |
146 | THR_Print("Using the startup recorder.\n" ); |
147 | } |
148 | return new TimelineEventStartupRecorder(); |
149 | } |
150 | } |
151 | |
152 | if (FLAG_trace_timeline) { |
153 | THR_Print("Using the ring timeline recorder.\n" ); |
154 | } |
155 | |
156 | // Always fall back to the ring recorder. |
157 | return new TimelineEventRingRecorder(); |
158 | } |
159 | |
160 | // Returns a caller freed array of stream names in FLAG_timeline_streams. |
161 | static MallocGrowableArray<char*>* GetEnabledByDefaultTimelineStreams() { |
162 | MallocGrowableArray<char*>* result = new MallocGrowableArray<char*>(); |
163 | if (FLAG_timeline_streams == NULL) { |
164 | // Nothing set. |
165 | return result; |
166 | } |
167 | char* save_ptr; // Needed for strtok_r. |
168 | // strtok modifies arg 1 so we make a copy of it. |
169 | char* streams = Utils::StrDup(FLAG_timeline_streams); |
170 | char* token = strtok_r(streams, "," , &save_ptr); |
171 | while (token != NULL) { |
172 | result->Add(Utils::StrDup(token)); |
173 | token = strtok_r(NULL, "," , &save_ptr); |
174 | } |
175 | free(streams); |
176 | return result; |
177 | } |
178 | |
179 | // Frees the result of |GetEnabledByDefaultTimelineStreams|. |
180 | static void FreeEnabledByDefaultTimelineStreams( |
181 | MallocGrowableArray<char*>* streams) { |
182 | if (streams == NULL) { |
183 | return; |
184 | } |
185 | for (intptr_t i = 0; i < streams->length(); i++) { |
186 | free((*streams)[i]); |
187 | } |
188 | delete streams; |
189 | } |
190 | |
191 | // Returns true if |streams| contains |stream| or "all". Not case sensitive. |
192 | static bool HasStream(MallocGrowableArray<char*>* streams, const char* stream) { |
193 | if ((FLAG_timeline_dir != NULL) || FLAG_timing || FLAG_complete_timeline || |
194 | FLAG_startup_timeline) { |
195 | return true; |
196 | } |
197 | for (intptr_t i = 0; i < streams->length(); i++) { |
198 | const char* checked_stream = (*streams)[i]; |
199 | if ((strstr(checked_stream, "all" ) != NULL) || |
200 | (strstr(checked_stream, stream) != NULL)) { |
201 | return true; |
202 | } |
203 | } |
204 | return false; |
205 | } |
206 | |
207 | void Timeline::Init() { |
208 | ASSERT(recorder_ == NULL); |
209 | recorder_ = CreateTimelineRecorder(); |
210 | ASSERT(recorder_ != NULL); |
211 | enabled_streams_ = GetEnabledByDefaultTimelineStreams(); |
212 | // Global overrides. |
213 | #define TIMELINE_STREAM_FLAG_DEFAULT(name, fuchsia_name) \ |
214 | stream_##name##_.set_enabled(HasStream(enabled_streams_, #name)); |
215 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAG_DEFAULT) |
216 | #undef TIMELINE_STREAM_FLAG_DEFAULT |
217 | } |
218 | |
219 | void Timeline::Cleanup() { |
220 | ASSERT(recorder_ != NULL); |
221 | |
222 | #ifndef PRODUCT |
223 | if (FLAG_timeline_dir != NULL) { |
224 | recorder_->WriteTo(FLAG_timeline_dir); |
225 | } |
226 | #endif |
227 | |
228 | // Disable global streams. |
229 | #define TIMELINE_STREAM_DISABLE(name, fuchsia_name) \ |
230 | Timeline::stream_##name##_.set_enabled(false); |
231 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_DISABLE) |
232 | #undef TIMELINE_STREAM_DISABLE |
233 | delete recorder_; |
234 | recorder_ = NULL; |
235 | if (enabled_streams_ != NULL) { |
236 | FreeEnabledByDefaultTimelineStreams(enabled_streams_); |
237 | enabled_streams_ = NULL; |
238 | } |
239 | } |
240 | |
241 | TimelineEventRecorder* Timeline::recorder() { |
242 | return recorder_; |
243 | } |
244 | |
245 | void Timeline::ReclaimCachedBlocksFromThreads() { |
246 | TimelineEventRecorder* recorder = Timeline::recorder(); |
247 | if (recorder == NULL) { |
248 | return; |
249 | } |
250 | |
251 | // Iterate over threads. |
252 | OSThreadIterator it; |
253 | while (it.HasNext()) { |
254 | OSThread* thread = it.Next(); |
255 | MutexLocker ml(thread->timeline_block_lock()); |
256 | // Grab block and clear it. |
257 | TimelineEventBlock* block = thread->timeline_block(); |
258 | thread->set_timeline_block(NULL); |
259 | // TODO(johnmccutchan): Consider dropping the timeline_block_lock here |
260 | // if we can do it everywhere. This would simplify the lock ordering |
261 | // requirements. |
262 | recorder->FinishBlock(block); |
263 | } |
264 | } |
265 | |
266 | #ifndef PRODUCT |
267 | void Timeline::PrintFlagsToJSONArray(JSONArray* arr) { |
268 | #define ADD_RECORDED_STREAM_NAME(name, fuchsia_name) \ |
269 | if (stream_##name##_.enabled()) { \ |
270 | arr->AddValue(#name); \ |
271 | } |
272 | TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME); |
273 | #undef ADD_RECORDED_STREAM_NAME |
274 | } |
275 | |
276 | void Timeline::PrintFlagsToJSON(JSONStream* js) { |
277 | JSONObject obj(js); |
278 | obj.AddProperty("type" , "TimelineFlags" ); |
279 | TimelineEventRecorder* recorder = Timeline::recorder(); |
280 | if (recorder == NULL) { |
281 | obj.AddProperty("recorderName" , "null" ); |
282 | } else { |
283 | obj.AddProperty("recorderName" , recorder->name()); |
284 | } |
285 | { |
286 | JSONArray availableStreams(&obj, "availableStreams" ); |
287 | #define ADD_STREAM_NAME(name, fuchsia_name) availableStreams.AddValue(#name); |
288 | TIMELINE_STREAM_LIST(ADD_STREAM_NAME); |
289 | #undef ADD_STREAM_NAME |
290 | } |
291 | { |
292 | JSONArray recordedStreams(&obj, "recordedStreams" ); |
293 | #define ADD_RECORDED_STREAM_NAME(name, fuchsia_name) \ |
294 | if (stream_##name##_.enabled()) { \ |
295 | recordedStreams.AddValue(#name); \ |
296 | } |
297 | TIMELINE_STREAM_LIST(ADD_RECORDED_STREAM_NAME); |
298 | #undef ADD_RECORDED_STREAM_NAME |
299 | } |
300 | } |
301 | #endif |
302 | |
303 | void Timeline::Clear() { |
304 | TimelineEventRecorder* recorder = Timeline::recorder(); |
305 | if (recorder == NULL) { |
306 | return; |
307 | } |
308 | ReclaimCachedBlocksFromThreads(); |
309 | recorder->Clear(); |
310 | } |
311 | |
312 | void TimelineEventArguments::SetNumArguments(intptr_t length) { |
313 | if (length == length_) { |
314 | return; |
315 | } |
316 | if (length == 0) { |
317 | Free(); |
318 | return; |
319 | } |
320 | if (buffer_ == NULL) { |
321 | // calloc already nullifies |
322 | buffer_ = reinterpret_cast<TimelineEventArgument*>( |
323 | calloc(sizeof(TimelineEventArgument), length)); |
324 | } else { |
325 | for (intptr_t i = length; i < length_; ++i) { |
326 | free(buffer_[i].value); |
327 | } |
328 | buffer_ = reinterpret_cast<TimelineEventArgument*>( |
329 | realloc(buffer_, sizeof(TimelineEventArgument) * length)); |
330 | if (length > length_) { |
331 | memset(buffer_ + length_, 0, |
332 | sizeof(TimelineEventArgument) * (length - length_)); |
333 | } |
334 | } |
335 | length_ = length; |
336 | } |
337 | |
338 | void TimelineEventArguments::SetArgument(intptr_t i, |
339 | const char* name, |
340 | char* argument) { |
341 | ASSERT(i >= 0); |
342 | ASSERT(i < length_); |
343 | buffer_[i].name = name; |
344 | buffer_[i].value = argument; |
345 | } |
346 | |
347 | void TimelineEventArguments::CopyArgument(intptr_t i, |
348 | const char* name, |
349 | const char* argument) { |
350 | SetArgument(i, name, Utils::StrDup(argument)); |
351 | } |
352 | |
353 | void TimelineEventArguments::FormatArgument(intptr_t i, |
354 | const char* name, |
355 | const char* fmt, |
356 | va_list args) { |
357 | ASSERT(i >= 0); |
358 | ASSERT(i < length_); |
359 | va_list args2; |
360 | va_copy(args2, args); |
361 | intptr_t len = Utils::VSNPrint(NULL, 0, fmt, args); |
362 | va_end(args); |
363 | |
364 | char* buffer = reinterpret_cast<char*>(malloc(len + 1)); |
365 | Utils::VSNPrint(buffer, (len + 1), fmt, args2); |
366 | va_end(args2); |
367 | |
368 | SetArgument(i, name, buffer); |
369 | } |
370 | |
371 | void TimelineEventArguments::StealArguments(TimelineEventArguments* arguments) { |
372 | Free(); |
373 | length_ = arguments->length_; |
374 | buffer_ = arguments->buffer_; |
375 | arguments->length_ = 0; |
376 | arguments->buffer_ = NULL; |
377 | } |
378 | |
379 | void TimelineEventArguments::Free() { |
380 | if (buffer_ == NULL) { |
381 | return; |
382 | } |
383 | for (intptr_t i = 0; i < length_; i++) { |
384 | free(buffer_[i].value); |
385 | } |
386 | free(buffer_); |
387 | buffer_ = NULL; |
388 | length_ = 0; |
389 | } |
390 | |
391 | TimelineEventRecorder* Timeline::recorder_ = NULL; |
392 | MallocGrowableArray<char*>* Timeline::enabled_streams_ = NULL; |
393 | |
394 | #define TIMELINE_STREAM_DEFINE(name, fuchsia_name) \ |
395 | TimelineStream Timeline::stream_##name##_(#name, fuchsia_name, false); |
396 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_DEFINE) |
397 | #undef TIMELINE_STREAM_DEFINE |
398 | |
399 | TimelineEvent::TimelineEvent() |
400 | : timestamp0_(0), |
401 | timestamp1_(0), |
402 | thread_timestamp0_(-1), |
403 | thread_timestamp1_(-1), |
404 | state_(0), |
405 | label_(NULL), |
406 | stream_(NULL), |
407 | thread_(OSThread::kInvalidThreadId), |
408 | isolate_id_(ILLEGAL_PORT), |
409 | isolate_group_id_(0) {} |
410 | |
411 | TimelineEvent::~TimelineEvent() { |
412 | Reset(); |
413 | } |
414 | |
415 | void TimelineEvent::Reset() { |
416 | if (owns_label() && label_ != NULL) { |
417 | free(const_cast<char*>(label_)); |
418 | } |
419 | state_ = 0; |
420 | thread_ = OSThread::kInvalidThreadId; |
421 | isolate_id_ = ILLEGAL_PORT; |
422 | isolate_group_id_ = 0; |
423 | stream_ = NULL; |
424 | label_ = NULL; |
425 | arguments_.Free(); |
426 | set_event_type(kNone); |
427 | set_pre_serialized_args(false); |
428 | set_owns_label(false); |
429 | } |
430 | |
431 | void TimelineEvent::AsyncBegin(const char* label, |
432 | int64_t async_id, |
433 | int64_t micros) { |
434 | Init(kAsyncBegin, label); |
435 | set_timestamp0(micros); |
436 | // Overload timestamp1_ with the async_id. |
437 | set_timestamp1(async_id); |
438 | } |
439 | |
440 | void TimelineEvent::AsyncInstant(const char* label, |
441 | int64_t async_id, |
442 | int64_t micros) { |
443 | Init(kAsyncInstant, label); |
444 | set_timestamp0(micros); |
445 | // Overload timestamp1_ with the async_id. |
446 | set_timestamp1(async_id); |
447 | } |
448 | |
449 | void TimelineEvent::AsyncEnd(const char* label, |
450 | int64_t async_id, |
451 | int64_t micros) { |
452 | Init(kAsyncEnd, label); |
453 | set_timestamp0(micros); |
454 | // Overload timestamp1_ with the async_id. |
455 | set_timestamp1(async_id); |
456 | } |
457 | |
458 | void TimelineEvent::DurationBegin(const char* label, |
459 | int64_t micros, |
460 | int64_t thread_micros) { |
461 | Init(kDuration, label); |
462 | set_timestamp0(micros); |
463 | set_thread_timestamp0(thread_micros); |
464 | } |
465 | |
466 | void TimelineEvent::DurationEnd(int64_t micros, int64_t thread_micros) { |
467 | ASSERT(timestamp1_ == 0); |
468 | set_timestamp1(micros); |
469 | set_thread_timestamp1(thread_micros); |
470 | } |
471 | |
472 | void TimelineEvent::Instant(const char* label, int64_t micros) { |
473 | Init(kInstant, label); |
474 | set_timestamp0(micros); |
475 | } |
476 | |
477 | void TimelineEvent::Duration(const char* label, |
478 | int64_t start_micros, |
479 | int64_t end_micros, |
480 | int64_t thread_start_micros, |
481 | int64_t thread_end_micros) { |
482 | Init(kDuration, label); |
483 | set_timestamp0(start_micros); |
484 | set_timestamp1(end_micros); |
485 | set_thread_timestamp0(thread_start_micros); |
486 | set_thread_timestamp1(thread_end_micros); |
487 | } |
488 | |
489 | void TimelineEvent::Begin(const char* label, |
490 | int64_t micros, |
491 | int64_t thread_micros) { |
492 | Init(kBegin, label); |
493 | set_timestamp0(micros); |
494 | set_thread_timestamp0(thread_micros); |
495 | } |
496 | |
497 | void TimelineEvent::End(const char* label, |
498 | int64_t micros, |
499 | int64_t thread_micros) { |
500 | Init(kEnd, label); |
501 | set_timestamp0(micros); |
502 | set_thread_timestamp0(thread_micros); |
503 | } |
504 | |
505 | void TimelineEvent::Counter(const char* label, int64_t micros) { |
506 | Init(kCounter, label); |
507 | set_timestamp0(micros); |
508 | } |
509 | |
510 | void TimelineEvent::FlowBegin(const char* label, |
511 | int64_t async_id, |
512 | int64_t micros) { |
513 | Init(kFlowBegin, label); |
514 | set_timestamp0(micros); |
515 | // Overload timestamp1_ with the async_id. |
516 | set_timestamp1(async_id); |
517 | } |
518 | |
519 | void TimelineEvent::FlowStep(const char* label, |
520 | int64_t async_id, |
521 | int64_t micros) { |
522 | Init(kFlowStep, label); |
523 | set_timestamp0(micros); |
524 | // Overload timestamp1_ with the async_id. |
525 | set_timestamp1(async_id); |
526 | } |
527 | |
528 | void TimelineEvent::FlowEnd(const char* label, |
529 | int64_t async_id, |
530 | int64_t micros) { |
531 | Init(kFlowEnd, label); |
532 | set_timestamp0(micros); |
533 | // Overload timestamp1_ with the async_id. |
534 | set_timestamp1(async_id); |
535 | } |
536 | |
537 | void TimelineEvent::Metadata(const char* label, int64_t micros) { |
538 | Init(kMetadata, label); |
539 | set_timestamp0(micros); |
540 | } |
541 | |
542 | void TimelineEvent::CompleteWithPreSerializedArgs(char* args_json) { |
543 | set_pre_serialized_args(true); |
544 | SetNumArguments(1); |
545 | SetArgument(0, "Dart Arguments" , args_json); |
546 | Complete(); |
547 | } |
548 | |
549 | void TimelineEvent::FormatArgument(intptr_t i, |
550 | const char* name, |
551 | const char* fmt, |
552 | ...) { |
553 | va_list args; |
554 | va_start(args, fmt); |
555 | arguments_.FormatArgument(i, name, fmt, args); |
556 | } |
557 | |
558 | void TimelineEvent::Complete() { |
559 | TimelineEventRecorder* recorder = Timeline::recorder(); |
560 | if (recorder != NULL) { |
561 | recorder->CompleteEvent(this); |
562 | } |
563 | } |
564 | |
565 | void TimelineEvent::Init(EventType event_type, const char* label) { |
566 | ASSERT(label != NULL); |
567 | state_ = 0; |
568 | timestamp0_ = 0; |
569 | timestamp1_ = 0; |
570 | thread_timestamp0_ = -1; |
571 | thread_timestamp1_ = -1; |
572 | OSThread* os_thread = OSThread::Current(); |
573 | ASSERT(os_thread != NULL); |
574 | thread_ = os_thread->trace_id(); |
575 | auto thread = Thread::Current(); |
576 | auto isolate = thread != nullptr ? thread->isolate() : nullptr; |
577 | auto isolate_group = thread != nullptr ? thread->isolate_group() : nullptr; |
578 | isolate_id_ = (isolate != nullptr) ? isolate->main_port() : ILLEGAL_PORT; |
579 | isolate_group_id_ = (isolate_group != nullptr) ? isolate_group->id() : 0; |
580 | label_ = label; |
581 | arguments_.Free(); |
582 | set_event_type(event_type); |
583 | set_pre_serialized_args(false); |
584 | set_owns_label(false); |
585 | } |
586 | |
587 | bool TimelineEvent::Within(int64_t time_origin_micros, |
588 | int64_t time_extent_micros) { |
589 | if ((time_origin_micros == -1) || (time_extent_micros == -1)) { |
590 | // No time range specified. |
591 | return true; |
592 | } |
593 | if (IsFinishedDuration()) { |
594 | // Event is from e_t0 to e_t1. |
595 | int64_t e_t0 = TimeOrigin(); |
596 | int64_t e_t1 = TimeEnd(); |
597 | ASSERT(e_t0 <= e_t1); |
598 | // Range is from r_t0 to r_t1. |
599 | int64_t r_t0 = time_origin_micros; |
600 | int64_t r_t1 = time_origin_micros + time_extent_micros; |
601 | ASSERT(r_t0 <= r_t1); |
602 | return !((r_t1 < e_t0) || (e_t1 < r_t0)); |
603 | } |
604 | int64_t delta = TimeOrigin() - time_origin_micros; |
605 | return (delta >= 0) && (delta <= time_extent_micros); |
606 | } |
607 | |
608 | #ifndef PRODUCT |
609 | void TimelineEvent::PrintJSON(JSONStream* stream) const { |
610 | JSONObject obj(stream); |
611 | int64_t pid = OS::ProcessId(); |
612 | int64_t tid = OSThread::ThreadIdToIntPtr(thread_); |
613 | obj.AddProperty("name" , label_); |
614 | obj.AddProperty("cat" , stream_ != NULL ? stream_->name() : NULL); |
615 | obj.AddProperty64("tid" , tid); |
616 | obj.AddProperty64("pid" , pid); |
617 | obj.AddPropertyTimeMicros("ts" , TimeOrigin()); |
618 | if (HasThreadCPUTime()) { |
619 | obj.AddPropertyTimeMicros("tts" , ThreadCPUTimeOrigin()); |
620 | } |
621 | switch (event_type()) { |
622 | case kBegin: { |
623 | obj.AddProperty("ph" , "B" ); |
624 | } break; |
625 | case kEnd: { |
626 | obj.AddProperty("ph" , "E" ); |
627 | } break; |
628 | case kDuration: { |
629 | obj.AddProperty("ph" , "X" ); |
630 | obj.AddPropertyTimeMicros("dur" , TimeDuration()); |
631 | if (HasThreadCPUTime()) { |
632 | obj.AddPropertyTimeMicros("tdur" , ThreadCPUTimeDuration()); |
633 | } |
634 | } break; |
635 | case kInstant: { |
636 | obj.AddProperty("ph" , "i" ); |
637 | obj.AddProperty("s" , "p" ); |
638 | } break; |
639 | case kAsyncBegin: { |
640 | obj.AddProperty("ph" , "b" ); |
641 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
642 | } break; |
643 | case kAsyncInstant: { |
644 | obj.AddProperty("ph" , "n" ); |
645 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
646 | } break; |
647 | case kAsyncEnd: { |
648 | obj.AddProperty("ph" , "e" ); |
649 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
650 | } break; |
651 | case kCounter: { |
652 | obj.AddProperty("ph" , "C" ); |
653 | } break; |
654 | case kFlowBegin: { |
655 | obj.AddProperty("ph" , "s" ); |
656 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
657 | } break; |
658 | case kFlowStep: { |
659 | obj.AddProperty("ph" , "t" ); |
660 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
661 | } break; |
662 | case kFlowEnd: { |
663 | obj.AddProperty("ph" , "f" ); |
664 | obj.AddProperty("bp" , "e" ); |
665 | obj.AddPropertyF("id" , "%" Px64 "" , AsyncId()); |
666 | } break; |
667 | case kMetadata: { |
668 | obj.AddProperty("ph" , "M" ); |
669 | } break; |
670 | default: |
671 | UNIMPLEMENTED(); |
672 | } |
673 | |
674 | if (pre_serialized_args()) { |
675 | ASSERT(arguments_.length() == 1); |
676 | stream->AppendSerializedObject("args" , arguments_[0].value); |
677 | if (isolate_id_ != ILLEGAL_PORT) { |
678 | stream->UncloseObject(); |
679 | stream->PrintfProperty("isolateId" , ISOLATE_SERVICE_ID_FORMAT_STRING, |
680 | static_cast<int64_t>(isolate_id_)); |
681 | stream->CloseObject(); |
682 | } |
683 | if (isolate_group_id_ != 0) { |
684 | stream->UncloseObject(); |
685 | stream->PrintfProperty("isolateGroupId" , |
686 | ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, |
687 | isolate_group_id_); |
688 | stream->CloseObject(); |
689 | } else { |
690 | ASSERT(isolate_group_id_ == ILLEGAL_PORT); |
691 | } |
692 | } else { |
693 | JSONObject args(&obj, "args" ); |
694 | for (intptr_t i = 0; i < arguments_.length(); i++) { |
695 | const TimelineEventArgument& arg = arguments_[i]; |
696 | args.AddProperty(arg.name, arg.value); |
697 | } |
698 | if (isolate_id_ != ILLEGAL_PORT) { |
699 | args.AddPropertyF("isolateId" , ISOLATE_SERVICE_ID_FORMAT_STRING, |
700 | static_cast<int64_t>(isolate_id_)); |
701 | } |
702 | if (isolate_group_id_ != 0) { |
703 | args.AddPropertyF("isolateGroupId" , |
704 | ISOLATE_GROUP_SERVICE_ID_FORMAT_STRING, |
705 | isolate_group_id_); |
706 | } else { |
707 | ASSERT(isolate_group_id_ == ILLEGAL_PORT); |
708 | } |
709 | } |
710 | } |
711 | #endif |
712 | |
713 | int64_t TimelineEvent::TimeOrigin() const { |
714 | return timestamp0_; |
715 | } |
716 | |
717 | int64_t TimelineEvent::AsyncId() const { |
718 | return timestamp1_; |
719 | } |
720 | |
721 | int64_t TimelineEvent::LowTime() const { |
722 | return timestamp0_; |
723 | } |
724 | |
725 | int64_t TimelineEvent::HighTime() const { |
726 | if (event_type() == kDuration) { |
727 | return timestamp1_; |
728 | } else { |
729 | return timestamp0_; |
730 | } |
731 | } |
732 | |
733 | int64_t TimelineEvent::TimeDuration() const { |
734 | if (timestamp1_ == 0) { |
735 | // This duration is still open, use current time as end. |
736 | return OS::GetCurrentMonotonicMicros() - timestamp0_; |
737 | } |
738 | return timestamp1_ - timestamp0_; |
739 | } |
740 | |
741 | bool TimelineEvent::HasThreadCPUTime() const { |
742 | return (thread_timestamp0_ != -1); |
743 | } |
744 | |
745 | int64_t TimelineEvent::ThreadCPUTimeOrigin() const { |
746 | ASSERT(HasThreadCPUTime()); |
747 | return thread_timestamp0_; |
748 | } |
749 | |
750 | int64_t TimelineEvent::ThreadCPUTimeDuration() const { |
751 | ASSERT(HasThreadCPUTime()); |
752 | if (thread_timestamp1_ == -1) { |
753 | // This duration is still open, use current time as end. |
754 | return OS::GetCurrentThreadCPUMicros() - thread_timestamp0_; |
755 | } |
756 | return thread_timestamp1_ - thread_timestamp0_; |
757 | } |
758 | |
759 | TimelineStream::TimelineStream(const char* name, |
760 | const char* fuchsia_name, |
761 | bool enabled) |
762 | : name_(name), |
763 | fuchsia_name_(fuchsia_name), |
764 | #if defined(HOST_OS_FUCHSIA) |
765 | enabled_(static_cast<uintptr_t>(true)) // For generated code. |
766 | #else |
767 | enabled_(static_cast<uintptr_t>(enabled)) |
768 | #endif |
769 | { |
770 | #if defined(HOST_OS_MACOS) |
771 | if (__builtin_available(iOS 12.0, macOS 10.14, *)) { |
772 | macos_log_ = os_log_create("Dart" , name); |
773 | } |
774 | #endif |
775 | } |
776 | |
777 | TimelineEvent* TimelineStream::StartEvent() { |
778 | TimelineEventRecorder* recorder = Timeline::recorder(); |
779 | if (!enabled() || (recorder == NULL)) { |
780 | return NULL; |
781 | } |
782 | ASSERT(name_ != NULL); |
783 | TimelineEvent* event = recorder->StartEvent(); |
784 | if (event != NULL) { |
785 | event->StreamInit(this); |
786 | } |
787 | return event; |
788 | } |
789 | |
790 | TimelineEventScope::TimelineEventScope(TimelineStream* stream, |
791 | const char* label) |
792 | : StackResource(reinterpret_cast<Thread*>(NULL)), |
793 | stream_(stream), |
794 | label_(label), |
795 | enabled_(false) { |
796 | Init(); |
797 | } |
798 | |
799 | TimelineEventScope::TimelineEventScope(Thread* thread, |
800 | TimelineStream* stream, |
801 | const char* label) |
802 | : StackResource(thread), stream_(stream), label_(label), enabled_(false) { |
803 | Init(); |
804 | } |
805 | |
806 | TimelineEventScope::~TimelineEventScope() {} |
807 | |
808 | void TimelineEventScope::Init() { |
809 | ASSERT(enabled_ == false); |
810 | ASSERT(label_ != NULL); |
811 | ASSERT(stream_ != NULL); |
812 | if (!stream_->enabled()) { |
813 | // Stream is not enabled, do nothing. |
814 | return; |
815 | } |
816 | enabled_ = true; |
817 | } |
818 | |
819 | void TimelineEventScope::SetNumArguments(intptr_t length) { |
820 | if (!enabled()) { |
821 | return; |
822 | } |
823 | arguments_.SetNumArguments(length); |
824 | } |
825 | |
826 | // |name| must be a compile time constant. Takes ownership of |argumentp|. |
827 | void TimelineEventScope::SetArgument(intptr_t i, |
828 | const char* name, |
829 | char* argument) { |
830 | if (!enabled()) { |
831 | return; |
832 | } |
833 | arguments_.SetArgument(i, name, argument); |
834 | } |
835 | |
836 | // |name| must be a compile time constant. Copies |argument|. |
837 | void TimelineEventScope::CopyArgument(intptr_t i, |
838 | const char* name, |
839 | const char* argument) { |
840 | if (!enabled()) { |
841 | return; |
842 | } |
843 | arguments_.CopyArgument(i, name, argument); |
844 | } |
845 | |
846 | void TimelineEventScope::FormatArgument(intptr_t i, |
847 | const char* name, |
848 | const char* fmt, |
849 | ...) { |
850 | if (!enabled()) { |
851 | return; |
852 | } |
853 | va_list args; |
854 | va_start(args, fmt); |
855 | arguments_.FormatArgument(i, name, fmt, args); |
856 | } |
857 | |
858 | void TimelineEventScope::StealArguments(TimelineEvent* event) { |
859 | if (event == NULL) { |
860 | return; |
861 | } |
862 | event->StealArguments(&arguments_); |
863 | } |
864 | |
865 | TimelineBeginEndScope::TimelineBeginEndScope(TimelineStream* stream, |
866 | const char* label) |
867 | : TimelineEventScope(stream, label) { |
868 | EmitBegin(); |
869 | } |
870 | |
871 | TimelineBeginEndScope::TimelineBeginEndScope(Thread* thread, |
872 | TimelineStream* stream, |
873 | const char* label) |
874 | : TimelineEventScope(thread, stream, label) { |
875 | EmitBegin(); |
876 | } |
877 | |
878 | TimelineBeginEndScope::~TimelineBeginEndScope() { |
879 | EmitEnd(); |
880 | } |
881 | |
882 | void TimelineBeginEndScope::EmitBegin() { |
883 | if (!ShouldEmitEvent()) { |
884 | return; |
885 | } |
886 | TimelineEvent* event = stream()->StartEvent(); |
887 | if (event == NULL) { |
888 | // Stream is now disabled. |
889 | set_enabled(false); |
890 | return; |
891 | } |
892 | ASSERT(event != NULL); |
893 | // Emit a begin event. |
894 | event->Begin(label()); |
895 | event->Complete(); |
896 | } |
897 | |
898 | void TimelineBeginEndScope::EmitEnd() { |
899 | if (!ShouldEmitEvent()) { |
900 | return; |
901 | } |
902 | TimelineEvent* event = stream()->StartEvent(); |
903 | if (event == NULL) { |
904 | // Stream is now disabled. |
905 | set_enabled(false); |
906 | return; |
907 | } |
908 | ASSERT(event != NULL); |
909 | // Emit an end event. |
910 | event->End(label()); |
911 | StealArguments(event); |
912 | event->Complete(); |
913 | } |
914 | |
915 | TimelineEventFilter::TimelineEventFilter(int64_t time_origin_micros, |
916 | int64_t time_extent_micros) |
917 | : time_origin_micros_(time_origin_micros), |
918 | time_extent_micros_(time_extent_micros) { |
919 | ASSERT(time_origin_micros_ >= -1); |
920 | ASSERT(time_extent_micros_ >= -1); |
921 | } |
922 | |
923 | TimelineEventFilter::~TimelineEventFilter() {} |
924 | |
925 | IsolateTimelineEventFilter::IsolateTimelineEventFilter( |
926 | Dart_Port isolate_id, |
927 | int64_t time_origin_micros, |
928 | int64_t time_extent_micros) |
929 | : TimelineEventFilter(time_origin_micros, time_extent_micros), |
930 | isolate_id_(isolate_id) {} |
931 | |
932 | TimelineEventRecorder::TimelineEventRecorder() |
933 | : async_id_(0), time_low_micros_(0), time_high_micros_(0) {} |
934 | |
935 | #ifndef PRODUCT |
936 | void TimelineEventRecorder::PrintJSONMeta(JSONArray* events) const { |
937 | OSThreadIterator it; |
938 | while (it.HasNext()) { |
939 | OSThread* thread = it.Next(); |
940 | const char* thread_name = thread->name(); |
941 | if (thread_name == NULL) { |
942 | // Only emit a thread name if one was set. |
943 | continue; |
944 | } |
945 | JSONObject obj(events); |
946 | int64_t pid = OS::ProcessId(); |
947 | int64_t tid = OSThread::ThreadIdToIntPtr(thread->trace_id()); |
948 | obj.AddProperty("name" , "thread_name" ); |
949 | obj.AddProperty("ph" , "M" ); |
950 | obj.AddProperty64("pid" , pid); |
951 | obj.AddProperty64("tid" , tid); |
952 | { |
953 | JSONObject args(&obj, "args" ); |
954 | args.AddPropertyF("name" , "%s (%" Pd64 ")" , thread_name, tid); |
955 | args.AddProperty("mode" , "basic" ); |
956 | } |
957 | } |
958 | } |
959 | #endif |
960 | |
961 | TimelineEvent* TimelineEventRecorder::ThreadBlockStartEvent() { |
962 | // Grab the current thread. |
963 | OSThread* thread = OSThread::Current(); |
964 | ASSERT(thread != NULL); |
965 | Mutex* thread_block_lock = thread->timeline_block_lock(); |
966 | ASSERT(thread_block_lock != NULL); |
967 | // We are accessing the thread's timeline block- so take the lock here. |
968 | // This lock will be held until the call to |CompleteEvent| is made. |
969 | thread_block_lock->Lock(); |
970 | #if defined(DEBUG) |
971 | Thread* T = Thread::Current(); |
972 | if (T != NULL) { |
973 | T->IncrementNoSafepointScopeDepth(); |
974 | } |
975 | #endif // defined(DEBUG) |
976 | |
977 | TimelineEventBlock* thread_block = thread->timeline_block(); |
978 | |
979 | if ((thread_block != NULL) && thread_block->IsFull()) { |
980 | MutexLocker ml(&lock_); |
981 | // Thread has a block and it is full: |
982 | // 1) Mark it as finished. |
983 | thread_block->Finish(); |
984 | // 2) Allocate a new block. |
985 | thread_block = GetNewBlockLocked(); |
986 | thread->set_timeline_block(thread_block); |
987 | } else if (thread_block == NULL) { |
988 | MutexLocker ml(&lock_); |
989 | // Thread has no block. Attempt to allocate one. |
990 | thread_block = GetNewBlockLocked(); |
991 | thread->set_timeline_block(thread_block); |
992 | } |
993 | if (thread_block != NULL) { |
994 | // NOTE: We are exiting this function with the thread's block lock held. |
995 | ASSERT(!thread_block->IsFull()); |
996 | TimelineEvent* event = thread_block->StartEvent(); |
997 | return event; |
998 | } |
999 | // Drop lock here as no event is being handed out. |
1000 | #if defined(DEBUG) |
1001 | if (T != NULL) { |
1002 | T->DecrementNoSafepointScopeDepth(); |
1003 | } |
1004 | #endif // defined(DEBUG) |
1005 | thread_block_lock->Unlock(); |
1006 | return NULL; |
1007 | } |
1008 | |
1009 | void TimelineEventRecorder::ResetTimeTracking() { |
1010 | time_high_micros_ = 0; |
1011 | time_low_micros_ = kMaxInt64; |
1012 | } |
1013 | |
1014 | void TimelineEventRecorder::ReportTime(int64_t micros) { |
1015 | if (time_high_micros_ < micros) { |
1016 | time_high_micros_ = micros; |
1017 | } |
1018 | if (time_low_micros_ > micros) { |
1019 | time_low_micros_ = micros; |
1020 | } |
1021 | } |
1022 | |
1023 | int64_t TimelineEventRecorder::TimeOriginMicros() const { |
1024 | if (time_high_micros_ == 0) { |
1025 | return 0; |
1026 | } |
1027 | return time_low_micros_; |
1028 | } |
1029 | |
1030 | int64_t TimelineEventRecorder::TimeExtentMicros() const { |
1031 | if (time_high_micros_ == 0) { |
1032 | return 0; |
1033 | } |
1034 | return time_high_micros_ - time_low_micros_; |
1035 | } |
1036 | |
1037 | void TimelineEventRecorder::ThreadBlockCompleteEvent(TimelineEvent* event) { |
1038 | if (event == NULL) { |
1039 | return; |
1040 | } |
1041 | // Grab the current thread. |
1042 | OSThread* thread = OSThread::Current(); |
1043 | ASSERT(thread != NULL); |
1044 | // Unlock the thread's block lock. |
1045 | Mutex* thread_block_lock = thread->timeline_block_lock(); |
1046 | ASSERT(thread_block_lock != NULL); |
1047 | #if defined(DEBUG) |
1048 | Thread* T = Thread::Current(); |
1049 | if (T != NULL) { |
1050 | T->DecrementNoSafepointScopeDepth(); |
1051 | } |
1052 | #endif // defined(DEBUG) |
1053 | thread_block_lock->Unlock(); |
1054 | } |
1055 | |
1056 | #ifndef PRODUCT |
1057 | void TimelineEventRecorder::WriteTo(const char* directory) { |
1058 | Dart_FileOpenCallback file_open = Dart::file_open_callback(); |
1059 | Dart_FileWriteCallback file_write = Dart::file_write_callback(); |
1060 | Dart_FileCloseCallback file_close = Dart::file_close_callback(); |
1061 | if ((file_open == NULL) || (file_write == NULL) || (file_close == NULL)) { |
1062 | return; |
1063 | } |
1064 | |
1065 | Timeline::ReclaimCachedBlocksFromThreads(); |
1066 | |
1067 | intptr_t pid = OS::ProcessId(); |
1068 | char* filename = |
1069 | OS::SCreate(NULL, "%s/dart-timeline-%" Pd ".json" , directory, pid); |
1070 | void* file = (*file_open)(filename, true); |
1071 | if (file == NULL) { |
1072 | OS::PrintErr("Failed to write timeline file: %s\n" , filename); |
1073 | free(filename); |
1074 | return; |
1075 | } |
1076 | free(filename); |
1077 | |
1078 | JSONStream js; |
1079 | TimelineEventFilter filter; |
1080 | PrintTraceEvent(&js, &filter); |
1081 | // Steal output from JSONStream. |
1082 | char* output = NULL; |
1083 | intptr_t output_length = 0; |
1084 | js.Steal(&output, &output_length); |
1085 | (*file_write)(output, output_length, file); |
1086 | // Free the stolen output. |
1087 | free(output); |
1088 | (*file_close)(file); |
1089 | |
1090 | return; |
1091 | } |
1092 | #endif |
1093 | |
1094 | int64_t TimelineEventRecorder::GetNextAsyncId() { |
1095 | // TODO(johnmccutchan): Gracefully handle wrap around. |
1096 | #if defined(HOST_OS_FUCHSIA) |
1097 | return trace_generate_nonce(); |
1098 | #else |
1099 | uint32_t next = static_cast<uint32_t>(async_id_.fetch_add(1u)); |
1100 | return static_cast<int64_t>(next); |
1101 | #endif |
1102 | } |
1103 | |
1104 | void TimelineEventRecorder::FinishBlock(TimelineEventBlock* block) { |
1105 | if (block == NULL) { |
1106 | return; |
1107 | } |
1108 | MutexLocker ml(&lock_); |
1109 | block->Finish(); |
1110 | } |
1111 | |
1112 | TimelineEventBlock* TimelineEventRecorder::GetNewBlock() { |
1113 | MutexLocker ml(&lock_); |
1114 | return GetNewBlockLocked(); |
1115 | } |
1116 | |
1117 | TimelineEventFixedBufferRecorder::TimelineEventFixedBufferRecorder( |
1118 | intptr_t capacity) |
1119 | : memory_(NULL), |
1120 | blocks_(NULL), |
1121 | capacity_(capacity), |
1122 | num_blocks_(0), |
1123 | block_cursor_(0) { |
1124 | // Capacity must be a multiple of TimelineEventBlock::kBlockSize |
1125 | ASSERT((capacity % TimelineEventBlock::kBlockSize) == 0); |
1126 | // Allocate blocks array. |
1127 | num_blocks_ = capacity / TimelineEventBlock::kBlockSize; |
1128 | |
1129 | intptr_t size = Utils::RoundUp(num_blocks_ * sizeof(TimelineEventBlock), |
1130 | VirtualMemory::PageSize()); |
1131 | const bool kNotExecutable = false; |
1132 | memory_ = VirtualMemory::Allocate(size, kNotExecutable, "dart-timeline" ); |
1133 | if (memory_ == NULL) { |
1134 | OUT_OF_MEMORY(); |
1135 | } |
1136 | blocks_ = reinterpret_cast<TimelineEventBlock*>(memory_->address()); |
1137 | } |
1138 | |
1139 | TimelineEventFixedBufferRecorder::~TimelineEventFixedBufferRecorder() { |
1140 | // Delete all blocks. |
1141 | for (intptr_t i = 0; i < num_blocks_; i++) { |
1142 | blocks_[i].Reset(); |
1143 | } |
1144 | delete memory_; |
1145 | } |
1146 | |
1147 | intptr_t TimelineEventFixedBufferRecorder::Size() { |
1148 | return memory_->size(); |
1149 | } |
1150 | |
1151 | #ifndef PRODUCT |
1152 | void TimelineEventFixedBufferRecorder::PrintJSONEvents( |
1153 | JSONArray* events, |
1154 | TimelineEventFilter* filter) { |
1155 | MutexLocker ml(&lock_); |
1156 | ResetTimeTracking(); |
1157 | intptr_t block_offset = FindOldestBlockIndex(); |
1158 | if (block_offset == -1) { |
1159 | // All blocks are empty. |
1160 | return; |
1161 | } |
1162 | for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) { |
1163 | TimelineEventBlock* block = |
1164 | &blocks_[(block_idx + block_offset) % num_blocks_]; |
1165 | if (!filter->IncludeBlock(block)) { |
1166 | continue; |
1167 | } |
1168 | for (intptr_t event_idx = 0; event_idx < block->length(); event_idx++) { |
1169 | TimelineEvent* event = block->At(event_idx); |
1170 | if (filter->IncludeEvent(event) && |
1171 | event->Within(filter->time_origin_micros(), |
1172 | filter->time_extent_micros())) { |
1173 | ReportTime(event->LowTime()); |
1174 | ReportTime(event->HighTime()); |
1175 | events->AddValue(event); |
1176 | } |
1177 | } |
1178 | } |
1179 | } |
1180 | |
1181 | void TimelineEventFixedBufferRecorder::PrintJSON(JSONStream* js, |
1182 | TimelineEventFilter* filter) { |
1183 | JSONObject topLevel(js); |
1184 | topLevel.AddProperty("type" , "Timeline" ); |
1185 | { |
1186 | JSONArray events(&topLevel, "traceEvents" ); |
1187 | PrintJSONMeta(&events); |
1188 | PrintJSONEvents(&events, filter); |
1189 | } |
1190 | topLevel.AddPropertyTimeMicros("timeOriginMicros" , TimeOriginMicros()); |
1191 | topLevel.AddPropertyTimeMicros("timeExtentMicros" , TimeExtentMicros()); |
1192 | } |
1193 | |
1194 | void TimelineEventFixedBufferRecorder::PrintTraceEvent( |
1195 | JSONStream* js, |
1196 | TimelineEventFilter* filter) { |
1197 | JSONArray events(js); |
1198 | PrintJSONMeta(&events); |
1199 | PrintJSONEvents(&events, filter); |
1200 | } |
1201 | #endif |
1202 | |
1203 | TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() { |
1204 | return &blocks_[0]; |
1205 | } |
1206 | |
1207 | void TimelineEventFixedBufferRecorder::Clear() { |
1208 | MutexLocker ml(&lock_); |
1209 | for (intptr_t i = 0; i < num_blocks_; i++) { |
1210 | TimelineEventBlock* block = &blocks_[i]; |
1211 | block->Reset(); |
1212 | } |
1213 | } |
1214 | |
1215 | intptr_t TimelineEventFixedBufferRecorder::FindOldestBlockIndex() const { |
1216 | int64_t earliest_time = kMaxInt64; |
1217 | intptr_t earliest_index = -1; |
1218 | for (intptr_t block_idx = 0; block_idx < num_blocks_; block_idx++) { |
1219 | TimelineEventBlock* block = &blocks_[block_idx]; |
1220 | if (block->IsEmpty()) { |
1221 | // Skip empty blocks. |
1222 | continue; |
1223 | } |
1224 | if (block->LowerTimeBound() < earliest_time) { |
1225 | earliest_time = block->LowerTimeBound(); |
1226 | earliest_index = block_idx; |
1227 | } |
1228 | } |
1229 | return earliest_index; |
1230 | } |
1231 | |
1232 | TimelineEvent* TimelineEventFixedBufferRecorder::StartEvent() { |
1233 | return ThreadBlockStartEvent(); |
1234 | } |
1235 | |
1236 | void TimelineEventFixedBufferRecorder::CompleteEvent(TimelineEvent* event) { |
1237 | if (event == NULL) { |
1238 | return; |
1239 | } |
1240 | ThreadBlockCompleteEvent(event); |
1241 | } |
1242 | |
1243 | TimelineEventBlock* TimelineEventRingRecorder::GetNewBlockLocked() { |
1244 | // TODO(johnmccutchan): This function should only hand out blocks |
1245 | // which have been marked as finished. |
1246 | if (block_cursor_ == num_blocks_) { |
1247 | block_cursor_ = 0; |
1248 | } |
1249 | TimelineEventBlock* block = &blocks_[block_cursor_++]; |
1250 | block->Reset(); |
1251 | block->Open(); |
1252 | return block; |
1253 | } |
1254 | |
1255 | TimelineEventBlock* TimelineEventStartupRecorder::GetNewBlockLocked() { |
1256 | if (block_cursor_ == num_blocks_) { |
1257 | return NULL; |
1258 | } |
1259 | TimelineEventBlock* block = &blocks_[block_cursor_++]; |
1260 | block->Reset(); |
1261 | block->Open(); |
1262 | return block; |
1263 | } |
1264 | |
1265 | TimelineEventCallbackRecorder::TimelineEventCallbackRecorder() {} |
1266 | |
1267 | TimelineEventCallbackRecorder::~TimelineEventCallbackRecorder() {} |
1268 | |
1269 | #ifndef PRODUCT |
1270 | void TimelineEventCallbackRecorder::PrintJSON(JSONStream* js, |
1271 | TimelineEventFilter* filter) { |
1272 | JSONObject topLevel(js); |
1273 | topLevel.AddProperty("type" , "Timeline" ); |
1274 | { |
1275 | JSONArray events(&topLevel, "traceEvents" ); |
1276 | PrintJSONMeta(&events); |
1277 | } |
1278 | topLevel.AddPropertyTimeMicros("timeOriginMicros" , TimeOriginMicros()); |
1279 | topLevel.AddPropertyTimeMicros("timeExtentMicros" , TimeExtentMicros()); |
1280 | } |
1281 | |
1282 | void TimelineEventCallbackRecorder::PrintTraceEvent( |
1283 | JSONStream* js, |
1284 | TimelineEventFilter* filter) { |
1285 | JSONArray events(js); |
1286 | } |
1287 | #endif |
1288 | |
1289 | TimelineEvent* TimelineEventCallbackRecorder::StartEvent() { |
1290 | TimelineEvent* event = new TimelineEvent(); |
1291 | return event; |
1292 | } |
1293 | |
1294 | void TimelineEventCallbackRecorder::CompleteEvent(TimelineEvent* event) { |
1295 | OnEvent(event); |
1296 | delete event; |
1297 | } |
1298 | |
1299 | TimelineEventPlatformRecorder::TimelineEventPlatformRecorder() {} |
1300 | |
1301 | TimelineEventPlatformRecorder::~TimelineEventPlatformRecorder() {} |
1302 | |
1303 | #ifndef PRODUCT |
1304 | void TimelineEventPlatformRecorder::PrintJSON(JSONStream* js, |
1305 | TimelineEventFilter* filter) { |
1306 | JSONObject topLevel(js); |
1307 | topLevel.AddProperty("type" , "Timeline" ); |
1308 | { |
1309 | JSONArray events(&topLevel, "traceEvents" ); |
1310 | PrintJSONMeta(&events); |
1311 | } |
1312 | topLevel.AddPropertyTimeMicros("timeOriginMicros" , TimeOriginMicros()); |
1313 | topLevel.AddPropertyTimeMicros("timeExtentMicros" , TimeExtentMicros()); |
1314 | } |
1315 | |
1316 | void TimelineEventPlatformRecorder::PrintTraceEvent( |
1317 | JSONStream* js, |
1318 | TimelineEventFilter* filter) { |
1319 | JSONArray events(js); |
1320 | } |
1321 | #endif |
1322 | |
1323 | TimelineEvent* TimelineEventPlatformRecorder::StartEvent() { |
1324 | TimelineEvent* event = new TimelineEvent(); |
1325 | return event; |
1326 | } |
1327 | |
1328 | void TimelineEventPlatformRecorder::CompleteEvent(TimelineEvent* event) { |
1329 | OnEvent(event); |
1330 | delete event; |
1331 | } |
1332 | |
1333 | TimelineEventEndlessRecorder::TimelineEventEndlessRecorder() |
1334 | : head_(nullptr), tail_(nullptr), block_index_(0) {} |
1335 | |
1336 | TimelineEventEndlessRecorder::~TimelineEventEndlessRecorder() { |
1337 | TimelineEventBlock* current = head_; |
1338 | head_ = tail_ = nullptr; |
1339 | |
1340 | while (current != nullptr) { |
1341 | TimelineEventBlock* next = current->next(); |
1342 | delete current; |
1343 | current = next; |
1344 | } |
1345 | } |
1346 | |
1347 | #ifndef PRODUCT |
1348 | void TimelineEventEndlessRecorder::PrintJSON(JSONStream* js, |
1349 | TimelineEventFilter* filter) { |
1350 | JSONObject topLevel(js); |
1351 | topLevel.AddProperty("type" , "Timeline" ); |
1352 | { |
1353 | JSONArray events(&topLevel, "traceEvents" ); |
1354 | PrintJSONMeta(&events); |
1355 | PrintJSONEvents(&events, filter); |
1356 | } |
1357 | topLevel.AddPropertyTimeMicros("timeOriginMicros" , TimeOriginMicros()); |
1358 | topLevel.AddPropertyTimeMicros("timeExtentMicros" , TimeExtentMicros()); |
1359 | } |
1360 | |
1361 | void TimelineEventEndlessRecorder::PrintTraceEvent( |
1362 | JSONStream* js, |
1363 | TimelineEventFilter* filter) { |
1364 | JSONArray events(js); |
1365 | PrintJSONMeta(&events); |
1366 | PrintJSONEvents(&events, filter); |
1367 | } |
1368 | #endif |
1369 | |
1370 | TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() { |
1371 | return head_; |
1372 | } |
1373 | |
1374 | TimelineEvent* TimelineEventEndlessRecorder::StartEvent() { |
1375 | return ThreadBlockStartEvent(); |
1376 | } |
1377 | |
1378 | void TimelineEventEndlessRecorder::CompleteEvent(TimelineEvent* event) { |
1379 | if (event == NULL) { |
1380 | return; |
1381 | } |
1382 | ThreadBlockCompleteEvent(event); |
1383 | } |
1384 | |
1385 | TimelineEventBlock* TimelineEventEndlessRecorder::GetNewBlockLocked() { |
1386 | TimelineEventBlock* block = new TimelineEventBlock(block_index_++); |
1387 | block->Open(); |
1388 | if (head_ == nullptr) { |
1389 | head_ = tail_ = block; |
1390 | } else { |
1391 | tail_->set_next(block); |
1392 | tail_ = block; |
1393 | } |
1394 | if (FLAG_trace_timeline) { |
1395 | OS::PrintErr("Created new block %p\n" , block); |
1396 | } |
1397 | return block; |
1398 | } |
1399 | |
1400 | #ifndef PRODUCT |
1401 | void TimelineEventEndlessRecorder::PrintJSONEvents( |
1402 | JSONArray* events, |
1403 | TimelineEventFilter* filter) { |
1404 | MutexLocker ml(&lock_); |
1405 | ResetTimeTracking(); |
1406 | for (TimelineEventBlock* current = head_; current != nullptr; |
1407 | current = current->next()) { |
1408 | if (!filter->IncludeBlock(current)) { |
1409 | continue; |
1410 | } |
1411 | intptr_t length = current->length(); |
1412 | for (intptr_t i = 0; i < length; i++) { |
1413 | TimelineEvent* event = current->At(i); |
1414 | if (filter->IncludeEvent(event) && |
1415 | event->Within(filter->time_origin_micros(), |
1416 | filter->time_extent_micros())) { |
1417 | ReportTime(event->LowTime()); |
1418 | ReportTime(event->HighTime()); |
1419 | events->AddValue(event); |
1420 | } |
1421 | } |
1422 | } |
1423 | } |
1424 | #endif |
1425 | |
1426 | void TimelineEventEndlessRecorder::Clear() { |
1427 | TimelineEventBlock* current = head_; |
1428 | while (current != NULL) { |
1429 | TimelineEventBlock* next = current->next(); |
1430 | delete current; |
1431 | current = next; |
1432 | } |
1433 | head_ = NULL; |
1434 | block_index_ = 0; |
1435 | OSThread* thread = OSThread::Current(); |
1436 | thread->set_timeline_block(NULL); |
1437 | } |
1438 | |
1439 | TimelineEventBlock::TimelineEventBlock(intptr_t block_index) |
1440 | : next_(NULL), |
1441 | length_(0), |
1442 | block_index_(block_index), |
1443 | thread_id_(OSThread::kInvalidThreadId), |
1444 | in_use_(false) {} |
1445 | |
1446 | TimelineEventBlock::~TimelineEventBlock() { |
1447 | Reset(); |
1448 | } |
1449 | |
1450 | #ifndef PRODUCT |
1451 | void TimelineEventBlock::PrintJSON(JSONStream* js) const { |
1452 | ASSERT(!in_use()); |
1453 | JSONArray events(js); |
1454 | for (intptr_t i = 0; i < length(); i++) { |
1455 | const TimelineEvent* event = At(i); |
1456 | events.AddValue(event); |
1457 | } |
1458 | } |
1459 | #endif |
1460 | |
1461 | TimelineEvent* TimelineEventBlock::StartEvent() { |
1462 | ASSERT(!IsFull()); |
1463 | if (FLAG_trace_timeline) { |
1464 | OSThread* os_thread = OSThread::Current(); |
1465 | ASSERT(os_thread != NULL); |
1466 | intptr_t tid = OSThread::ThreadIdToIntPtr(os_thread->id()); |
1467 | OS::PrintErr("StartEvent in block %p for thread %" Pd "\n" , this, tid); |
1468 | } |
1469 | return &events_[length_++]; |
1470 | } |
1471 | |
1472 | int64_t TimelineEventBlock::LowerTimeBound() const { |
1473 | if (length_ == 0) { |
1474 | return kMaxInt64; |
1475 | } |
1476 | ASSERT(length_ > 0); |
1477 | return events_[0].TimeOrigin(); |
1478 | } |
1479 | |
1480 | bool TimelineEventBlock::CheckBlock() { |
1481 | if (length() == 0) { |
1482 | return true; |
1483 | } |
1484 | |
1485 | for (intptr_t i = 0; i < length(); i++) { |
1486 | if (At(i)->thread() != thread_id()) { |
1487 | return false; |
1488 | } |
1489 | } |
1490 | |
1491 | // - events have monotonically increasing timestamps. |
1492 | int64_t last_time = LowerTimeBound(); |
1493 | for (intptr_t i = 0; i < length(); i++) { |
1494 | if (last_time > At(i)->TimeOrigin()) { |
1495 | return false; |
1496 | } |
1497 | last_time = At(i)->TimeOrigin(); |
1498 | } |
1499 | |
1500 | return true; |
1501 | } |
1502 | |
1503 | void TimelineEventBlock::Reset() { |
1504 | for (intptr_t i = 0; i < kBlockSize; i++) { |
1505 | // Clear any extra data. |
1506 | events_[i].Reset(); |
1507 | } |
1508 | length_ = 0; |
1509 | thread_id_ = OSThread::kInvalidThreadId; |
1510 | in_use_ = false; |
1511 | } |
1512 | |
1513 | void TimelineEventBlock::Open() { |
1514 | OSThread* os_thread = OSThread::Current(); |
1515 | ASSERT(os_thread != NULL); |
1516 | thread_id_ = os_thread->trace_id(); |
1517 | in_use_ = true; |
1518 | } |
1519 | |
1520 | void TimelineEventBlock::Finish() { |
1521 | if (FLAG_trace_timeline) { |
1522 | OS::PrintErr("Finish block %p\n" , this); |
1523 | } |
1524 | in_use_ = false; |
1525 | #ifndef PRODUCT |
1526 | if (Service::timeline_stream.enabled()) { |
1527 | ServiceEvent service_event(NULL, ServiceEvent::kTimelineEvents); |
1528 | service_event.set_timeline_event_block(this); |
1529 | Service::HandleEvent(&service_event); |
1530 | } |
1531 | #endif |
1532 | } |
1533 | |
1534 | TimelineEventBlockIterator::TimelineEventBlockIterator( |
1535 | TimelineEventRecorder* recorder) |
1536 | : current_(NULL), recorder_(NULL) { |
1537 | Reset(recorder); |
1538 | } |
1539 | |
1540 | TimelineEventBlockIterator::~TimelineEventBlockIterator() { |
1541 | Reset(NULL); |
1542 | } |
1543 | |
1544 | void TimelineEventBlockIterator::Reset(TimelineEventRecorder* recorder) { |
1545 | // Clear current. |
1546 | current_ = NULL; |
1547 | if (recorder_ != NULL) { |
1548 | // Unlock old recorder. |
1549 | recorder_->lock_.Unlock(); |
1550 | } |
1551 | recorder_ = recorder; |
1552 | if (recorder_ == NULL) { |
1553 | return; |
1554 | } |
1555 | // Lock new recorder. |
1556 | recorder_->lock_.Lock(); |
1557 | // Queue up first block. |
1558 | current_ = recorder_->GetHeadBlockLocked(); |
1559 | } |
1560 | |
1561 | bool TimelineEventBlockIterator::HasNext() const { |
1562 | return current_ != NULL; |
1563 | } |
1564 | |
1565 | TimelineEventBlock* TimelineEventBlockIterator::Next() { |
1566 | ASSERT(current_ != NULL); |
1567 | TimelineEventBlock* r = current_; |
1568 | current_ = current_->next(); |
1569 | return r; |
1570 | } |
1571 | |
1572 | void DartTimelineEventHelpers::ReportTaskEvent(Thread* thread, |
1573 | TimelineEvent* event, |
1574 | int64_t id, |
1575 | const char* phase, |
1576 | const char* category, |
1577 | char* name, |
1578 | char* args) { |
1579 | ASSERT(phase != NULL); |
1580 | ASSERT((phase[0] == 'n') || (phase[0] == 'b') || (phase[0] == 'e') || |
1581 | (phase[0] == 'B') || (phase[0] == 'E')); |
1582 | ASSERT(phase[1] == '\0'); |
1583 | const int64_t start = OS::GetCurrentMonotonicMicros(); |
1584 | const int64_t start_cpu = OS::GetCurrentThreadCPUMicrosForTimeline(); |
1585 | switch (phase[0]) { |
1586 | case 'n': |
1587 | event->AsyncInstant(name, id, start); |
1588 | break; |
1589 | case 'b': |
1590 | event->AsyncBegin(name, id, start); |
1591 | break; |
1592 | case 'e': |
1593 | event->AsyncEnd(name, id, start); |
1594 | break; |
1595 | case 'B': |
1596 | event->Begin(name, start, start_cpu); |
1597 | break; |
1598 | case 'E': |
1599 | event->End(name, start, start_cpu); |
1600 | break; |
1601 | default: |
1602 | UNREACHABLE(); |
1603 | } |
1604 | event->set_owns_label(true); |
1605 | event->CompleteWithPreSerializedArgs(args); |
1606 | } |
1607 | |
1608 | void DartTimelineEventHelpers::ReportFlowEvent(Thread* thread, |
1609 | TimelineEvent* event, |
1610 | const char* category, |
1611 | char* name, |
1612 | int64_t type, |
1613 | int64_t flow_id, |
1614 | char* args) { |
1615 | const int64_t start = OS::GetCurrentMonotonicMicros(); |
1616 | TimelineEvent::EventType event_type = |
1617 | static_cast<TimelineEvent::EventType>(type); |
1618 | switch (event_type) { |
1619 | case TimelineEvent::kFlowBegin: |
1620 | event->FlowBegin(name, flow_id, start); |
1621 | break; |
1622 | case TimelineEvent::kFlowStep: |
1623 | event->FlowStep(name, flow_id, start); |
1624 | break; |
1625 | case TimelineEvent::kFlowEnd: |
1626 | event->FlowEnd(name, flow_id, start); |
1627 | break; |
1628 | default: |
1629 | UNREACHABLE(); |
1630 | break; |
1631 | } |
1632 | event->set_owns_label(true); |
1633 | event->CompleteWithPreSerializedArgs(args); |
1634 | } |
1635 | |
1636 | void DartTimelineEventHelpers::ReportInstantEvent(Thread* thread, |
1637 | TimelineEvent* event, |
1638 | const char* category, |
1639 | char* name, |
1640 | char* args) { |
1641 | const int64_t start = OS::GetCurrentMonotonicMicros(); |
1642 | event->Instant(name, start); |
1643 | event->set_owns_label(true); |
1644 | event->CompleteWithPreSerializedArgs(args); |
1645 | } |
1646 | |
1647 | } // namespace dart |
1648 | |
1649 | #endif // defined(SUPPORT_TIMELINE) |
1650 | |