blob: d09609c99396c52e8ceed9f2b98c2cba2feb2b0a [file] [log] [blame]
/* -*- 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 */