TraceLogging.h
/* -*- 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/LinkedList.h"
#include "jsalloc.h"
#include "js/HashTable.h"
#include "js/TypeDecls.h"
#include "js/Vector.h"
#include "threading/Thread.h"
#include "vm/MutexIDs.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 with as low
* overhead as possible to not 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 atl(logger, TraceLoggerTextId);
* - AutoTraceLog atl(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 containing
* the string to report 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(const TraceLoggerEvent& event);
TraceLoggerEvent& operator=(const TraceLoggerEvent& other);
~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(const TraceLoggerEvent& event) {}
TraceLoggerEvent& operator=(const TraceLoggerEvent& other) {};
~TraceLoggerEvent() {}
#endif
TraceLoggerEventPayload* payload() const {
MOZ_ASSERT(hasPayload());
return payload_;
}
bool hasPayload() const {
return !!payload_;
}
};
/**
* An internal class holding the string information to report, together with an
* unique id and a useCount. Whenever this useCount reaches 0, this event
* cannot get started/stopped anymore. Consumers may still request the
* string information.
*/
class TraceLoggerEventPayload {
uint32_t textId_;
UniqueChars 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;
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.
// Together with events.lastEntryId(), this gives an unique id for every
// event.
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(),
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(bool force = false, const char* = "");
bool enabled() { return enabled_ > 0; }
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();
}
getIterationAndSize(lastIteration, lastSize);
return start;
}
void getIterationAndSize(uint32_t* iteration, uint32_t* size) const {
*iteration = iteration_;
*size = events.size();
}
// 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 the next 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 == events.maxSize())
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 be 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
};
#ifdef JS_TRACE_LOGGING
class TraceLoggerMainThread
: public TraceLoggerThread,
public mozilla::LinkedListElement<TraceLoggerMainThread>
{
};
#endif
class TraceLoggerThreadState
{
#ifdef JS_TRACE_LOGGING
typedef HashMap<Thread::Id,
TraceLoggerThread*,
Thread::Hasher,
SystemAllocPolicy> ThreadLoggerHashMap;
#ifdef DEBUG
bool initialized;
#endif
bool enabledTextIds[TraceLogger_Last];
bool mainThreadEnabled;
bool offThreadEnabled;
bool graphSpewingEnabled;
bool spewErrors;
ThreadLoggerHashMap threadLoggers;
mozilla::LinkedList<TraceLoggerMainThread> traceLoggerMainThreadList;
public:
uint64_t startupTime;
Mutex lock;
TraceLoggerThreadState()
:
#ifdef DEBUG
initialized(false),
#endif
mainThreadEnabled(false),
offThreadEnabled(false),
graphSpewingEnabled(false),
spewErrors(false),
lock(js::mutexid::TraceLoggerThreadState)
{ }
bool init();
~TraceLoggerThreadState();
TraceLoggerThread* forMainThread(JSRuntime* runtime);
TraceLoggerThread* forMainThread(jit::CompileRuntime* runtime);
TraceLoggerThread* forThread(const Thread::Id& thread);
void destroyMainThread(JSRuntime* runtime);
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);
}
private:
TraceLoggerThread* forMainThread(PerThreadData* mainThread);
#endif
};
#ifdef JS_TRACE_LOGGING
void DestroyTraceLoggerThreadState();
void DestroyTraceLoggerMainThread(JSRuntime* runtime);
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 */