1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- 2 * vim: set ts=8 sts=4 et sw=4 tw=99: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef TraceLoggingGraph_h 8 #define TraceLoggingGraph_h 9 10 #include "mozilla/MemoryReporting.h" 11 12 #include "js/TypeDecls.h" 13 #include "vm/MutexIDs.h" 14 #include "vm/TraceLoggingTypes.h" 15 16 // clang-format off 17 /* 18 * The output of a tracelogging session is saved in /tmp/tl-data.json. 19 * The format of that file is a JS array per tracelogger (=thread), with a map 20 * containing: 21 * - dict: Name of the file containing a json table with the log text. 22 * All other files only contain a index to this table when logging. 23 * - events: Name of the file containing a flat list of log events saved 24 * in binary format. 25 * (64bit: Time Stamp Counter, 32bit index to dict) 26 * - tree: Name of the file containing the events with duration. The content 27 * is already in a tree data structure. This is also saved in a 28 * binary file. 29 * - treeFormat: The format used to encode the tree. By default "64,64,31,1,32". 30 * There are currently no other formats to save the tree. 31 * - 64,64,31,1,32 signifies how many bytes are used for the different 32 * parts of the tree. 33 * => 64 bits: Time Stamp Counter of start of event. 34 * => 64 bits: Time Stamp Counter of end of event. 35 * => 31 bits: Index to dict file containing the log text. 36 * => 1 bit: Boolean signifying if this entry has children. 37 * When true, the child can be found just right after this 38 * entry. 39 * => 32 bits: Containing the ID of the next event on the same depth 40 * or 0 if there isn't an event on the same depth anymore. 41 * 42 * /-> The position in the file. Id is this divided by size of entry. 43 * | So in this case this would be 1 (192bits per entry). 44 * | /-> Indicates there are children. The 45 * | | first child is located at current 46 * | | ID + 1. So 1 + 1 in this case: 2. 47 * | | Or 0x00180 in the tree file. 48 * | | /-> Next event on the same depth is 49 * | | | located at 4. So 0x00300 in the 50 * | | | tree file. 51 * 0x0000C0: [start, end, dictId, 1, 4] 52 * 53 * 54 * Example: 55 * 0x0: [start, end, dictId, 1, 0] 56 * | 57 * /----------------------------------\ 58 * | | 59 * 0xC0: [start, end, dictId, 0, 2] 0x180 [start, end, dictId, 1, 0] 60 * | 61 * /----------------------------------\ 62 * | | 63 * 0x240: [start, end, dictId, 0, 4] 0x300 [start, end, dictId, 0, 0] 64 */ 65 // clang-format on 66 67 namespace js { 68 void DestroyTraceLoggerGraphState(); 69 size_t SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf); 70 } // namespace js 71 72 class TraceLoggerGraphState { 73 uint32_t numLoggers; 74 uint32_t pid_; 75 76 // File pointer to the "tl-data.json" file. (Explained above). 77 FILE* out; 78 79 #ifdef DEBUG 80 bool initialized; 81 #endif 82 83 public: 84 js::Mutex lock; 85 86 public: TraceLoggerGraphState()87 TraceLoggerGraphState() 88 : numLoggers(0), 89 pid_(0), 90 out(nullptr) 91 #ifdef DEBUG 92 , 93 initialized(false) 94 #endif 95 , 96 lock(js::mutexid::TraceLoggerGraphState) { 97 } 98 99 bool init(); 100 ~TraceLoggerGraphState(); 101 102 uint32_t nextLoggerId(); pid()103 uint32_t pid() { return pid_; } 104 105 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)106 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { 107 return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf); 108 } 109 }; 110 111 class TraceLoggerGraph { 112 // The layout of the tree in memory and in the log file. Readable by JS 113 // using TypedArrays. 114 struct TreeEntry { 115 uint64_t start_; 116 uint64_t stop_; 117 union { 118 struct { 119 uint32_t textId_ : 31; 120 uint32_t hasChildren_ : 1; 121 } s; 122 uint32_t value_; 123 } u; 124 uint32_t nextId_; 125 TreeEntryTreeEntry126 TreeEntry(uint64_t start, uint64_t stop, uint32_t textId, bool hasChildren, 127 uint32_t nextId) { 128 start_ = start; 129 stop_ = stop; 130 u.s.textId_ = textId; 131 u.s.hasChildren_ = hasChildren; 132 nextId_ = nextId; 133 } TreeEntryTreeEntry134 TreeEntry() {} startTreeEntry135 uint64_t start() { return start_; } stopTreeEntry136 uint64_t stop() { return stop_; } textIdTreeEntry137 uint32_t textId() { return u.s.textId_; } hasChildrenTreeEntry138 bool hasChildren() { return u.s.hasChildren_; } nextIdTreeEntry139 uint32_t nextId() { return nextId_; } setStartTreeEntry140 void setStart(uint64_t start) { start_ = start; } setStopTreeEntry141 void setStop(uint64_t stop) { stop_ = stop; } setTextIdTreeEntry142 void setTextId(uint32_t textId) { 143 MOZ_ASSERT(textId < uint32_t(1 << 31)); 144 u.s.textId_ = textId; 145 } setHasChildrenTreeEntry146 void setHasChildren(bool hasChildren) { u.s.hasChildren_ = hasChildren; } setNextIdTreeEntry147 void setNextId(uint32_t nextId) { nextId_ = nextId; } 148 }; 149 150 // Helper structure for keeping track of the current entries in 151 // the tree. Pushed by `start(id)`, popped by `stop(id)`. The active flag 152 // is used to know if a subtree doesn't need to get logged. 153 struct StackEntry { 154 uint32_t treeId_; 155 uint32_t lastChildId_; 156 struct { 157 uint32_t textId_ : 31; 158 uint32_t active_ : 1; 159 } s; 160 StackEntry(uint32_t treeId, uint32_t lastChildId, bool active = true) treeId_StackEntry161 : treeId_(treeId), lastChildId_(lastChildId) { 162 s.textId_ = 0; 163 s.active_ = active; 164 } treeIdStackEntry165 uint32_t treeId() { return treeId_; } lastChildIdStackEntry166 uint32_t lastChildId() { return lastChildId_; } textIdStackEntry167 uint32_t textId() { return s.textId_; } activeStackEntry168 bool active() { return s.active_; } setTreeIdStackEntry169 void setTreeId(uint32_t treeId) { treeId_ = treeId; } setLastChildIdStackEntry170 void setLastChildId(uint32_t lastChildId) { lastChildId_ = lastChildId; } setTextIdStackEntry171 void setTextId(uint32_t textId) { 172 MOZ_ASSERT(textId < uint32_t(1 << 31)); 173 s.textId_ = textId; 174 } setActiveStackEntry175 void setActive(bool active) { s.active_ = active; } 176 }; 177 178 public: TraceLoggerGraph()179 TraceLoggerGraph() {} 180 ~TraceLoggerGraph(); 181 182 bool init(uint64_t timestamp); 183 184 // Link a textId with a particular text. 185 void addTextId(uint32_t id, const char* text); 186 187 // Create a tree out of all the given events. 188 void log(ContinuousSpace<EventEntry>& events); 189 treeSizeFlushLimit()190 static size_t treeSizeFlushLimit() { 191 // Allow tree size to grow to 100MB. 192 return 100 * 1024 * 1024 / sizeof(TreeEntry); 193 } 194 nextTextId()195 uint32_t nextTextId() { return nextTextId_; } 196 197 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 198 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const; 199 200 private: 201 bool failed = false; 202 bool enabled = false; 203 uint32_t nextTextId_ = 0; 204 205 FILE* dictFile = nullptr; 206 FILE* treeFile = nullptr; 207 FILE* eventFile = nullptr; 208 209 ContinuousSpace<TreeEntry> tree; 210 ContinuousSpace<StackEntry> stack; 211 uint32_t treeOffset = 0; 212 213 // Helper functions that convert a TreeEntry in different endianness 214 // in place. 215 void entryToBigEndian(TreeEntry* entry); 216 void entryToSystemEndian(TreeEntry* entry); 217 218 // Helper functions to get/save a tree from file. 219 bool getTreeEntry(uint32_t treeId, TreeEntry* entry); 220 bool saveTreeEntry(uint32_t treeId, TreeEntry* entry); 221 222 // Return the first StackEntry that is active. 223 StackEntry& getActiveAncestor(); 224 225 // This contains the meat of startEvent, except the test for enough space, 226 // the test if tracelogger is enabled and the timestamp computation. 227 void startEvent(uint32_t id, uint64_t timestamp); 228 bool startEventInternal(uint32_t id, uint64_t timestamp); 229 230 // Update functions that can adjust the items in the tree, 231 // both in memory or already written to disk. 232 bool updateHasChildren(uint32_t treeId, bool hasChildren = true); 233 bool updateNextId(uint32_t treeId, uint32_t nextId); 234 bool updateStop(uint32_t treeId, uint64_t timestamp); 235 236 // Flush the tree. 237 bool flush(); 238 239 // Stop a tree event. 240 void stopEvent(uint32_t id, uint64_t timestamp); 241 void stopEvent(uint64_t timestamp); 242 243 // Log an (non-tree) event. 244 void logTimestamp(uint32_t id, uint64_t timestamp); 245 246 // Disable logging and forcefully report all not yet stopped tree events 247 // as stopped. 248 void disable(uint64_t timestamp); 249 }; 250 251 #endif /* TraceLoggingGraph_h */ 252