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 | #ifndef RUNTIME_VM_TIMELINE_H_ |
6 | #define RUNTIME_VM_TIMELINE_H_ |
7 | |
8 | #include "include/dart_tools_api.h" |
9 | |
10 | #include "platform/atomic.h" |
11 | #include "vm/allocation.h" |
12 | #include "vm/bitfield.h" |
13 | #include "vm/globals.h" |
14 | #include "vm/growable_array.h" |
15 | #include "vm/os.h" |
16 | #include "vm/os_thread.h" |
17 | |
18 | #if defined(FUCHSIA_SDK) || defined (HOST_OS_FUCHSIA) |
19 | #include <lib/trace-engine/context.h> |
20 | #include <lib/trace-engine/instrumentation.h> |
21 | #elif defined(HOST_OS_MACOS) |
22 | #include <os/availability.h> |
23 | #if defined(__MAC_10_14) || defined (__IPHONE_12_0) |
24 | #define HOST_OS_SUPPORTS_SIGNPOST 1 |
25 | #endif |
26 | //signpost.h exists in macOS 10.14, iOS 12 or above |
27 | #if defined(HOST_OS_SUPPORTS_SIGNPOST) |
28 | #include <os/signpost.h> |
29 | #else |
30 | #include <os/log.h> |
31 | #endif |
32 | #endif |
33 | |
34 | namespace dart { |
35 | |
36 | class JSONArray; |
37 | class JSONObject; |
38 | class JSONStream; |
39 | class Object; |
40 | class ObjectPointerVisitor; |
41 | class Isolate; |
42 | class Thread; |
43 | class TimelineEvent; |
44 | class TimelineEventBlock; |
45 | class TimelineEventRecorder; |
46 | class TimelineStream; |
47 | class VirtualMemory; |
48 | class Zone; |
49 | |
50 | #define CALLBACK_RECORDER_NAME "Callback" |
51 | #define ENDLESS_RECORDER_NAME "Endless" |
52 | #define FUCHSIA_RECORDER_NAME "Fuchsia" |
53 | #define MACOS_RECORDER_NAME "Macos" |
54 | #define RING_RECORDER_NAME "Ring" |
55 | #define STARTUP_RECORDER_NAME "Startup" |
56 | #define SYSTRACE_RECORDER_NAME "Systrace" |
57 | |
58 | // (name, fuchsia_name). |
59 | #define TIMELINE_STREAM_LIST(V) \ |
60 | V(API, "dart:api") \ |
61 | V(Compiler, "dart:compiler") \ |
62 | V(CompilerVerbose, "dart:compiler.verbose") \ |
63 | V(Dart, "dart:dart") \ |
64 | V(Debugger, "dart:debugger") \ |
65 | V(Embedder, "dart:embedder") \ |
66 | V(GC, "dart:gc") \ |
67 | V(Isolate, "dart:isolate") \ |
68 | V(VM, "dart:vm") |
69 | |
70 | // A stream of timeline events. A stream has a name and can be enabled or |
71 | // disabled (globally and per isolate). |
72 | class TimelineStream { |
73 | public: |
74 | TimelineStream(const char* name, const char* fuchsia_name, bool enabled); |
75 | |
76 | const char* name() const { return name_; } |
77 | const char* fuchsia_name() const { return fuchsia_name_; } |
78 | |
79 | bool enabled() { |
80 | #if defined(HOST_OS_FUCHSIA) |
81 | #ifdef PRODUCT |
82 | return trace_is_category_enabled(fuchsia_name_); |
83 | #else |
84 | return trace_is_category_enabled(fuchsia_name_) || enabled_ != 0; |
85 | #endif // PRODUCT |
86 | #else |
87 | return enabled_ != 0; |
88 | #endif // defined(HOST_OS_FUCHSIA) |
89 | } |
90 | |
91 | void set_enabled(bool enabled) { enabled_ = enabled ? 1 : 0; } |
92 | |
93 | // Records an event. Will return |NULL| if not enabled. The returned |
94 | // |TimelineEvent| is in an undefined state and must be initialized. |
95 | // NOTE: It is not allowed to call StartEvent again without completing |
96 | // the first event. |
97 | TimelineEvent* StartEvent(); |
98 | |
99 | static intptr_t enabled_offset() { |
100 | return OFFSET_OF(TimelineStream, enabled_); |
101 | } |
102 | |
103 | #if defined(HOST_OS_FUCHSIA) |
104 | trace_site_t* trace_site() { return &trace_site_; } |
105 | #elif defined(HOST_OS_MACOS) |
106 | os_log_t macos_log() { return macos_log_; } |
107 | #endif |
108 | |
109 | private: |
110 | const char* const name_; |
111 | const char* const fuchsia_name_; |
112 | |
113 | // This field is accessed by generated code (intrinsic) and expects to see |
114 | // 0 or 1. If this becomes a BitField, the generated code must be updated. |
115 | uintptr_t enabled_; |
116 | |
117 | #if defined(HOST_OS_FUCHSIA) |
118 | trace_site_t trace_site_ = {}; |
119 | #elif defined(HOST_OS_MACOS) |
120 | os_log_t macos_log_ = {}; |
121 | #endif |
122 | }; |
123 | |
124 | class Timeline : public AllStatic { |
125 | public: |
126 | // Initialize timeline system. Not thread safe. |
127 | static void Init(); |
128 | |
129 | // Cleanup timeline system. Not thread safe. |
130 | static void Cleanup(); |
131 | |
132 | // Access the global recorder. Not thread safe. |
133 | static TimelineEventRecorder* recorder(); |
134 | |
135 | // Reclaim all |TimelineEventBlocks|s that are cached by threads. |
136 | static void ReclaimCachedBlocksFromThreads(); |
137 | |
138 | static void Clear(); |
139 | |
140 | #ifndef PRODUCT |
141 | // Print information about streams to JSON. |
142 | static void PrintFlagsToJSON(JSONStream* json); |
143 | |
144 | // Output the recorded streams to a JSONS array. |
145 | static void PrintFlagsToJSONArray(JSONArray* arr); |
146 | #endif |
147 | |
148 | #define TIMELINE_STREAM_ACCESSOR(name, fuchsia_name) \ |
149 | static TimelineStream* Get##name##Stream() { return &stream_##name##_; } |
150 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_ACCESSOR) |
151 | #undef TIMELINE_STREAM_ACCESSOR |
152 | |
153 | #define TIMELINE_STREAM_FLAGS(name, fuchsia_name) \ |
154 | static void SetStream##name##Enabled(bool enabled) { \ |
155 | stream_##name##_.set_enabled(enabled); \ |
156 | } |
157 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_FLAGS) |
158 | #undef TIMELINE_STREAM_FLAGS |
159 | |
160 | private: |
161 | static TimelineEventRecorder* recorder_; |
162 | static MallocGrowableArray<char*>* enabled_streams_; |
163 | |
164 | #define TIMELINE_STREAM_DECLARE(name, fuchsia_name) \ |
165 | static TimelineStream stream_##name##_; |
166 | TIMELINE_STREAM_LIST(TIMELINE_STREAM_DECLARE) |
167 | #undef TIMELINE_STREAM_DECLARE |
168 | |
169 | friend class TimelineRecorderOverride; |
170 | friend class ReclaimBlocksIsolateVisitor; |
171 | }; |
172 | |
173 | struct TimelineEventArgument { |
174 | const char* name; |
175 | char* value; |
176 | }; |
177 | |
178 | class TimelineEventArguments { |
179 | public: |
180 | TimelineEventArguments() : buffer_(NULL), length_(0) {} |
181 | ~TimelineEventArguments() { Free(); } |
182 | // Get/Set the number of arguments in the event. |
183 | void SetNumArguments(intptr_t length); |
184 | // |name| must be a compile time constant. Takes ownership of |argument|. |
185 | void SetArgument(intptr_t i, const char* name, char* argument); |
186 | // |name| must be a compile time constant. Copies |argument|. |
187 | void CopyArgument(intptr_t i, const char* name, const char* argument); |
188 | // |name| must be a compile time constant. Takes ownership of |args| |
189 | void FormatArgument(intptr_t i, |
190 | const char* name, |
191 | const char* fmt, |
192 | va_list args); |
193 | |
194 | void StealArguments(TimelineEventArguments* arguments); |
195 | |
196 | TimelineEventArgument* buffer() const { return buffer_; } |
197 | |
198 | intptr_t length() const { return length_; } |
199 | |
200 | void Free(); |
201 | |
202 | TimelineEventArgument& operator[](intptr_t index) const { |
203 | return buffer_[index]; |
204 | } |
205 | |
206 | bool IsEmpty() { return length_ == 0; } |
207 | |
208 | bool IsNotEmpty() { return length_ != 0; } |
209 | |
210 | private: |
211 | TimelineEventArgument* buffer_; |
212 | intptr_t length_; |
213 | DISALLOW_COPY_AND_ASSIGN(TimelineEventArguments); |
214 | }; |
215 | |
216 | // You should get a |TimelineEvent| from a |TimelineStream|. |
217 | class TimelineEvent { |
218 | public: |
219 | // Keep in sync with StateBits below. |
220 | enum EventType { |
221 | kNone, |
222 | kBegin, |
223 | kEnd, |
224 | kDuration, |
225 | kInstant, |
226 | kAsyncBegin, |
227 | kAsyncInstant, |
228 | kAsyncEnd, |
229 | kCounter, |
230 | kFlowBegin, |
231 | kFlowStep, |
232 | kFlowEnd, |
233 | kMetadata, |
234 | kNumEventTypes, |
235 | }; |
236 | |
237 | TimelineEvent(); |
238 | ~TimelineEvent(); |
239 | |
240 | void Reset(); |
241 | |
242 | bool IsValid() const { |
243 | return (event_type() > kNone) && (event_type() < kNumEventTypes); |
244 | } |
245 | |
246 | // Marks the beginning of an asynchronous operation with |async_id|. |
247 | void AsyncBegin(const char* label, |
248 | int64_t async_id, |
249 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
250 | // Marks an instantaneous event associated with |async_id|. |
251 | void AsyncInstant(const char* label, |
252 | int64_t async_id, |
253 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
254 | // Marks the end of an asynchronous operation associated with |async_id|. |
255 | void AsyncEnd(const char* label, |
256 | int64_t async_id, |
257 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
258 | |
259 | void DurationBegin( |
260 | const char* label, |
261 | int64_t micros = OS::GetCurrentMonotonicMicros(), |
262 | int64_t thread_micros = OS::GetCurrentThreadCPUMicrosForTimeline()); |
263 | void DurationEnd( |
264 | int64_t micros = OS::GetCurrentMonotonicMicros(), |
265 | int64_t thread_micros = OS::GetCurrentThreadCPUMicrosForTimeline()); |
266 | |
267 | void Instant(const char* label, |
268 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
269 | |
270 | void Duration(const char* label, |
271 | int64_t start_micros, |
272 | int64_t end_micros, |
273 | int64_t thread_start_micros = -1, |
274 | int64_t thread_end_micros = -1); |
275 | |
276 | void Begin( |
277 | const char* label, |
278 | int64_t micros = OS::GetCurrentMonotonicMicros(), |
279 | int64_t thread_micros = OS::GetCurrentThreadCPUMicrosForTimeline()); |
280 | |
281 | void End(const char* label, |
282 | int64_t micros = OS::GetCurrentMonotonicMicros(), |
283 | int64_t thread_micros = OS::GetCurrentThreadCPUMicrosForTimeline()); |
284 | |
285 | void Counter(const char* label, |
286 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
287 | |
288 | void FlowBegin(const char* label, |
289 | int64_t async_id, |
290 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
291 | void FlowStep(const char* label, |
292 | int64_t async_id, |
293 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
294 | void FlowEnd(const char* label, |
295 | int64_t async_id, |
296 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
297 | |
298 | void Metadata(const char* label, |
299 | int64_t micros = OS::GetCurrentMonotonicMicros()); |
300 | |
301 | void CompleteWithPreSerializedArgs(char* args_json); |
302 | |
303 | // Get/Set the number of arguments in the event. |
304 | intptr_t GetNumArguments() { return arguments_.length(); } |
305 | void SetNumArguments(intptr_t length) { arguments_.SetNumArguments(length); } |
306 | // |name| must be a compile time constant. Takes ownership of |argument|. |
307 | void SetArgument(intptr_t i, const char* name, char* argument) { |
308 | arguments_.SetArgument(i, name, argument); |
309 | } |
310 | // |name| must be a compile time constant. Copies |argument|. |
311 | void CopyArgument(intptr_t i, const char* name, const char* argument) { |
312 | arguments_.CopyArgument(i, name, argument); |
313 | } |
314 | // |name| must be a compile time constant. |
315 | void FormatArgument(intptr_t i, const char* name, const char* fmt, ...) |
316 | PRINTF_ATTRIBUTE(4, 5); |
317 | |
318 | void StealArguments(TimelineEventArguments* arguments) { |
319 | arguments_.StealArguments(arguments); |
320 | } |
321 | // Mandatory to call when this event is completely filled out. |
322 | void Complete(); |
323 | |
324 | EventType event_type() const { return EventTypeField::decode(state_); } |
325 | |
326 | bool IsFinishedDuration() const { |
327 | return (event_type() == kDuration) && (timestamp1_ > timestamp0_); |
328 | } |
329 | |
330 | bool HasThreadCPUTime() const; |
331 | int64_t ThreadCPUTimeDuration() const; |
332 | int64_t ThreadCPUTimeOrigin() const; |
333 | |
334 | int64_t TimeOrigin() const; |
335 | int64_t AsyncId() const; |
336 | int64_t TimeDuration() const; |
337 | int64_t TimeEnd() const { |
338 | ASSERT(IsFinishedDuration()); |
339 | return timestamp1_; |
340 | } |
341 | |
342 | // The lowest time value stored in this event. |
343 | int64_t LowTime() const; |
344 | // The highest time value stored in this event. |
345 | int64_t HighTime() const; |
346 | |
347 | #ifndef PRODUCT |
348 | void PrintJSON(JSONStream* stream) const; |
349 | #endif |
350 | |
351 | ThreadId thread() const { return thread_; } |
352 | |
353 | void set_thread(ThreadId tid) { thread_ = tid; } |
354 | |
355 | Dart_Port isolate_id() const { return isolate_id_; } |
356 | |
357 | uint64_t isolate_group_id() const { return isolate_group_id_; } |
358 | |
359 | const char* label() const { return label_; } |
360 | |
361 | // Does this duration end before |micros| ? |
362 | bool DurationFinishedBefore(int64_t micros) const { |
363 | return TimeEnd() <= micros; |
364 | } |
365 | |
366 | bool IsDuration() const { return (event_type() == kDuration); } |
367 | |
368 | bool IsBegin() const { return (event_type() == kBegin); } |
369 | |
370 | bool IsEnd() const { return (event_type() == kEnd); } |
371 | |
372 | // Is this event a synchronous begin or end event? |
373 | bool IsBeginOrEnd() const { return IsBegin() || IsEnd(); } |
374 | |
375 | // Does this duration fully contain |other| ? |
376 | bool DurationContains(TimelineEvent* other) const { |
377 | ASSERT(IsFinishedDuration()); |
378 | if (other->IsBegin()) { |
379 | if (other->TimeOrigin() < TimeOrigin()) { |
380 | return false; |
381 | } |
382 | if (other->TimeOrigin() > TimeEnd()) { |
383 | return false; |
384 | } |
385 | return true; |
386 | } else { |
387 | ASSERT(other->IsFinishedDuration()); |
388 | if (other->TimeOrigin() < TimeOrigin()) { |
389 | return false; |
390 | } |
391 | if (other->TimeEnd() < TimeOrigin()) { |
392 | return false; |
393 | } |
394 | if (other->TimeOrigin() > TimeEnd()) { |
395 | return false; |
396 | } |
397 | if (other->TimeEnd() > TimeEnd()) { |
398 | return false; |
399 | } |
400 | return true; |
401 | } |
402 | } |
403 | |
404 | bool Within(int64_t time_origin_micros, int64_t time_extent_micros); |
405 | |
406 | void set_owns_label(bool owns_label) { |
407 | state_ = OwnsLabelBit::update(owns_label, state_); |
408 | } |
409 | |
410 | TimelineEventArgument* arguments() const { return arguments_.buffer(); } |
411 | |
412 | intptr_t arguments_length() const { return arguments_.length(); } |
413 | |
414 | private: |
415 | void StreamInit(TimelineStream* stream) { stream_ = stream; } |
416 | void Init(EventType event_type, const char* label); |
417 | |
418 | void set_event_type(EventType event_type) { |
419 | // We only reserve 4 bits to hold the event type. |
420 | COMPILE_ASSERT(kNumEventTypes < 16); |
421 | state_ = EventTypeField::update(event_type, state_); |
422 | } |
423 | |
424 | void set_timestamp0(int64_t value) { |
425 | ASSERT(timestamp0_ == 0); |
426 | timestamp0_ = value; |
427 | } |
428 | void set_timestamp1(int64_t value) { |
429 | ASSERT(timestamp1_ == 0); |
430 | timestamp1_ = value; |
431 | } |
432 | |
433 | void set_thread_timestamp0(int64_t value) { |
434 | ASSERT(thread_timestamp0_ == -1); |
435 | thread_timestamp0_ = value; |
436 | } |
437 | |
438 | void set_thread_timestamp1(int64_t value) { |
439 | ASSERT(thread_timestamp1_ == -1); |
440 | thread_timestamp1_ = value; |
441 | } |
442 | |
443 | bool pre_serialized_args() const { |
444 | return PreSerializedArgsBit::decode(state_); |
445 | } |
446 | |
447 | void set_pre_serialized_args(bool pre_serialized_args) { |
448 | state_ = PreSerializedArgsBit::update(pre_serialized_args, state_); |
449 | } |
450 | |
451 | bool owns_label() const { return OwnsLabelBit::decode(state_); } |
452 | |
453 | enum StateBits { |
454 | kEventTypeBit = 0, // reserve 4 bits for type. |
455 | kPreSerializedArgsBit = 4, |
456 | kOwnsLabelBit = 5, |
457 | kNextBit = 6, |
458 | }; |
459 | |
460 | class EventTypeField : public BitField<uword, EventType, kEventTypeBit, 4> {}; |
461 | class PreSerializedArgsBit |
462 | : public BitField<uword, bool, kPreSerializedArgsBit, 1> {}; |
463 | class OwnsLabelBit : public BitField<uword, bool, kOwnsLabelBit, 1> {}; |
464 | |
465 | int64_t timestamp0_; |
466 | int64_t timestamp1_; |
467 | int64_t thread_timestamp0_; |
468 | int64_t thread_timestamp1_; |
469 | TimelineEventArguments arguments_; |
470 | uword state_; |
471 | const char* label_; |
472 | TimelineStream* stream_; |
473 | ThreadId thread_; |
474 | Dart_Port isolate_id_; |
475 | uint64_t isolate_group_id_; |
476 | |
477 | friend class TimelineEventRecorder; |
478 | friend class TimelineEventEndlessRecorder; |
479 | friend class TimelineEventRingRecorder; |
480 | friend class TimelineEventStartupRecorder; |
481 | friend class TimelineEventPlatformRecorder; |
482 | friend class TimelineEventFuchsiaRecorder; |
483 | friend class TimelineEventMacosRecorder; |
484 | friend class TimelineStream; |
485 | friend class TimelineTestHelper; |
486 | DISALLOW_COPY_AND_ASSIGN(TimelineEvent); |
487 | }; |
488 | |
489 | #ifdef SUPPORT_TIMELINE |
490 | #define TIMELINE_DURATION(thread, stream, name) \ |
491 | TimelineBeginEndScope tbes(thread, Timeline::Get##stream##Stream(), name); |
492 | #define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) \ |
493 | TimelineBeginEndScope tbes(thread, Timeline::GetCompilerStream(), name); \ |
494 | if (tbes.enabled()) { \ |
495 | tbes.SetNumArguments(1); \ |
496 | tbes.CopyArgument(0, "function", function.ToQualifiedCString()); \ |
497 | } |
498 | |
499 | #define TIMELINE_FUNCTION_GC_DURATION(thread, name) \ |
500 | TimelineBeginEndScope tbes(thread, Timeline::GetGCStream(), name); |
501 | #define TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, name) \ |
502 | TIMELINE_FUNCTION_GC_DURATION(thread, name) \ |
503 | tbes.SetNumArguments(1); \ |
504 | tbes.CopyArgument(0, "mode", "basic"); |
505 | #else |
506 | #define TIMELINE_DURATION(thread, stream, name) |
507 | #define TIMELINE_FUNCTION_COMPILATION_DURATION(thread, name, function) |
508 | #define TIMELINE_FUNCTION_GC_DURATION(thread, name) |
509 | #define TIMELINE_FUNCTION_GC_DURATION_BASIC(thread, name) |
510 | #endif // !PRODUCT |
511 | |
512 | // See |TimelineBeginEndScope|. |
513 | class TimelineEventScope : public StackResource { |
514 | public: |
515 | bool enabled() const { return enabled_; } |
516 | |
517 | intptr_t GetNumArguments() { return arguments_.length(); } |
518 | void SetNumArguments(intptr_t length); |
519 | |
520 | void SetArgument(intptr_t i, const char* name, char* argument); |
521 | |
522 | void CopyArgument(intptr_t i, const char* name, const char* argument); |
523 | |
524 | void FormatArgument(intptr_t i, const char* name, const char* fmt, ...) |
525 | PRINTF_ATTRIBUTE(4, 5); |
526 | |
527 | protected: |
528 | TimelineEventScope(TimelineStream* stream, const char* label); |
529 | |
530 | TimelineEventScope(Thread* thread, TimelineStream* stream, const char* label); |
531 | |
532 | bool ShouldEmitEvent() const { return enabled_; } |
533 | |
534 | void set_enabled(bool enabled) { enabled_ = enabled; } |
535 | |
536 | const char* label() const { return label_; } |
537 | |
538 | TimelineEventArgument* arguments() const { return arguments_.buffer(); } |
539 | |
540 | intptr_t arguments_length() const { return arguments_.length(); } |
541 | |
542 | TimelineStream* stream() const { return stream_; } |
543 | |
544 | virtual ~TimelineEventScope(); |
545 | |
546 | void StealArguments(TimelineEvent* event); |
547 | |
548 | private: |
549 | void Init(); |
550 | |
551 | TimelineStream* stream_; |
552 | const char* label_; |
553 | TimelineEventArguments arguments_; |
554 | bool enabled_; |
555 | |
556 | DISALLOW_COPY_AND_ASSIGN(TimelineEventScope); |
557 | }; |
558 | |
559 | class TimelineBeginEndScope : public TimelineEventScope { |
560 | public: |
561 | TimelineBeginEndScope(TimelineStream* stream, const char* label); |
562 | |
563 | TimelineBeginEndScope(Thread* thread, |
564 | TimelineStream* stream, |
565 | const char* label); |
566 | |
567 | virtual ~TimelineBeginEndScope(); |
568 | |
569 | private: |
570 | void EmitBegin(); |
571 | void EmitEnd(); |
572 | |
573 | DISALLOW_COPY_AND_ASSIGN(TimelineBeginEndScope); |
574 | }; |
575 | |
576 | // A block of |TimelineEvent|s. Not thread safe. |
577 | class TimelineEventBlock { |
578 | public: |
579 | static const intptr_t kBlockSize = 64; |
580 | |
581 | explicit TimelineEventBlock(intptr_t index); |
582 | ~TimelineEventBlock(); |
583 | |
584 | TimelineEventBlock* next() const { return next_; } |
585 | void set_next(TimelineEventBlock* next) { next_ = next; } |
586 | |
587 | intptr_t length() const { return length_; } |
588 | |
589 | intptr_t block_index() const { return block_index_; } |
590 | |
591 | bool IsEmpty() const { return length_ == 0; } |
592 | |
593 | bool IsFull() const { return length_ == kBlockSize; } |
594 | |
595 | TimelineEvent* At(intptr_t index) { |
596 | ASSERT(index >= 0); |
597 | ASSERT(index < kBlockSize); |
598 | return &events_[index]; |
599 | } |
600 | |
601 | const TimelineEvent* At(intptr_t index) const { |
602 | ASSERT(index >= 0); |
603 | ASSERT(index < kBlockSize); |
604 | return &events_[index]; |
605 | } |
606 | |
607 | // Attempt to sniff the timestamp from the first event. |
608 | int64_t LowerTimeBound() const; |
609 | |
610 | // Returns false if |this| violates any of the following invariants: |
611 | // - events in the block come from one thread. |
612 | // - events have monotonically increasing timestamps. |
613 | bool CheckBlock(); |
614 | |
615 | // Call Reset on all events and set length to 0. |
616 | void Reset(); |
617 | |
618 | // Only safe to access under the recorder's lock. |
619 | bool in_use() const { return in_use_; } |
620 | |
621 | // Only safe to access under the recorder's lock. |
622 | ThreadId thread_id() const { return thread_id_; } |
623 | |
624 | protected: |
625 | #ifndef PRODUCT |
626 | void PrintJSON(JSONStream* stream) const; |
627 | #endif |
628 | |
629 | TimelineEvent* StartEvent(); |
630 | |
631 | TimelineEvent events_[kBlockSize]; |
632 | TimelineEventBlock* next_; |
633 | intptr_t length_; |
634 | intptr_t block_index_; |
635 | |
636 | // Only accessed under the recorder's lock. |
637 | ThreadId thread_id_; |
638 | bool in_use_; |
639 | |
640 | void Open(); |
641 | void Finish(); |
642 | |
643 | friend class Thread; |
644 | friend class TimelineEventRecorder; |
645 | friend class TimelineEventEndlessRecorder; |
646 | friend class TimelineEventRingRecorder; |
647 | friend class TimelineEventStartupRecorder; |
648 | friend class TimelineEventPlatformRecorder; |
649 | friend class TimelineTestHelper; |
650 | friend class JSONStream; |
651 | |
652 | private: |
653 | DISALLOW_COPY_AND_ASSIGN(TimelineEventBlock); |
654 | }; |
655 | |
656 | class TimelineEventFilter : public ValueObject { |
657 | public: |
658 | TimelineEventFilter(int64_t time_origin_micros = -1, |
659 | int64_t time_extent_micros = -1); |
660 | |
661 | virtual ~TimelineEventFilter(); |
662 | |
663 | virtual bool IncludeBlock(TimelineEventBlock* block) { |
664 | if (block == NULL) { |
665 | return false; |
666 | } |
667 | // Not empty and not in use. |
668 | return !block->IsEmpty() && !block->in_use(); |
669 | } |
670 | |
671 | virtual bool IncludeEvent(TimelineEvent* event) { |
672 | if (event == NULL) { |
673 | return false; |
674 | } |
675 | return event->IsValid(); |
676 | } |
677 | |
678 | int64_t time_origin_micros() const { return time_origin_micros_; } |
679 | |
680 | int64_t time_extent_micros() const { return time_extent_micros_; } |
681 | |
682 | private: |
683 | int64_t time_origin_micros_; |
684 | int64_t time_extent_micros_; |
685 | }; |
686 | |
687 | class IsolateTimelineEventFilter : public TimelineEventFilter { |
688 | public: |
689 | explicit IsolateTimelineEventFilter(Dart_Port isolate_id, |
690 | int64_t time_origin_micros = -1, |
691 | int64_t time_extent_micros = -1); |
692 | |
693 | bool IncludeBlock(TimelineEventBlock* block) { |
694 | if (block == NULL) { |
695 | return false; |
696 | } |
697 | // Not empty, not in use, and isolate match. |
698 | return !block->IsEmpty() && !block->in_use(); |
699 | } |
700 | |
701 | bool IncludeEvent(TimelineEvent* event) { |
702 | return event->IsValid() && (event->isolate_id() == isolate_id_); |
703 | } |
704 | |
705 | private: |
706 | Dart_Port isolate_id_; |
707 | }; |
708 | |
709 | // Recorder of |TimelineEvent|s. |
710 | class TimelineEventRecorder { |
711 | public: |
712 | TimelineEventRecorder(); |
713 | virtual ~TimelineEventRecorder() {} |
714 | |
715 | TimelineEventBlock* GetNewBlock(); |
716 | |
717 | // Interface method(s) which must be implemented. |
718 | #ifndef PRODUCT |
719 | virtual void PrintJSON(JSONStream* js, TimelineEventFilter* filter) = 0; |
720 | virtual void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter) = 0; |
721 | #endif |
722 | virtual const char* name() const = 0; |
723 | int64_t GetNextAsyncId(); |
724 | |
725 | void FinishBlock(TimelineEventBlock* block); |
726 | |
727 | virtual intptr_t Size() = 0; |
728 | |
729 | protected: |
730 | #ifndef PRODUCT |
731 | void WriteTo(const char* directory); |
732 | #endif |
733 | |
734 | // Interface method(s) which must be implemented. |
735 | virtual TimelineEvent* StartEvent() = 0; |
736 | virtual void CompleteEvent(TimelineEvent* event) = 0; |
737 | virtual TimelineEventBlock* GetHeadBlockLocked() = 0; |
738 | virtual TimelineEventBlock* GetNewBlockLocked() = 0; |
739 | virtual void Clear() = 0; |
740 | |
741 | // Utility method(s). |
742 | #ifndef PRODUCT |
743 | void PrintJSONMeta(JSONArray* array) const; |
744 | #endif |
745 | TimelineEvent* ThreadBlockStartEvent(); |
746 | void ThreadBlockCompleteEvent(TimelineEvent* event); |
747 | |
748 | void ResetTimeTracking(); |
749 | void ReportTime(int64_t micros); |
750 | int64_t TimeOriginMicros() const; |
751 | int64_t TimeExtentMicros() const; |
752 | |
753 | Mutex lock_; |
754 | RelaxedAtomic<uintptr_t> async_id_; |
755 | int64_t time_low_micros_; |
756 | int64_t time_high_micros_; |
757 | |
758 | friend class TimelineEvent; |
759 | friend class TimelineEventBlockIterator; |
760 | friend class TimelineStream; |
761 | friend class TimelineTestHelper; |
762 | friend class Timeline; |
763 | |
764 | private: |
765 | DISALLOW_COPY_AND_ASSIGN(TimelineEventRecorder); |
766 | }; |
767 | |
768 | // An abstract recorder that stores events in a buffer of fixed capacity. |
769 | class TimelineEventFixedBufferRecorder : public TimelineEventRecorder { |
770 | public: |
771 | static const intptr_t kDefaultCapacity = 32 * KB; // Number of events. |
772 | |
773 | explicit TimelineEventFixedBufferRecorder(intptr_t capacity); |
774 | virtual ~TimelineEventFixedBufferRecorder(); |
775 | |
776 | #ifndef PRODUCT |
777 | void PrintJSON(JSONStream* js, TimelineEventFilter* filter); |
778 | void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter); |
779 | #endif |
780 | |
781 | intptr_t Size(); |
782 | |
783 | protected: |
784 | TimelineEvent* StartEvent(); |
785 | void CompleteEvent(TimelineEvent* event); |
786 | TimelineEventBlock* GetHeadBlockLocked(); |
787 | intptr_t FindOldestBlockIndex() const; |
788 | void Clear(); |
789 | |
790 | #ifndef PRODUCT |
791 | void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter); |
792 | #endif |
793 | |
794 | VirtualMemory* memory_; |
795 | TimelineEventBlock* blocks_; |
796 | intptr_t capacity_; |
797 | intptr_t num_blocks_; |
798 | intptr_t block_cursor_; |
799 | }; |
800 | |
801 | // A recorder that stores events in a buffer of fixed capacity. When the buffer |
802 | // is full, new events overwrite old events. |
803 | class TimelineEventRingRecorder : public TimelineEventFixedBufferRecorder { |
804 | public: |
805 | explicit TimelineEventRingRecorder(intptr_t capacity = kDefaultCapacity) |
806 | : TimelineEventFixedBufferRecorder(capacity) {} |
807 | virtual ~TimelineEventRingRecorder() {} |
808 | |
809 | const char* name() const { return RING_RECORDER_NAME; } |
810 | |
811 | protected: |
812 | TimelineEventBlock* GetNewBlockLocked(); |
813 | }; |
814 | |
815 | // A recorder that stores events in a buffer of fixed capacity. When the buffer |
816 | // is full, new events are dropped. |
817 | class TimelineEventStartupRecorder : public TimelineEventFixedBufferRecorder { |
818 | public: |
819 | explicit TimelineEventStartupRecorder(intptr_t capacity = kDefaultCapacity) |
820 | : TimelineEventFixedBufferRecorder(capacity) {} |
821 | virtual ~TimelineEventStartupRecorder() {} |
822 | |
823 | const char* name() const { return STARTUP_RECORDER_NAME; } |
824 | |
825 | protected: |
826 | TimelineEventBlock* GetNewBlockLocked(); |
827 | }; |
828 | |
829 | // An abstract recorder that calls |OnEvent| whenever an event is complete. |
830 | // This should only be used for testing. |
831 | class TimelineEventCallbackRecorder : public TimelineEventRecorder { |
832 | public: |
833 | TimelineEventCallbackRecorder(); |
834 | virtual ~TimelineEventCallbackRecorder(); |
835 | |
836 | #ifndef PRODUCT |
837 | void PrintJSON(JSONStream* js, TimelineEventFilter* filter); |
838 | void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter); |
839 | #endif |
840 | |
841 | // Called when |event| is completed. It is unsafe to keep a reference to |
842 | // |event| as it may be freed as soon as this function returns. |
843 | virtual void OnEvent(TimelineEvent* event) = 0; |
844 | |
845 | const char* name() const { return CALLBACK_RECORDER_NAME; } |
846 | |
847 | protected: |
848 | TimelineEventBlock* GetNewBlockLocked() { return NULL; } |
849 | TimelineEventBlock* GetHeadBlockLocked() { return NULL; } |
850 | void Clear() {} |
851 | TimelineEvent* StartEvent(); |
852 | void CompleteEvent(TimelineEvent* event); |
853 | }; |
854 | |
855 | // A recorder that stores events in chains of blocks of events. |
856 | // NOTE: This recorder will continue to allocate blocks until it exhausts |
857 | // memory. |
858 | class TimelineEventEndlessRecorder : public TimelineEventRecorder { |
859 | public: |
860 | TimelineEventEndlessRecorder(); |
861 | virtual ~TimelineEventEndlessRecorder(); |
862 | |
863 | #ifndef PRODUCT |
864 | void PrintJSON(JSONStream* js, TimelineEventFilter* filter); |
865 | void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter); |
866 | #endif |
867 | |
868 | const char* name() const { return ENDLESS_RECORDER_NAME; } |
869 | intptr_t Size() { return block_index_ * sizeof(TimelineEventBlock); } |
870 | |
871 | protected: |
872 | TimelineEvent* StartEvent(); |
873 | void CompleteEvent(TimelineEvent* event); |
874 | TimelineEventBlock* GetNewBlockLocked(); |
875 | TimelineEventBlock* GetHeadBlockLocked(); |
876 | void Clear(); |
877 | |
878 | #ifndef PRODUCT |
879 | void PrintJSONEvents(JSONArray* array, TimelineEventFilter* filter); |
880 | #endif |
881 | |
882 | TimelineEventBlock* head_; |
883 | TimelineEventBlock* tail_; |
884 | intptr_t block_index_; |
885 | |
886 | friend class TimelineTestHelper; |
887 | }; |
888 | |
889 | // An iterator for blocks. |
890 | class TimelineEventBlockIterator { |
891 | public: |
892 | explicit TimelineEventBlockIterator(TimelineEventRecorder* recorder); |
893 | ~TimelineEventBlockIterator(); |
894 | |
895 | void Reset(TimelineEventRecorder* recorder); |
896 | |
897 | // Returns false when there are no more blocks. |
898 | bool HasNext() const; |
899 | |
900 | // Returns the next block and moves forward. |
901 | TimelineEventBlock* Next(); |
902 | |
903 | private: |
904 | TimelineEventBlock* current_; |
905 | TimelineEventRecorder* recorder_; |
906 | }; |
907 | |
908 | // The TimelineEventPlatformRecorder records timeline events to a platform |
909 | // specific destination. It's implementation is in the timeline_{linux,...}.cc |
910 | // files. |
911 | class TimelineEventPlatformRecorder : public TimelineEventRecorder { |
912 | public: |
913 | TimelineEventPlatformRecorder(); |
914 | virtual ~TimelineEventPlatformRecorder(); |
915 | |
916 | #ifndef PRODUCT |
917 | void PrintJSON(JSONStream* js, TimelineEventFilter* filter); |
918 | void PrintTraceEvent(JSONStream* js, TimelineEventFilter* filter); |
919 | #endif |
920 | |
921 | // Called when |event| is completed. It is unsafe to keep a reference to |
922 | // |event| as it may be freed as soon as this function returns. |
923 | virtual void OnEvent(TimelineEvent* event) = 0; |
924 | |
925 | virtual const char* name() const = 0; |
926 | |
927 | protected: |
928 | TimelineEventBlock* GetNewBlockLocked() { return NULL; } |
929 | TimelineEventBlock* GetHeadBlockLocked() { return NULL; } |
930 | void Clear() {} |
931 | TimelineEvent* StartEvent(); |
932 | void CompleteEvent(TimelineEvent* event); |
933 | }; |
934 | |
935 | #if defined(HOST_OS_FUCHSIA) |
936 | // A recorder that sends events to Fuchsia's tracing app. |
937 | class TimelineEventFuchsiaRecorder : public TimelineEventPlatformRecorder { |
938 | public: |
939 | TimelineEventFuchsiaRecorder() {} |
940 | virtual ~TimelineEventFuchsiaRecorder() {} |
941 | |
942 | const char* name() const { return FUCHSIA_RECORDER_NAME; } |
943 | intptr_t Size() { return 0; } |
944 | |
945 | private: |
946 | void OnEvent(TimelineEvent* event); |
947 | }; |
948 | #endif // defined(HOST_OS_FUCHSIA) |
949 | |
950 | #if defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) |
951 | // A recorder that writes events to Android Systrace. This class is exposed in |
952 | // this header file only so that PrintSystrace can be visible to |
953 | // timeline_test.cc. |
954 | class TimelineEventSystraceRecorder : public TimelineEventPlatformRecorder { |
955 | public: |
956 | TimelineEventSystraceRecorder(); |
957 | virtual ~TimelineEventSystraceRecorder(); |
958 | |
959 | static intptr_t PrintSystrace(TimelineEvent* event, |
960 | char* buffer, |
961 | intptr_t buffer_size); |
962 | |
963 | const char* name() const { return SYSTRACE_RECORDER_NAME; } |
964 | intptr_t Size() { return 0; } |
965 | |
966 | private: |
967 | void OnEvent(TimelineEvent* event); |
968 | |
969 | int systrace_fd_; |
970 | }; |
971 | #endif // defined(HOST_OS_ANDROID) || defined(HOST_OS_LINUX) |
972 | |
973 | #if defined(HOST_OS_MACOS) |
974 | // A recorder that sends events to Macos's tracing app. See: |
975 | // https://developer.apple.com/documentation/os/logging?language=objc |
976 | class TimelineEventMacosRecorder : public TimelineEventPlatformRecorder { |
977 | public: |
978 | TimelineEventMacosRecorder() API_AVAILABLE(ios(12.0), macos(10.14)); |
979 | virtual ~TimelineEventMacosRecorder() API_AVAILABLE(ios(12.0), macos(10.14)); |
980 | |
981 | const char* name() const { return MACOS_RECORDER_NAME; } |
982 | intptr_t Size() { return 0; } |
983 | |
984 | private: |
985 | void OnEvent(TimelineEvent* event) API_AVAILABLE(ios(12.0), macos(10.14)); |
986 | }; |
987 | #endif // defined(HOST_OS_MACOS) |
988 | |
989 | class DartTimelineEventHelpers : public AllStatic { |
990 | public: |
991 | static void ReportTaskEvent(Thread* thread, |
992 | TimelineEvent* event, |
993 | int64_t id, |
994 | const char* phase, |
995 | const char* category, |
996 | char* name, |
997 | char* args); |
998 | |
999 | static void ReportFlowEvent(Thread* thread, |
1000 | TimelineEvent* event, |
1001 | const char* category, |
1002 | char* name, |
1003 | int64_t type, |
1004 | int64_t flow_id, |
1005 | char* args); |
1006 | |
1007 | static void ReportInstantEvent(Thread* thread, |
1008 | TimelineEvent* event, |
1009 | const char* category, |
1010 | char* name, |
1011 | char* args); |
1012 | }; |
1013 | |
1014 | } // namespace dart |
1015 | |
1016 | #endif // RUNTIME_VM_TIMELINE_H_ |
1017 | |