diff options
Diffstat (limited to 'js/src/vm/TraceLoggingGraph.h')
-rw-r--r-- | js/src/vm/TraceLoggingGraph.h | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/js/src/vm/TraceLoggingGraph.h b/js/src/vm/TraceLoggingGraph.h new file mode 100644 index 000000000..6d596952b --- /dev/null +++ b/js/src/vm/TraceLoggingGraph.h @@ -0,0 +1,272 @@ +/* -*- 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 TraceLoggingGraph_h +#define TraceLoggingGraph_h + +#include "js/TypeDecls.h" +#include "vm/MutexIDs.h" +#include "vm/TraceLoggingTypes.h" + +/* + * 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] + */ + +namespace js { +void DestroyTraceLoggerGraphState(); +} // 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_; } +}; + +class TraceLoggerGraph +{ + // 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() + { } + 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() {} + ~TraceLoggerGraph(); + + bool init(uint64_t timestamp); + + // Link a textId with a particular text. + void addTextId(uint32_t id, const char* text); + + // Create a tree out of all the given events. + void log(ContinuousSpace<EventEntry>& events); + + static size_t treeSizeFlushLimit() { + // Allow tree size to grow to 100MB. + return 100 * 1024 * 1024 / sizeof(TreeEntry); + } + + private: + bool failed = false; + bool enabled = false; +#ifdef DEBUG + uint32_t nextTextId = 0; +#endif + + 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 */ |