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