Source code

Revision control

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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 TraceLoggingGraph_h
#define TraceLoggingGraph_h
#include "mozilla/MemoryReporting.h"
#include "mozilla/TimeStamp.h"
#include "js/TypeDecls.h"
#include "vm/MutexIDs.h"
#include "vm/TraceLoggingTypes.h"
// clang-format off
/*
* The output of a tracelogging session is saved in /tmp/tl-data.json.
* The format of that file is a JS array per tracelogger (=thread), with a map
* containing:
* - dict: Name of the file containing a json table with the log text.
* All other files only contain a index to this table when logging.
* - events: Name of the file containing a flat list of log events saved
* in binary format.
* (64bit: Time Stamp Counter, 32bit index to dict)
* - tree: Name of the file containing the events with duration. The content
* is already in a tree data structure. This is also saved in a
* binary file.
* - treeFormat: The format used to encode the tree. By default "64,64,31,1,32".
* There are currently no other formats to save the tree.
* - 64,64,31,1,32 signifies how many bytes are used for the different
* parts of the tree.
* => 64 bits: Time Stamp Counter of start of event.
* => 64 bits: Time Stamp Counter of end of event.
* => 31 bits: Index to dict file containing the log text.
* => 1 bit: Boolean signifying if this entry has children.
* When true, the child can be found just right after this
* entry.
* => 32 bits: Containing the ID of the next event on the same depth
* or 0 if there isn't an event on the same depth anymore.
*
* /-> The position in the file. Id is this divided by size of entry.
* | So in this case this would be 1 (192bits per entry).
* | /-> Indicates there are children. The
* | | first child is located at current
* | | ID + 1. So 1 + 1 in this case: 2.
* | | Or 0x00180 in the tree file.
* | | /-> Next event on the same depth is
* | | | located at 4. So 0x00300 in the
* | | | tree file.
* 0x0000C0: [start, end, dictId, 1, 4]
*
*
* Example:
* 0x0: [start, end, dictId, 1, 0]
* |
* /----------------------------------\
* | |
* 0xC0: [start, end, dictId, 0, 2] 0x180 [start, end, dictId, 1, 0]
* |
* /----------------------------------\
* | |
* 0x240: [start, end, dictId, 0, 4] 0x300 [start, end, dictId, 0, 0]
*/
// clang-format on
namespace js {
void DestroyTraceLoggerGraphState();
size_t SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf);
} // namespace js
class TraceLoggerGraphState {
uint32_t numLoggers;
uint32_t pid_;
// File pointer to the "tl-data.json" file. (Explained above).
FILE* out;
#ifdef DEBUG
bool initialized;
#endif
public:
js::Mutex lock;
public:
TraceLoggerGraphState()
: numLoggers(0),
pid_(0),
out(nullptr)
#ifdef DEBUG
,
initialized(false)
#endif
,
lock(js::mutexid::TraceLoggerGraphState) {
}
bool init();
~TraceLoggerGraphState();
uint32_t nextLoggerId();
uint32_t pid() { return pid_; }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
};
namespace js {
class TraceLoggerThread;
} // namespace js
class TraceLoggerGraph {
// This is needed so that we can write the data to the JSON writer from the TL
// thread class.
friend class js::TraceLoggerThread;
private:
// The layout of the tree in memory and in the log file. Readable by JS
// using TypedArrays.
struct TreeEntry {
uint64_t start_;
uint64_t stop_;
union {
struct {
uint32_t textId_ : 31;
uint32_t hasChildren_ : 1;
} s;
uint32_t value_;
} u;
uint32_t nextId_;
TreeEntry(uint64_t start, uint64_t stop, uint32_t textId, bool hasChildren,
uint32_t nextId) {
start_ = start;
stop_ = stop;
u.s.textId_ = textId;
u.s.hasChildren_ = hasChildren;
nextId_ = nextId;
}
TreeEntry() : start_(0), stop_(0), u{}, nextId_(0) {}
uint64_t start() { return start_; }
uint64_t stop() { return stop_; }
uint32_t textId() { return u.s.textId_; }
bool hasChildren() { return u.s.hasChildren_; }
uint32_t nextId() { return nextId_; }
void setStart(uint64_t start) { start_ = start; }
void setStop(uint64_t stop) { stop_ = stop; }
void setTextId(uint32_t textId) {
MOZ_ASSERT(textId < uint32_t(1 << 31));
u.s.textId_ = textId;
}
void setHasChildren(bool hasChildren) { u.s.hasChildren_ = hasChildren; }
void setNextId(uint32_t nextId) { nextId_ = nextId; }
};
// Helper structure for keeping track of the current entries in
// the tree. Pushed by `start(id)`, popped by `stop(id)`. The active flag
// is used to know if a subtree doesn't need to get logged.
struct StackEntry {
uint32_t treeId_;
uint32_t lastChildId_;
struct {
uint32_t textId_ : 31;
uint32_t active_ : 1;
} s;
StackEntry(uint32_t treeId, uint32_t lastChildId, bool active = true)
: treeId_(treeId), lastChildId_(lastChildId) {
s.textId_ = 0;
s.active_ = active;
}
uint32_t treeId() { return treeId_; }
uint32_t lastChildId() { return lastChildId_; }
uint32_t textId() { return s.textId_; }
bool active() { return s.active_; }
void setTreeId(uint32_t treeId) { treeId_ = treeId; }
void setLastChildId(uint32_t lastChildId) { lastChildId_ = lastChildId; }
void setTextId(uint32_t textId) {
MOZ_ASSERT(textId < uint32_t(1 << 31));
s.textId_ = textId;
}
void setActive(bool active) { s.active_ = active; }
};
public:
TraceLoggerGraph() = default;
~TraceLoggerGraph();
bool init(uint64_t timestamp, bool graphFileEnabled);
// Link a textId with a particular text.
void addTextId(uint32_t id, const char* text);
void addTextId(uint32_t id, const char* text, mozilla::Maybe<uint32_t>& line,
mozilla::Maybe<uint32_t>& column);
// Create a tree out of all the given events.
void log(ContinuousSpace<EventEntry>& events, mozilla::TimeStamp startTime);
static size_t treeSizeFlushLimit() {
// Allow tree size to grow to 100MB.
return 100 * 1024 * 1024 / sizeof(TreeEntry);
}
uint32_t nextTextId() { return nextTextId_; }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
bool failed = false;
bool enabled = false;
uint32_t nextTextId_ = 0;
FILE* dictFile = nullptr;
FILE* treeFile = nullptr;
FILE* eventFile = nullptr;
ContinuousSpace<TreeEntry> tree;
ContinuousSpace<StackEntry> stack;
uint32_t treeOffset = 0;
// Helper functions that convert a TreeEntry in different endianness
// in place.
void entryToBigEndian(TreeEntry* entry);
void entryToSystemEndian(TreeEntry* entry);
// Helper functions to get/save a tree from file.
bool getTreeEntry(uint32_t treeId, TreeEntry* entry);
bool saveTreeEntry(uint32_t treeId, TreeEntry* entry);
// Return the first StackEntry that is active.
StackEntry& getActiveAncestor();
// This contains the meat of startEvent, except the test for enough space,
// the test if tracelogger is enabled and the timestamp computation.
void startEvent(uint32_t id, uint64_t timestamp);
bool startEventInternal(uint32_t id, uint64_t timestamp);
// Update functions that can adjust the items in the tree,
// both in memory or already written to disk.
bool updateHasChildren(uint32_t treeId, bool hasChildren = true);
bool updateNextId(uint32_t treeId, uint32_t nextId);
bool updateStop(uint32_t treeId, uint64_t timestamp);
// Flush the tree.
bool flush();
// Stop a tree event.
void stopEvent(uint32_t id, uint64_t timestamp);
void stopEvent(uint64_t timestamp);
// Log an (non-tree) event.
void logTimestamp(uint32_t id, uint64_t timestamp);
// Disable logging and forcefully report all not yet stopped tree events
// as stopped.
void disable(uint64_t timestamp);
};
#endif /* TraceLoggingGraph_h */