| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #ifndef TraceLogging_h |
| #define TraceLogging_h |
| |
| #include "mozilla/GuardObjects.h" |
| #include "mozilla/UniquePtr.h" |
| |
| #include "jsalloc.h" |
| #include "jslock.h" |
| |
| #include "js/HashTable.h" |
| #include "js/TypeDecls.h" |
| #include "js/Vector.h" |
| #include "vm/TraceLoggingGraph.h" |
| #include "vm/TraceLoggingTypes.h" |
| |
| struct JSRuntime; |
| |
| namespace JS { |
| class ReadOnlyCompileOptions; |
| } // namespace JS |
| |
| namespace js { |
| class PerThreadData; |
| |
| namespace jit { |
| class CompileRuntime; |
| } // namespace jit |
| |
| /* |
| * Tracelogging overview. |
| * |
| * Tracelogging makes it possible to trace the occurrence of a single event and/or |
| * the start and stop of an event. This is implemented to give an as low overhead as |
| * possible so it doesn't interfere with running. |
| * |
| * |
| * Logging something is done in 3 stages. |
| * 1) Get the tracelogger of the current thread. |
| * - TraceLoggerForMainThread(JSRuntime*) |
| * - TraceLoggerForCurrentThread(); // Should NOT be used for the mainthread. |
| * |
| * 2) Optionally create a TraceLoggerEvent for the text that needs to get logged. This |
| * step takes some time, so try to do this beforehand, outside the hot |
| * path and don't do unnecessary repetitions, since it will cripple |
| * performance. |
| * - TraceLoggerEvent event(logger, "foo"); |
| * |
| * There are also some predefined events. They are located in |
| * TraceLoggerTextId. They don't require to create an TraceLoggerEvent and |
| * can also be used as an argument to these functions. |
| * 3) Log the occurrence of a single event: |
| * - TraceLogTimestamp(logger, TraceLoggerTextId); |
| * Note: it is temporarily not supported to provide an TraceLoggerEvent as |
| * argument to log the occurrence of a single event. |
| * |
| * or log the start and stop of an event: |
| * - TraceLogStartEvent(logger, TraceLoggerTextId); |
| * - TraceLogStartEvent(logger, TraceLoggerEvent); |
| * - TraceLogStopEvent(logger, TraceLoggerTextId); |
| * - TraceLogStopEvent(logger, TraceLoggerEvent); |
| * |
| * or the start/stop of an event with a RAII class: |
| * - AutoTraceLog logger(logger, TraceLoggerTextId); |
| * - AutoTraceLog logger(logger, TraceLoggerEvent); |
| */ |
| |
| class AutoTraceLog; |
| class TraceLoggerEventPayload; |
| class TraceLoggerThread; |
| |
| /** |
| * An event that can be used to report start/stop events to TraceLogger. |
| * It prepares the given info, by requesting a TraceLoggerEventPayload for the |
| * given info. (Which contains the string that needs to get reported and an |
| * unique id). It also increases the useCount of this payload, so it cannot |
| * get removed. |
| */ |
| class TraceLoggerEvent { |
| private: |
| TraceLoggerEventPayload* payload_; |
| |
| public: |
| TraceLoggerEvent() { payload_ = nullptr; }; |
| #ifdef JS_TRACE_LOGGING |
| TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId textId); |
| TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type, JSScript* script); |
| TraceLoggerEvent(TraceLoggerThread* logger, TraceLoggerTextId type, |
| const JS::ReadOnlyCompileOptions& compileOptions); |
| TraceLoggerEvent(TraceLoggerThread* logger, const char* text); |
| ~TraceLoggerEvent(); |
| #else |
| TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId textId) {} |
| TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId type, JSScript* script) {} |
| TraceLoggerEvent (TraceLoggerThread* logger, TraceLoggerTextId type, |
| const JS::ReadOnlyCompileOptions& compileOptions) {} |
| TraceLoggerEvent (TraceLoggerThread* logger, const char* text) {} |
| ~TraceLoggerEvent() {} |
| #endif |
| |
| TraceLoggerEventPayload* payload() const { |
| MOZ_ASSERT(hasPayload()); |
| return payload_; |
| } |
| bool hasPayload() const { |
| return !!payload_; |
| } |
| |
| #if defined(JS_TRACE_LOGGING) |
| TraceLoggerEvent& operator=(const TraceLoggerEvent& other); |
| #else |
| TraceLoggerEvent& operator=(const TraceLoggerEvent& other) { return *this; } |
| #endif |
| TraceLoggerEvent(const TraceLoggerEvent& event) = delete; |
| }; |
| |
| /** |
| * An internal class holding the to-report string information, together with an |
| * unique id and a useCount. Whenever this useCount reaches 0, this event |
| * cannot get started/stopped anymore. Though consumers might still request the |
| * to-report string information. |
| */ |
| class TraceLoggerEventPayload { |
| uint32_t textId_; |
| mozilla::UniquePtr<char, JS::FreePolicy> string_; |
| uint32_t uses_; |
| |
| public: |
| TraceLoggerEventPayload(uint32_t textId, char* string) |
| : textId_(textId), |
| string_(string), |
| uses_(0) |
| { } |
| |
| ~TraceLoggerEventPayload() { |
| MOZ_ASSERT(uses_ == 0); |
| } |
| |
| uint32_t textId() { |
| return textId_; |
| } |
| const char* string() { |
| return string_.get(); |
| } |
| uint32_t uses() { |
| return uses_; |
| } |
| void use() { |
| uses_++; |
| } |
| void release() { |
| uses_--; |
| } |
| }; |
| |
| class TraceLoggerThread |
| { |
| #ifdef JS_TRACE_LOGGING |
| private: |
| typedef HashMap<const void*, |
| TraceLoggerEventPayload*, |
| PointerHasher<const void*, 3>, |
| SystemAllocPolicy> PointerHashMap; |
| typedef HashMap<uint32_t, |
| TraceLoggerEventPayload*, |
| DefaultHasher<uint32_t>, |
| SystemAllocPolicy> TextIdHashMap; |
| |
| uint32_t enabled; |
| bool failed; |
| |
| mozilla::UniquePtr<TraceLoggerGraph> graph; |
| |
| PointerHashMap pointerMap; |
| TextIdHashMap textIdPayloads; |
| uint32_t nextTextId; |
| |
| ContinuousSpace<EventEntry> events; |
| |
| // Every time the events get flushed, this count is increased by one. |
| // This together with events.lastEntryId(), gives an unique position. |
| uint32_t iteration_; |
| |
| public: |
| AutoTraceLog* top; |
| |
| TraceLoggerThread() |
| : enabled(0), |
| failed(false), |
| graph(), |
| nextTextId(TraceLogger_Last), |
| iteration_(0), |
| top(nullptr) |
| { } |
| |
| bool init(); |
| ~TraceLoggerThread(); |
| |
| bool init(uint32_t loggerId); |
| void initGraph(); |
| |
| bool enable(); |
| bool enable(JSContext* cx); |
| bool disable(); |
| |
| private: |
| bool fail(JSContext* cx, const char* error); |
| |
| public: |
| // Given the previous iteration and size, return an array of events |
| // (there could be lost events). At the same time update the iteration and |
| // size and gives back how many events there are. |
| EventEntry* getEventsStartingAt(uint32_t* lastIteration, uint32_t* lastSize, size_t* num) { |
| EventEntry* start; |
| if (iteration_ == *lastIteration) { |
| MOZ_ASSERT(*lastSize <= events.size()); |
| *num = events.size() - *lastSize; |
| start = events.data() + *lastSize; |
| } else { |
| *num = events.size(); |
| start = events.data(); |
| } |
| |
| *lastIteration = iteration_; |
| *lastSize = events.size(); |
| return start; |
| } |
| |
| // Extract the details filename, lineNumber and columnNumber out of a event |
| // containing script information. |
| void extractScriptDetails(uint32_t textId, const char** filename, size_t* filename_len, |
| const char** lineno, size_t* lineno_len, const char** colno, |
| size_t* colno_len); |
| |
| bool lostEvents(uint32_t lastIteration, uint32_t lastSize) { |
| // If still logging in the same iteration, there are no lost events. |
| if (lastIteration == iteration_) { |
| MOZ_ASSERT(lastSize <= events.size()); |
| return false; |
| } |
| |
| // If we are in a consecutive iteration we are only sure we didn't lose any events, |
| // when the lastSize equals the maximum size 'events' can get. |
| if (lastIteration == iteration_ - 1 && lastSize == CONTINUOUSSPACE_LIMIT) |
| return false; |
| |
| return true; |
| } |
| |
| const char* eventText(uint32_t id); |
| bool textIdIsScriptEvent(uint32_t id); |
| |
| // The createTextId functions map a unique input to a logger ID. |
| // This can be used to give start and stop events. Calls to these functions should be |
| // limited if possible, because of the overhead. |
| // Note: it is not allowed to use them in logTimestamp. |
| TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId textId); |
| TraceLoggerEventPayload* getOrCreateEventPayload(const char* text); |
| TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type, JSScript* script); |
| TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type, |
| const JS::ReadOnlyCompileOptions& script); |
| private: |
| TraceLoggerEventPayload* getOrCreateEventPayload(TraceLoggerTextId type, const char* filename, |
| size_t lineno, size_t colno, const void* p); |
| |
| public: |
| // Log an event (no start/stop, only the timestamp is recorded). |
| void logTimestamp(TraceLoggerTextId id); |
| |
| // Record timestamps for start and stop of an event. |
| void startEvent(TraceLoggerTextId id); |
| void startEvent(const TraceLoggerEvent& event); |
| void stopEvent(TraceLoggerTextId id); |
| void stopEvent(const TraceLoggerEvent& event); |
| |
| // These functions are actually private and shouldn't be used in normal |
| // code. They are made public so they can get used in assembly. |
| void logTimestamp(uint32_t id); |
| void startEvent(uint32_t id); |
| void stopEvent(uint32_t id); |
| private: |
| void stopEvent(); |
| void log(uint32_t id); |
| |
| public: |
| static unsigned offsetOfEnabled() { |
| return offsetof(TraceLoggerThread, enabled); |
| } |
| #endif |
| }; |
| |
| class TraceLoggerThreadState |
| { |
| #ifdef JS_TRACE_LOGGING |
| typedef HashMap<PRThread*, |
| TraceLoggerThread*, |
| PointerHasher<PRThread*, 3>, |
| SystemAllocPolicy> ThreadLoggerHashMap; |
| typedef Vector<TraceLoggerThread*, 1, js::SystemAllocPolicy > MainThreadLoggers; |
| |
| #ifdef DEBUG |
| bool initialized; |
| #endif |
| |
| bool enabledTextIds[TraceLogger_Last]; |
| bool mainThreadEnabled; |
| bool offThreadEnabled; |
| bool graphSpewingEnabled; |
| ThreadLoggerHashMap threadLoggers; |
| MainThreadLoggers mainThreadLoggers; |
| |
| public: |
| uint64_t startupTime; |
| PRLock* lock; |
| |
| TraceLoggerThreadState() |
| : |
| #ifdef DEBUG |
| initialized(false), |
| #endif |
| mainThreadEnabled(false), |
| offThreadEnabled(false), |
| graphSpewingEnabled(false), |
| lock(nullptr) |
| { } |
| |
| bool init(); |
| ~TraceLoggerThreadState(); |
| |
| TraceLoggerThread* forMainThread(JSRuntime* runtime); |
| TraceLoggerThread* forMainThread(jit::CompileRuntime* runtime); |
| TraceLoggerThread* forThread(PRThread* thread); |
| |
| bool isTextIdEnabled(uint32_t textId) { |
| if (textId < TraceLogger_Last) |
| return enabledTextIds[textId]; |
| return true; |
| } |
| void enableTextId(JSContext* cx, uint32_t textId); |
| void disableTextId(JSContext* cx, uint32_t textId); |
| |
| private: |
| TraceLoggerThread* forMainThread(PerThreadData* mainThread); |
| TraceLoggerThread* create(); |
| #endif |
| }; |
| |
| #ifdef JS_TRACE_LOGGING |
| void DestroyTraceLoggerThreadState(); |
| |
| TraceLoggerThread* TraceLoggerForMainThread(JSRuntime* runtime); |
| TraceLoggerThread* TraceLoggerForMainThread(jit::CompileRuntime* runtime); |
| TraceLoggerThread* TraceLoggerForCurrentThread(); |
| #else |
| inline TraceLoggerThread* TraceLoggerForMainThread(JSRuntime* runtime) { |
| return nullptr; |
| }; |
| inline TraceLoggerThread* TraceLoggerForMainThread(jit::CompileRuntime* runtime) { |
| return nullptr; |
| }; |
| inline TraceLoggerThread* TraceLoggerForCurrentThread() { |
| return nullptr; |
| }; |
| #endif |
| |
| inline bool TraceLoggerEnable(TraceLoggerThread* logger) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| return logger->enable(); |
| #endif |
| return false; |
| } |
| inline bool TraceLoggerEnable(TraceLoggerThread* logger, JSContext* cx) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| return logger->enable(cx); |
| #endif |
| return false; |
| } |
| inline bool TraceLoggerDisable(TraceLoggerThread* logger) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| return logger->disable(); |
| #endif |
| return false; |
| } |
| |
| #ifdef JS_TRACE_LOGGING |
| bool TraceLogTextIdEnabled(uint32_t textId); |
| void TraceLogEnableTextId(JSContext* cx, uint32_t textId); |
| void TraceLogDisableTextId(JSContext* cx, uint32_t textId); |
| #else |
| inline bool TraceLogTextIdEnabled(uint32_t textId) { |
| return false; |
| } |
| inline void TraceLogEnableTextId(JSContext* cx, uint32_t textId) {} |
| inline void TraceLogDisableTextId(JSContext* cx, uint32_t textId) {} |
| #endif |
| inline void TraceLogTimestamp(TraceLoggerThread* logger, TraceLoggerTextId textId) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->logTimestamp(textId); |
| #endif |
| } |
| inline void TraceLogStartEvent(TraceLoggerThread* logger, TraceLoggerTextId textId) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->startEvent(textId); |
| #endif |
| } |
| inline void TraceLogStartEvent(TraceLoggerThread* logger, const TraceLoggerEvent& event) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->startEvent(event); |
| #endif |
| } |
| inline void TraceLogStopEvent(TraceLoggerThread* logger, TraceLoggerTextId textId) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->stopEvent(textId); |
| #endif |
| } |
| inline void TraceLogStopEvent(TraceLoggerThread* logger, const TraceLoggerEvent& event) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->stopEvent(event); |
| #endif |
| } |
| |
| // Helper functions for assembly. May not be used otherwise. |
| inline void TraceLogTimestampPrivate(TraceLoggerThread* logger, uint32_t id) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->logTimestamp(id); |
| #endif |
| } |
| inline void TraceLogStartEventPrivate(TraceLoggerThread* logger, uint32_t id) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->startEvent(id); |
| #endif |
| } |
| inline void TraceLogStopEventPrivate(TraceLoggerThread* logger, uint32_t id) { |
| #ifdef JS_TRACE_LOGGING |
| if (logger) |
| logger->stopEvent(id); |
| #endif |
| } |
| |
| // Automatic logging at the start and end of function call. |
| class MOZ_RAII AutoTraceLog |
| { |
| #ifdef JS_TRACE_LOGGING |
| TraceLoggerThread* logger; |
| union { |
| const TraceLoggerEvent* event; |
| TraceLoggerTextId id; |
| } payload; |
| bool isEvent; |
| bool executed; |
| AutoTraceLog* prev; |
| |
| public: |
| AutoTraceLog(TraceLoggerThread* logger, |
| const TraceLoggerEvent& event MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : logger(logger), |
| isEvent(true), |
| executed(false) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| payload.event = &event; |
| if (logger) { |
| logger->startEvent(event); |
| |
| prev = logger->top; |
| logger->top = this; |
| } |
| } |
| |
| AutoTraceLog(TraceLoggerThread* logger, TraceLoggerTextId id MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| : logger(logger), |
| isEvent(false), |
| executed(false) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| payload.id = id; |
| if (logger) { |
| logger->startEvent(id); |
| |
| prev = logger->top; |
| logger->top = this; |
| } |
| } |
| |
| ~AutoTraceLog() |
| { |
| if (logger) { |
| while (this != logger->top) |
| logger->top->stop(); |
| stop(); |
| } |
| } |
| private: |
| void stop() { |
| if (!executed) { |
| executed = true; |
| if (isEvent) |
| logger->stopEvent(*payload.event); |
| else |
| logger->stopEvent(payload.id); |
| } |
| |
| if (logger->top == this) |
| logger->top = prev; |
| } |
| #else |
| public: |
| AutoTraceLog(TraceLoggerThread* logger, uint32_t textId MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| AutoTraceLog(TraceLoggerThread* logger, |
| const TraceLoggerEvent& event MOZ_GUARD_OBJECT_NOTIFIER_PARAM) |
| { |
| MOZ_GUARD_OBJECT_NOTIFIER_INIT; |
| } |
| #endif |
| |
| private: |
| MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER |
| }; |
| |
| } // namespace js |
| |
| #endif /* TraceLogging_h */ |