• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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