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