1// Copyright 2019 The Marl Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// The Trace API produces a trace event file that can be consumed with Chrome's
16// chrome://tracing viewer.
17// Documentation can be found at:
18// https://www.chromium.org/developers/how-tos/trace-event-profiling-tool
19// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
20
21#ifndef marl_trace_h
22#define marl_trace_h
23
24#define MARL_TRACE_ENABLED 0
25
26#if MARL_TRACE_ENABLED
27
28#include <array>
29#include <atomic>
30#include <chrono>
31#include <condition_variable>
32#include <cstdarg>
33#include <cstring>
34#include <mutex>
35#include <ostream>
36#include <queue>
37#include <thread>
38
39namespace marl {
40
41// Trace writes a trace event file into the current working directory that can
42// be consumed with Chrome's chrome://tracing viewer.
43// Use the MARL_* macros below instead of using this class directly.
44class Trace {
45 public:
46 static constexpr size_t MaxEventNameLength = 64;
47
48 static Trace* get();
49
50 void nameThread(const char* fmt, ...);
51 void beginEvent(const char* fmt, ...);
52 void endEvent();
53 void beginAsyncEvent(uint32_t id, const char* fmt, ...);
54 void endAsyncEvent(uint32_t id, const char* fmt, ...);
55
56 class ScopedEvent {
57 public:
58 inline ScopedEvent(const char* fmt, ...);
59 inline ~ScopedEvent();
60
61 private:
62 Trace* const trace;
63 };
64
65 class ScopedAsyncEvent {
66 public:
67 inline ScopedAsyncEvent(uint32_t id, const char* fmt, ...);
68 inline ~ScopedAsyncEvent();
69
70 private:
71 Trace* const trace;
72 const uint32_t id;
73 std::string name;
74 };
75
76 private:
77 Trace();
78 ~Trace();
79 Trace(const Trace&) = delete;
80 Trace& operator=(const Trace&) = delete;
81
82 struct Event {
83 enum class Type : uint8_t {
84 Begin = 'B',
85 End = 'E',
86 Complete = 'X',
87 Instant = 'i',
88 Counter = 'C',
89 AsyncStart = 'b',
90 AsyncInstant = 'n',
91 AsyncEnd = 'e',
92 FlowStart = 's',
93 FlowStep = 't',
94 FlowEnd = 'f',
95 Sample = 'P',
96 ObjectCreated = 'N',
97 ObjectSnapshot = 'O',
98 ObjectDestroyed = 'D',
99 Metadata = 'M',
100 GlobalMemoryDump = 'V',
101 ProcessMemoryDump = 'v',
102 Mark = 'R',
103 ClockSync = 'c',
104 ContextEnter = '(',
105 ContextLeave = ')',
106
107 // Internal types
108 Shutdown = 'S',
109 };
110
111 Event();
112 virtual ~Event() = default;
113 virtual Type type() const = 0;
114 virtual void write(std::ostream& out) const;
115
116 char name[MaxEventNameLength] = {};
117 const char** categories = nullptr;
118 uint64_t timestamp = 0; // in microseconds
119 uint32_t processID = 0;
120 uint32_t threadID;
121 uint32_t fiberID;
122 };
123
124 struct BeginEvent : public Event {
125 Type type() const override { return Type::Begin; }
126 };
127 struct EndEvent : public Event {
128 Type type() const override { return Type::End; }
129 };
130 struct MetadataEvent : public Event {
131 Type type() const override { return Type::Metadata; }
132 };
133 struct Shutdown : public Event {
134 Type type() const override { return Type::Shutdown; }
135 };
136
137 struct AsyncEvent : public Event {
138 void write(std::ostream& out) const override;
139 uint32_t id;
140 };
141
142 struct AsyncStartEvent : public AsyncEvent {
143 Type type() const override { return Type::AsyncStart; }
144 };
145 struct AsyncEndEvent : public AsyncEvent {
146 Type type() const override { return Type::AsyncEnd; }
147 };
148
149 struct NameThreadEvent : public MetadataEvent {
150 void write(std::ostream& out) const override;
151 };
152
153 uint64_t timestamp(); // in microseconds
154
155 void put(Event*);
156 std::unique_ptr<Event> take();
157
158 struct EventQueue {
159 std::queue<std::unique_ptr<Event> > data; // guarded by mutes
160 std::condition_variable condition;
161 std::mutex mutex;
162 };
163 // TODO: Increasing this from 1 can cause events to go out of order.
164 // Investigate, fix.
165 std::array<EventQueue, 1> eventQueues;
166 std::atomic<unsigned int> eventQueueWriteIdx = {0};
167 unsigned int eventQueueReadIdx = 0;
168 std::chrono::time_point<std::chrono::high_resolution_clock> createdAt =
169 std::chrono::high_resolution_clock::now();
170 std::thread thread;
171 std::atomic<bool> stopped = {false};
172};
173
174Trace::ScopedEvent::ScopedEvent(const char* fmt, ...) : trace(Trace::get()) {
175 if (trace != nullptr) {
176 char name[Trace::MaxEventNameLength];
177 va_list vararg;
178 va_start(vararg, fmt);
179 vsnprintf(name, Trace::MaxEventNameLength, fmt, vararg);
180 va_end(vararg);
181
182 trace->beginEvent(name);
183 }
184}
185
186Trace::ScopedEvent::~ScopedEvent() {
187 if (trace != nullptr) {
188 trace->endEvent();
189 }
190}
191
192Trace::ScopedAsyncEvent::ScopedAsyncEvent(uint32_t id, const char* fmt, ...)
193 : trace(Trace::get()), id(id) {
194 if (trace != nullptr) {
195 char buf[Trace::MaxEventNameLength];
196 va_list vararg;
197 va_start(vararg, fmt);
198 vsnprintf(buf, Trace::MaxEventNameLength, fmt, vararg);
199 va_end(vararg);
200 name = buf;
201
202 trace->beginAsyncEvent(id, "%s", buf);
203 }
204}
205
206Trace::ScopedAsyncEvent::~ScopedAsyncEvent() {
207 if (trace != nullptr) {
208 trace->endAsyncEvent(id, "%s", name.c_str());
209 }
210}
211
212} // namespace marl
213
214#define MARL_CONCAT_(a, b) a##b
215#define MARL_CONCAT(a, b) MARL_CONCAT_(a, b)
216#define MARL_SCOPED_EVENT(...) \
217 marl::Trace::ScopedEvent MARL_CONCAT(scoped_event, __LINE__)(__VA_ARGS__);
218#define MARL_BEGIN_ASYNC_EVENT(id, ...) \
219 do { \
220 if (auto t = marl::Trace::get()) { \
221 t->beginAsyncEvent(id, __VA_ARGS__); \
222 } \
223 } while (false);
224#define MARL_END_ASYNC_EVENT(id, ...) \
225 do { \
226 if (auto t = marl::Trace::get()) { \
227 t->endAsyncEvent(id, __VA_ARGS__); \
228 } \
229 } while (false);
230#define MARL_SCOPED_ASYNC_EVENT(id, ...) \
231 marl::Trace::ScopedAsyncEvent MARL_CONCAT(defer_, __LINE__)(id, __VA_ARGS__);
232#define MARL_NAME_THREAD(...) \
233 do { \
234 if (auto t = marl::Trace::get()) { \
235 t->nameThread(__VA_ARGS__); \
236 } \
237 } while (false);
238
239#else // MARL_TRACE_ENABLED
240
241#define MARL_SCOPED_EVENT(...)
242#define MARL_BEGIN_ASYNC_EVENT(id, ...)
243#define MARL_END_ASYNC_EVENT(id, ...)
244#define MARL_SCOPED_ASYNC_EVENT(id, ...)
245#define MARL_NAME_THREAD(...)
246
247#endif // MARL_TRACE_ENABLED
248
249#endif // marl_trace_h
250