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