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