1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #ifndef TraceLogging_h
8 #define TraceLogging_h
9
10 #include "mozilla/GuardObjects.h"
11 #include "mozilla/LinkedList.h"
12
13 #include "jsalloc.h"
14
15 #include "js/HashTable.h"
16 #include "js/TypeDecls.h"
17 #include "js/Vector.h"
18 #include "threading/Thread.h"
19 #include "vm/MutexIDs.h"
20 #include "vm/TraceLoggingGraph.h"
21 #include "vm/TraceLoggingTypes.h"
22
23 struct JSRuntime;
24
25 namespace JS {
26 class ReadOnlyCompileOptions;
27 } // namespace JS
28
29 namespace js {
30 class PerThreadData;
31
32 namespace jit {
33 class CompileRuntime;
34 } // namespace jit
35
36 /*
37 * Tracelogging overview.
38 *
39 * Tracelogging makes it possible to trace the occurrence of a single event
40 * and/or the start and stop of an event. This is implemented with as low
41 * overhead as possible to not interfere with running.
42 *
43 * Logging something is done in 3 stages.
44 * 1) Get the tracelogger of the current thread.
45 * - TraceLoggerForMainThread(JSRuntime*)
46 * - TraceLoggerForCurrentThread(); // Should NOT be used for the mainthread.
47 *
48 * 2) Optionally create a TraceLoggerEvent for the text that needs to get logged. This
49 * step takes some time, so try to do this beforehand, outside the hot
50 * path and don't do unnecessary repetitions, since it will cripple
51 * performance.
52 * - TraceLoggerEvent event(logger, "foo");
53 *
54 * There are also some predefined events. They are located in
55 * TraceLoggerTextId. They don't require to create an TraceLoggerEvent and
56 * can also be used as an argument to these functions.
57 *
58 * 3) Log the occurrence of a single event:
59 * - TraceLogTimestamp(logger, TraceLoggerTextId);
60 * Note: it is temporarily not supported to provide an TraceLoggerEvent as
61 * argument to log the occurrence of a single event.
62 *
63 * or log the start and stop of an event:
64 * - TraceLogStartEvent(logger, TraceLoggerTextId);
65 * - TraceLogStartEvent(logger, TraceLoggerEvent);
66 * - TraceLogStopEvent(logger, TraceLoggerTextId);
67 * - TraceLogStopEvent(logger, TraceLoggerEvent);
68 *
69 * or the start/stop of an event with a RAII class:
70 * - AutoTraceLog atl(logger, TraceLoggerTextId);
71 * - AutoTraceLog atl(logger, TraceLoggerEvent);
72 */
73
74 class AutoTraceLog;
75 class TraceLoggerEventPayload;
76 class TraceLoggerThread;
77
78 /**
79 * An event that can be used to report start/stop events to TraceLogger. It
80 * prepares the given info by requesting a TraceLoggerEventPayload containing
81 * the string to report and an unique id. It also increases the useCount of
82 * this payload, so it cannot get removed.
83 */
84 class TraceLoggerEvent {
85 private:
86 TraceLoggerEventPayload* payload_;
87
88 public:
TraceLoggerEvent()89 TraceLoggerEvent() { payload_ = nullptr; };
90 #ifdef JS_TRACE_LOGGING
91 TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId textId);
92 TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type, JSScript* script);
93 TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type,
94 const JS::ReadOnlyCompileOptions& compileOptions);
95 TraceLoggerEvent(TraceLoggerThread* logger, const char* text);
96 TraceLoggerEvent(const TraceLoggerEvent& event);
97 TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
98 ~TraceLoggerEvent();
99 #else
TraceLoggerEvent(TraceLoggerThread * logger,TraceLoggerTextId textId)100 TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId textId) {}
TraceLoggerEvent(TraceLoggerThread * logger,TraceLoggerTextId type,JSScript * script)101 TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId type, JSScript* script) {}
TraceLoggerEvent(TraceLoggerThread * logger,TraceLoggerTextId type,const JS::ReadOnlyCompileOptions & compileOptions)102 TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId type,
103 const JS::ReadOnlyCompileOptions& compileOptions) {}
TraceLoggerEvent(TraceLoggerThread * logger,const char * text)104 TraceLoggerEvent (TraceLoggerThread* logger, const char* text) {}
TraceLoggerEvent(const TraceLoggerEvent & event)105 TraceLoggerEvent(const TraceLoggerEvent& event) {}
106 TraceLoggerEvent& operator=(const TraceLoggerEvent& other) {};
~TraceLoggerEvent()107 ~TraceLoggerEvent() {}
108 #endif
109
payload()110 TraceLoggerEventPayload* payload() const {
111 MOZ_ASSERT(hasPayload());
112 return payload_;
113 }
hasPayload()114 bool hasPayload() const {
115 return !!payload_;
116 }
117 };
118
119 /**
120 * An internal class holding the string information to report, together with an
121 * unique id and a useCount. Whenever this useCount reaches 0, this event
122 * cannot get started/stopped anymore. Consumers may still request the
123 * string information.
124 */
125 class TraceLoggerEventPayload {
126 uint32_t textId_;
127 UniqueChars string_;
128 uint32_t uses_;
129
130 public:
TraceLoggerEventPayload(uint32_t textId,char * string)131 TraceLoggerEventPayload(uint32_t textId, char* string)
132 : textId_(textId),
133 string_(string),
134 uses_(0)
135 { }
136
~TraceLoggerEventPayload()137 ~TraceLoggerEventPayload() {
138 MOZ_ASSERT(uses_ == 0);
139 }
140
textId()141 uint32_t textId() {
142 return textId_;
143 }
string()144 const char* string() {
145 return string_.get();
146 }
uses()147 uint32_t uses() {
148 return uses_;
149 }
use()150 void use() {
151 uses_++;
152 }
release()153 void release() {
154 uses_--;
155 }
156 };
157
158 class TraceLoggerThread
159 {
160 #ifdef JS_TRACE_LOGGING
161 private:
162 typedef HashMap<const void*,
163 TraceLoggerEventPayload*,
164 PointerHasher<const void*, 3>,
165 SystemAllocPolicy> PointerHashMap;
166 typedef HashMap<uint32_t,
167 TraceLoggerEventPayload*,
168 DefaultHasher<uint32_t>,
169 SystemAllocPolicy> TextIdHashMap;
170
171 uint32_t enabled_;
172 bool failed;
173
174 UniquePtr<TraceLoggerGraph> graph;
175
176 PointerHashMap pointerMap;
177 TextIdHashMap textIdPayloads;
178 uint32_t nextTextId;
179
180 ContinuousSpace<EventEntry> events;
181
182 // Every time the events get flushed, this count is increased by one.
183 // Together with events.lastEntryId(), this gives an unique id for every
184 // event.
185 uint32_t iteration_;
186
187 #ifdef DEBUG
188 typedef Vector<uint32_t, 1, js::SystemAllocPolicy > GraphStack;
189 GraphStack graphStack;
190 #endif
191
192 public:
193 AutoTraceLog* top;
194
TraceLoggerThread()195 TraceLoggerThread()
196 : enabled_(0),
197 failed(false),
198 graph(),
199 nextTextId(TraceLogger_Last),
200 iteration_(0),
201 top(nullptr)
202 { }
203
204 bool init();
205 ~TraceLoggerThread();
206
207 bool init(uint32_t loggerId);
208 void initGraph();
209
210 bool enable();
211 bool enable(JSContext* cx);
212 bool disable(bool force = false, const char* = "");
enabled()213 bool enabled() { return enabled_ > 0; }
214
215 private:
216 bool fail(JSContext* cx, const char* error);
217
218 public:
219 // Given the previous iteration and size, return an array of events
220 // (there could be lost events). At the same time update the iteration and
221 // size and gives back how many events there are.
getEventsStartingAt(uint32_t * lastIteration,uint32_t * lastSize,size_t * num)222 EventEntry* getEventsStartingAt(uint32_t* lastIteration, uint32_t* lastSize, size_t* num) {
223 EventEntry* start;
224 if (iteration_ == *lastIteration) {
225 MOZ_ASSERT(*lastSize <= events.size());
226 *num = events.size() - *lastSize;
227 start = events.data() + *lastSize;
228 } else {
229 *num = events.size();
230 start = events.data();
231 }
232
233 getIterationAndSize(lastIteration, lastSize);
234 return start;
235 }
236
getIterationAndSize(uint32_t * iteration,uint32_t * size)237 void getIterationAndSize(uint32_t* iteration, uint32_t* size) const {
238 *iteration = iteration_;
239 *size = events.size();
240 }
241
242 // Extract the details filename, lineNumber and columnNumber out of a event
243 // containing script information.
244 void extractScriptDetails(uint32_t textId, const char** filename, size_t* filename_len,
245 const char** lineno, size_t* lineno_len, const char** colno,
246 size_t* colno_len);
247
lostEvents(uint32_t lastIteration,uint32_t lastSize)248 bool lostEvents(uint32_t lastIteration, uint32_t lastSize) {
249 // If still logging in the same iteration, there are no lost events.
250 if (lastIteration == iteration_) {
251 MOZ_ASSERT(lastSize <= events.size());
252 return false;
253 }
254
255 // If we are in the next consecutive iteration we are only sure we
256 // didn't lose any events when the lastSize equals the maximum size
257 // 'events' can get.
258 if (lastIteration == iteration_ - 1 && lastSize == events.maxSize())
259 return false;
260
261 return true;
262 }
263
264 const char* eventText(uint32_t id);
265 bool textIdIsScriptEvent(uint32_t id);
266
267 // The createTextId functions map a unique input to a logger ID.
268 // This can be used to give start and stop events. Calls to these functions should be
269 // limited if possible, because of the overhead.
270 // Note: it is not allowed to use them in logTimestamp.
271 TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId textId);
272 TraceLoggerEventPayload* getOrCreateEventPayload(const char* text);
273 TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type, JSScript* script);
274 TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type,
275 const JS::ReadOnlyCompileOptions& script);
276 private:
277 TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type, const char* filename,
278 size_t lineno, size_t colno, const void* p);
279
280 public:
281 // Log an event (no start/stop, only the timestamp is recorded).
282 void logTimestamp(TraceLoggerTextId id);
283
284 // Record timestamps for start and stop of an event.
285 void startEvent(TraceLoggerTextId id);
286 void startEvent(const TraceLoggerEvent& event);
287 void stopEvent(TraceLoggerTextId id);
288 void stopEvent(const TraceLoggerEvent& event);
289
290 // These functions are actually private and shouldn't be used in normal
291 // code. They are made public so they can be used in assembly.
292 void logTimestamp(uint32_t id);
293 void startEvent(uint32_t id);
294 void stopEvent(uint32_t id);
295 private:
296 void stopEvent();
297 void log(uint32_t id);
298
299 public:
offsetOfEnabled()300 static unsigned offsetOfEnabled() {
301 return offsetof(TraceLoggerThread, enabled_);
302 }
303 #endif
304 };
305
306 #ifdef JS_TRACE_LOGGING
307 class TraceLoggerMainThread
308 : public TraceLoggerThread,
309 public mozilla::LinkedListElement<TraceLoggerMainThread>
310 {
311
312 };
313 #endif
314
315 class TraceLoggerThreadState
316 {
317 #ifdef JS_TRACE_LOGGING
318 typedef HashMap<Thread::Id,
319 TraceLoggerThread*,
320 Thread::Hasher,
321 SystemAllocPolicy> ThreadLoggerHashMap;
322
323 #ifdef DEBUG
324 bool initialized;
325 #endif
326
327 bool enabledTextIds[TraceLogger_Last];
328 bool mainThreadEnabled;
329 bool offThreadEnabled;
330 bool graphSpewingEnabled;
331 bool spewErrors;
332 ThreadLoggerHashMap threadLoggers;
333 mozilla::LinkedList<TraceLoggerMainThread> traceLoggerMainThreadList;
334
335 public:
336 uint64_t startupTime;
337 Mutex lock;
338
TraceLoggerThreadState()339 TraceLoggerThreadState()
340 :
341 #ifdef DEBUG
342 initialized(false),
343 #endif
344 mainThreadEnabled(false),
345 offThreadEnabled(false),
346 graphSpewingEnabled(false),
347 spewErrors(false),
348 lock(js::mutexid::TraceLoggerThreadState)
349 { }
350
351 bool init();
352 ~TraceLoggerThreadState();
353
354 TraceLoggerThread* forMainThread(JSRuntime* runtime);
355 TraceLoggerThread* forMainThread(jit::CompileRuntime* runtime);
356 TraceLoggerThread* forThread(const Thread::Id& thread);
357 void destroyMainThread(JSRuntime* runtime);
358
isTextIdEnabled(uint32_t textId)359 bool isTextIdEnabled(uint32_t textId) {
360 if (textId < TraceLogger_Last)
361 return enabledTextIds[textId];
362 return true;
363 }
364 void enableTextId(JSContext* cx, uint32_t textId);
365 void disableTextId(JSContext* cx, uint32_t textId);
maybeSpewError(const char * text)366 void maybeSpewError(const char* text) {
367 if (spewErrors)
368 fprintf(stderr, "%s\n", text);
369 }
370
371 private:
372 TraceLoggerThread* forMainThread(PerThreadData* mainThread);
373 #endif
374 };
375
376 #ifdef JS_TRACE_LOGGING
377 void DestroyTraceLoggerThreadState();
378 void DestroyTraceLoggerMainThread(JSRuntime* runtime);
379
380 TraceLoggerThread* TraceLoggerForMainThread(JSRuntime* runtime);
381 TraceLoggerThread* TraceLoggerForMainThread(jit::CompileRuntime* runtime);
382 TraceLoggerThread* TraceLoggerForCurrentThread();
383 #else
TraceLoggerForMainThread(JSRuntime * runtime)384 inline TraceLoggerThread* TraceLoggerForMainThread(JSRuntime* runtime) {
385 return nullptr;
386 };
TraceLoggerForMainThread(jit::CompileRuntime * runtime)387 inline TraceLoggerThread* TraceLoggerForMainThread(jit::CompileRuntime* runtime) {
388 return nullptr;
389 };
TraceLoggerForCurrentThread()390 inline TraceLoggerThread* TraceLoggerForCurrentThread() {
391 return nullptr;
392 };
393 #endif
394
TraceLoggerEnable(TraceLoggerThread * logger)395 inline bool TraceLoggerEnable(TraceLoggerThread* logger) {
396 #ifdef JS_TRACE_LOGGING
397 if (logger)
398 return logger->enable();
399 #endif
400 return false;
401 }
TraceLoggerEnable(TraceLoggerThread * logger,JSContext * cx)402 inline bool TraceLoggerEnable(TraceLoggerThread* logger, JSContext* cx) {
403 #ifdef JS_TRACE_LOGGING
404 if (logger)
405 return logger->enable(cx);
406 #endif
407 return false;
408 }
TraceLoggerDisable(TraceLoggerThread * logger)409 inline bool TraceLoggerDisable(TraceLoggerThread* logger) {
410 #ifdef JS_TRACE_LOGGING
411 if (logger)
412 return logger->disable();
413 #endif
414 return false;
415 }
416
417 #ifdef JS_TRACE_LOGGING
418 bool TraceLogTextIdEnabled(uint32_t textId);
419 void TraceLogEnableTextId(JSContext* cx, uint32_t textId);
420 void TraceLogDisableTextId(JSContext* cx, uint32_t textId);
421 #else
TraceLogTextIdEnabled(uint32_t textId)422 inline bool TraceLogTextIdEnabled(uint32_t textId) {
423 return false;
424 }
TraceLogEnableTextId(JSContext * cx,uint32_t textId)425 inline void TraceLogEnableTextId(JSContext* cx, uint32_t textId) {}
TraceLogDisableTextId(JSContext * cx,uint32_t textId)426 inline void TraceLogDisableTextId(JSContext* cx, uint32_t textId) {}
427 #endif
TraceLogTimestamp(TraceLoggerThread * logger,TraceLoggerTextId textId)428 inline void TraceLogTimestamp(TraceLoggerThread* logger, TraceLoggerTextId textId) {
429 #ifdef JS_TRACE_LOGGING
430 if (logger)
431 logger->logTimestamp(textId);
432 #endif
433 }
TraceLogStartEvent(TraceLoggerThread * logger,TraceLoggerTextId textId)434 inline void TraceLogStartEvent(TraceLoggerThread* logger, TraceLoggerTextId textId) {
435 #ifdef JS_TRACE_LOGGING
436 if (logger)
437 logger->startEvent(textId);
438 #endif
439 }
TraceLogStartEvent(TraceLoggerThread * logger,const TraceLoggerEvent & event)440 inline void TraceLogStartEvent(TraceLoggerThread* logger, const TraceLoggerEvent& event) {
441 #ifdef JS_TRACE_LOGGING
442 if (logger)
443 logger->startEvent(event);
444 #endif
445 }
TraceLogStopEvent(TraceLoggerThread * logger,TraceLoggerTextId textId)446 inline void TraceLogStopEvent(TraceLoggerThread* logger, TraceLoggerTextId textId) {
447 #ifdef JS_TRACE_LOGGING
448 if (logger)
449 logger->stopEvent(textId);
450 #endif
451 }
TraceLogStopEvent(TraceLoggerThread * logger,const TraceLoggerEvent & event)452 inline void TraceLogStopEvent(TraceLoggerThread* logger, const TraceLoggerEvent& event) {
453 #ifdef JS_TRACE_LOGGING
454 if (logger)
455 logger->stopEvent(event);
456 #endif
457 }
458
459 // Helper functions for assembly. May not be used otherwise.
TraceLogTimestampPrivate(TraceLoggerThread * logger,uint32_t id)460 inline void TraceLogTimestampPrivate(TraceLoggerThread* logger, uint32_t id) {
461 #ifdef JS_TRACE_LOGGING
462 if (logger)
463 logger->logTimestamp(id);
464 #endif
465 }
TraceLogStartEventPrivate(TraceLoggerThread * logger,uint32_t id)466 inline void TraceLogStartEventPrivate(TraceLoggerThread* logger, uint32_t id) {
467 #ifdef JS_TRACE_LOGGING
468 if (logger)
469 logger->startEvent(id);
470 #endif
471 }
TraceLogStopEventPrivate(TraceLoggerThread * logger,uint32_t id)472 inline void TraceLogStopEventPrivate(TraceLoggerThread* logger, uint32_t id) {
473 #ifdef JS_TRACE_LOGGING
474 if (logger)
475 logger->stopEvent(id);
476 #endif
477 }
478
479 // Automatic logging at the start and end of function call.
480 class MOZ_RAII AutoTraceLog
481 {
482 #ifdef JS_TRACE_LOGGING
483 TraceLoggerThread* logger;
484 union {
485 const TraceLoggerEvent* event;
486 TraceLoggerTextId id;
487 } payload;
488 bool isEvent;
489 bool executed;
490 AutoTraceLog* prev;
491
492 public:
AutoTraceLog(TraceLoggerThread * logger,const TraceLoggerEvent & event MOZ_GUARD_OBJECT_NOTIFIER_PARAM)493 AutoTraceLog(TraceLoggerThread* logger,
494 const TraceLoggerEvent& event MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
495 : logger(logger),
496 isEvent(true),
497 executed(false)
498 {
499 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
500 payload.event = &event;
501 if (logger) {
502 logger->startEvent(event);
503
504 prev = logger->top;
505 logger->top = this;
506 }
507 }
508
AutoTraceLog(TraceLoggerThread * logger,TraceLoggerTextId id MOZ_GUARD_OBJECT_NOTIFIER_PARAM)509 AutoTraceLog(TraceLoggerThread* logger, TraceLoggerTextId id MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
510 : logger(logger),
511 isEvent(false),
512 executed(false)
513 {
514 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
515 payload.id = id;
516 if (logger) {
517 logger->startEvent(id);
518
519 prev = logger->top;
520 logger->top = this;
521 }
522 }
523
~AutoTraceLog()524 ~AutoTraceLog()
525 {
526 if (logger) {
527 while (this != logger->top)
528 logger->top->stop();
529 stop();
530 }
531 }
532 private:
stop()533 void stop() {
534 if (!executed) {
535 executed = true;
536 if (isEvent)
537 logger->stopEvent(*payload.event);
538 else
539 logger->stopEvent(payload.id);
540 }
541
542 if (logger->top == this)
543 logger->top = prev;
544 }
545 #else
546 public:
547 AutoTraceLog(TraceLoggerThread* logger, uint32_t textId MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
548 {
549 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
550 }
551 AutoTraceLog(TraceLoggerThread* logger,
552 const TraceLoggerEvent& event MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
553 {
554 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
555 }
556 #endif
557
558 private:
559 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
560 };
561
562 } // namespace js
563
564 #endif /* TraceLogging_h */
565