#ifndef TraceLogging_h
#define TraceLogging_h
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Maybe.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/Vector.h"
#include <utility>
#include "jsapi.h"
#include "js/AllocPolicy.h"
#include "js/HashTable.h"
#include "js/TraceLoggerAPI.h"
#include "js/TypeDecls.h"
#include "js/Vector.h"
#include "threading/LockGuard.h"
#include "vm/MutexIDs.h"
#include "vm/TraceLoggingGraph.h"
#include "vm/TraceLoggingTypes.h"
namespace js {
namespace jit {
class CompileRuntime;
}
class AutoTraceLog;
class TraceLoggerEventPayload;
class TraceLoggerThread;
class TraceLoggerEvent {
#ifdef JS_TRACE_LOGGING
private:
class EventPayloadOrTextId {
uintptr_t payload_;
public:
EventPayloadOrTextId() : payload_(0) {}
bool isEventPayload() const { return (payload_ & 1) == 0; }
TraceLoggerEventPayload* eventPayload() const {
MOZ_ASSERT(isEventPayload());
return (TraceLoggerEventPayload*)payload_;
}
void setEventPayload(TraceLoggerEventPayload* payload) {
MOZ_ASSERT(payload);
payload_ = (uintptr_t)payload;
MOZ_ASSERT((payload_ & 1) == 0);
}
bool isTextId() const { return (payload_ & 1) == 1; }
uint32_t textId() const {
MOZ_ASSERT(isTextId());
return payload_ >> 1;
}
void setTextId(TraceLoggerTextId textId) {
static_assert(TraceLogger_Last < (UINT32_MAX >> 1),
"Too many predefined text ids.");
payload_ = (((uint32_t)textId) << 1) | 1;
}
};
EventPayloadOrTextId payload_;
public:
TraceLoggerEvent() : payload_() {}
explicit TraceLoggerEvent(TraceLoggerTextId textId);
TraceLoggerEvent(TraceLoggerTextId type, JSScript* script);
TraceLoggerEvent(TraceLoggerTextId type, const char* filename, uint32_t line,
uint32_t column);
explicit TraceLoggerEvent(const char* text);
TraceLoggerEvent(const TraceLoggerEvent& event);
TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
~TraceLoggerEvent();
uint32_t textId() const;
bool hasTextId() const { return hasExtPayload() || payload_.isTextId(); }
private:
TraceLoggerEventPayload* extPayload() const {
MOZ_ASSERT(hasExtPayload());
return payload_.eventPayload();
}
bool hasExtPayload() const {
return payload_.isEventPayload() && !!payload_.eventPayload();
}
#else
public:
TraceLoggerEvent() {}
explicit TraceLoggerEvent(TraceLoggerTextId textId) {}
TraceLoggerEvent(TraceLoggerTextId type, JSScript* script) {}
TraceLoggerEvent(TraceLoggerTextId type, const char* filename, uint32_t line,
uint32_t column) {}
explicit TraceLoggerEvent(const char* text) {}
TraceLoggerEvent(const TraceLoggerEvent& event) {}
TraceLoggerEvent& operator=(const TraceLoggerEvent& other) { return *this; };
~TraceLoggerEvent() {}
uint32_t textId() const { return 0; }
bool hasTextId() const { return false; }
#endif
};
#ifdef DEBUG
bool CurrentThreadOwnsTraceLoggerThreadStateLock();
#endif
class TraceLoggerEventPayload {
uint32_t textId_;
uint32_t dictionaryId_;
mozilla::Maybe<uint32_t> line_;
mozilla::Maybe<uint32_t> col_;
mozilla::Atomic<uint32_t> uses_;
public:
TraceLoggerEventPayload(uint32_t textId, uint32_t dictionaryId)
: textId_(textId),
dictionaryId_(dictionaryId),
line_(mozilla::Nothing()),
col_(mozilla::Nothing()),
uses_(0) {}
~TraceLoggerEventPayload() { MOZ_ASSERT(uses_ == 0); }
void setLine(uint32_t line) { line_ = mozilla::Some(line); }
void setColumn(uint32_t col) { col_ = mozilla::Some(col); }
mozilla::Maybe<uint32_t> line() { return line_; }
mozilla::Maybe<uint32_t> column() { return col_; }
uint32_t textId() { return textId_; }
uint32_t dictionaryId() { return dictionaryId_; }
void setDictionaryId(uint32_t dictId) { dictionaryId_ = dictId; }
uint32_t uses() { return uses_; }
void use() {
MOZ_ASSERT_IF(!uses_, CurrentThreadOwnsTraceLoggerThreadStateLock());
uses_++;
}
void release() { uses_--; }
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this);
}
};
class TraceLoggerThread : public mozilla::LinkedListElement<TraceLoggerThread> {
#ifdef JS_TRACE_LOGGING
friend JS::TraceLoggerIdImpl;
friend JS::TraceLoggerTimeStampImpl;
friend JS::TraceLoggerDurationImpl;
friend JS::TraceLoggerLineNoImpl;
friend JS::TraceLoggerColNoImpl;
private:
uint32_t enabled_;
bool failed;
UniquePtr<TraceLoggerGraph> graph;
ContinuousSpace<EventEntry> events;
uint32_t iteration_;
# ifdef DEBUG
typedef Vector<uint32_t, 1, js::SystemAllocPolicy> GraphStack;
GraphStack graphStack;
# endif
public:
AutoTraceLog* top;
TraceLoggerThread()
: enabled_(0), failed(false), graph(), iteration_(0), top(nullptr) {}
bool init();
~TraceLoggerThread();
bool init(uint32_t loggerId);
void initGraph();
void clear();
bool enable();
bool enable(JSContext* cx);
bool disable(bool force = false, const char* = "");
bool enabled() { return enabled_ > 0; }
void silentFail(const char* error);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
bool fail(JSContext* cx, const char* error);
public:
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();
}
getIterationAndSize(lastIteration, lastSize);
return start;
}
void getIterationAndSize(uint32_t* iteration, uint32_t* size) const {
*iteration = iteration_;
*size = events.size();
}
bool lostEvents(uint32_t lastIteration, uint32_t lastSize) {
if (lastIteration == iteration_) {
MOZ_ASSERT(lastSize <= events.size());
return false;
}
if (lastIteration == iteration_ - 1 && lastSize == events.maxSize()) {
return false;
}
return true;
}
private:
const char* maybeEventText(uint32_t id);
public:
const char* eventText(uint32_t id) {
const char* text = maybeEventText(id);
MOZ_ASSERT(text);
return text;
};
public:
void logTimestamp(TraceLoggerTextId id);
void startEvent(TraceLoggerTextId id);
void startEvent(const TraceLoggerEvent& event);
void stopEvent(TraceLoggerTextId id);
void stopEvent(const TraceLoggerEvent& event);
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
friend JS::TraceLoggerDictionaryImpl;
# ifdef DEBUG
bool initialized;
# endif
bool enabledTextIds[TraceLogger_Last];
bool mainThreadEnabled;
bool helperThreadEnabled;
bool graphEnabled;
bool graphFileEnabled;
bool spewErrors;
mozilla::LinkedList<TraceLoggerThread> threadLoggers;
typedef HashMap<uint32_t, TraceLoggerEventPayload*, DefaultHasher<uint32_t>,
SystemAllocPolicy>
TextIdToPayloadMap;
typedef mozilla::Vector<UniqueChars, 0, SystemAllocPolicy> DictionaryVector;
typedef HashMap<const char*, uint32_t, mozilla::CStringHasher,
SystemAllocPolicy>
StringHashToDictionaryMap;
TextIdToPayloadMap textIdPayloads;
StringHashToDictionaryMap payloadDictionary;
DictionaryVector dictionaryData;
uint32_t nextTextId;
uint32_t nextDictionaryId;
public:
mozilla::TimeStamp startTime;
double getTimeStampOffset(mozilla::TimeStamp time) {
mozilla::TimeDuration delta = time - startTime;
return delta.ToMicroseconds();
}
Mutex lock;
TraceLoggerThreadState()
:
# ifdef DEBUG
initialized(false),
# endif
mainThreadEnabled(false),
helperThreadEnabled(false),
graphEnabled(false),
graphFileEnabled(false),
spewErrors(false),
nextTextId(TraceLogger_Last),
nextDictionaryId(0),
lock(js::mutexid::TraceLoggerThreadState) {
}
bool init();
~TraceLoggerThreadState();
void enableDefaultLogging();
void enableIonLogging();
void enableFrontendLogging();
void clear();
bool remapDictionaryEntries(
mozilla::Vector<UniqueChars, 0, SystemAllocPolicy>* newDictionary,
uint32_t* newNextDictionaryId);
TraceLoggerThread* forCurrentThread(JSContext* cx);
void destroyLogger(TraceLoggerThread* logger);
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);
void maybeSpewError(const char* text) {
if (spewErrors) {
fprintf(stderr, "%s\n", text);
}
}
const char* maybeEventText(uint32_t id);
const char* maybeEventText(TraceLoggerEventPayload* p);
void purgeUnusedPayloads();
TraceLoggerEventPayload* getOrCreateEventPayload(const char* text);
TraceLoggerEventPayload* getOrCreateEventPayload(JSScript* script);
TraceLoggerEventPayload* getOrCreateEventPayload(const char* filename,
uint32_t lineno,
uint32_t colno);
TraceLoggerEventPayload* getPayload(uint32_t id);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
bool isGraphFileEnabled() { return graphFileEnabled; }
bool isGraphEnabled() { return graphEnabled; }
void enableTextIdsForProfiler();
void disableTextIdsForProfiler();
void disableAllTextIds();
#endif
};
#ifdef JS_TRACE_LOGGING
void ResetTraceLogger();
void DestroyTraceLoggerThreadState();
void DestroyTraceLogger(TraceLoggerThread* logger);
TraceLoggerThread* TraceLoggerForCurrentThread(JSContext* cx = nullptr);
#else
inline TraceLoggerThread* TraceLoggerForCurrentThread(JSContext* cx = nullptr) {
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);
}
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_TRACELOGGER_ENABLE_FAIL, "internal error");
#endif
return false;
}
inline bool TraceLoggerDisable(TraceLoggerThread* logger) {
#ifdef JS_TRACE_LOGGING
if (logger) {
return logger->disable();
}
#endif
return false;
}
inline void TraceLoggerSilentFail(TraceLoggerThread* logger,
const char* error) {
#ifdef JS_TRACE_LOGGING
if (logger) {
logger->silentFail(error);
}
#endif
}
#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
}
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
}
size_t SizeOfTraceLogState(mozilla::MallocSizeOf mallocSizeOf);
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), prev(nullptr) {
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), prev(nullptr) {
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
};
}
#endif