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
39 namespace 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.
44 class 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 {
typeBeginEvent125 Type type() const override { return Type::Begin; }
126 };
127 struct EndEvent : public Event {
typeEndEvent128 Type type() const override { return Type::End; }
129 };
130 struct MetadataEvent : public Event {
typeMetadataEvent131 Type type() const override { return Type::Metadata; }
132 };
133 struct Shutdown : public Event {
typeShutdown134 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 {
typeAsyncStartEvent143 Type type() const override { return Type::AsyncStart; }
144 };
145 struct AsyncEndEvent : public AsyncEvent {
typeAsyncEndEvent146 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
ScopedEvent(const char * fmt,...)174 Trace::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
~ScopedEvent()186 Trace::ScopedEvent::~ScopedEvent() {
187 if (trace != nullptr) {
188 trace->endEvent();
189 }
190 }
191
ScopedAsyncEvent(uint32_t id,const char * fmt,...)192 Trace::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
~ScopedAsyncEvent()206 Trace::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