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 #include "vm/TraceLoggingGraph.h"
8 
9 #ifdef XP_WIN
10 #  include <process.h>
11 #  define getpid _getpid
12 #else
13 #  include <unistd.h>
14 #endif
15 
16 #include "mozilla/EndianUtils.h"
17 #include "mozilla/MemoryReporting.h"
18 #include "mozilla/ScopeExit.h"
19 
20 #include "js/Printf.h"
21 #include "js/UniquePtr.h"
22 #include "threading/LockGuard.h"
23 #include "threading/Thread.h"
24 #include "util/Text.h"
25 #include "vm/TraceLogging.h"
26 
27 #ifndef DEFAULT_TRACE_LOG_DIR
28 #  if defined(_WIN32)
29 #    define DEFAULT_TRACE_LOG_DIR "."
30 #  else
31 #    define DEFAULT_TRACE_LOG_DIR "/tmp/"
32 #  endif
33 #endif
34 
35 using mozilla::MakeScopeExit;
36 using mozilla::NativeEndian;
37 
38 TraceLoggerGraphState* traceLoggerGraphState = nullptr;
39 
40 // gcc and clang have these in symcat.h, but MSVC does not.
41 #ifndef STRINGX
42 #  define STRINGX(x) #  x
43 #endif
44 #ifndef XSTRING
45 #  define XSTRING(macro) STRINGX(macro)
46 #endif
47 
48 #define MAX_LOGGERS 999
49 
50 // Return a filename relative to the output directory. %u and %d substitutions
51 // are allowed, with %u standing for a full 32-bit number and %d standing for
52 // an up to 3-digit number.
53 static js::UniqueChars MOZ_FORMAT_PRINTF(1, 2)
AllocTraceLogFilename(const char * pattern,...)54     AllocTraceLogFilename(const char* pattern, ...) {
55   va_list ap;
56 
57   const char* outdir =
58       getenv("TLDIR") ? getenv("TLDIR") : DEFAULT_TRACE_LOG_DIR;
59   size_t len = strlen(outdir) + 1;  // "+ 1" is for the '/'
60 
61   for (const char* p = pattern; *p; p++) {
62     if (*p == '%') {
63       p++;
64       if (*p == 'u') {
65         len += sizeof("4294967295") - 1;
66       } else if (*p == 'd') {
67         len += sizeof(XSTRING(MAX_LOGGERS)) - 1;
68       } else {
69         MOZ_CRASH("Invalid format");
70       }
71     } else {
72       len++;
73     }
74   }
75 
76   len++;  // For the terminating NUL.
77 
78   js::UniqueChars filename(js_pod_malloc<char>(len));
79   if (!filename) {
80     return nullptr;
81   }
82   char* rest = filename.get() + sprintf(filename.get(), "%s/", outdir);
83 
84   va_start(ap, pattern);
85   int ret = vsnprintf(rest, len, pattern, ap);
86   va_end(ap);
87   if (ret < 0) {
88     return nullptr;
89   }
90 
91   MOZ_ASSERT(size_t(ret) <= len - (strlen(outdir) + 1),
92              "overran TL filename buffer; %d given too large a value?");
93 
94   return filename;
95 }
96 
init()97 bool TraceLoggerGraphState::init() {
98   pid_ = (uint32_t)getpid();
99 
100   js::UniqueChars filename = AllocTraceLogFilename("tl-data.%u.json", pid_);
101   out = fopen(filename.get(), "w");
102   if (!out) {
103     fprintf(stderr, "warning: failed to create TraceLogger output file %s\n",
104             filename.get());
105     return false;
106   }
107 
108   fprintf(out, "[");
109 
110   // Write the latest tl-data.*.json file to tl-data.json.
111   // In most cases that is the wanted file.
112   js::UniqueChars masterFilename = AllocTraceLogFilename("tl-data.json");
113   if (FILE* last = fopen(masterFilename.get(), "w")) {
114     char* basename = strrchr(filename.get(), '/');
115     basename = basename ? basename + 1 : filename.get();
116     fprintf(last, "\"%s\"", basename);
117     fclose(last);
118   }
119 
120 #ifdef DEBUG
121   initialized = true;
122 #endif
123   return true;
124 }
125 
~TraceLoggerGraphState()126 TraceLoggerGraphState::~TraceLoggerGraphState() {
127   if (out) {
128     fprintf(out, "]");
129     fclose(out);
130     out = nullptr;
131   }
132 
133 #ifdef DEBUG
134   initialized = false;
135 #endif
136 }
137 
nextLoggerId()138 uint32_t TraceLoggerGraphState::nextLoggerId() {
139   js::LockGuard<js::Mutex> guard(lock);
140 
141   MOZ_ASSERT(initialized);
142 
143   if (numLoggers > MAX_LOGGERS) {
144     fputs("TraceLogging: Can't create more than " XSTRING(
145               MAX_LOGGERS) " different loggers.",
146           stderr);
147     return uint32_t(-1);
148   }
149 
150   if (numLoggers > 0) {
151     int written = fprintf(out, ",\n");
152     if (written < 0) {
153       fprintf(stderr, "TraceLogging: Error while writing.\n");
154       return uint32_t(-1);
155     }
156   }
157 
158   int written = fprintf(
159       out,
160       "{\"tree\":\"tl-tree.%u.%u.tl\", \"events\":\"tl-event.%u.%u.tl\", "
161       "\"dict\":\"tl-dict.%u.%u.json\", \"treeFormat\":\"64,64,31,1,32\"",
162       pid_, numLoggers, pid_, numLoggers, pid_, numLoggers);
163 
164   if (written > 0) {
165     char threadName[16];
166     js::ThisThread::GetName(threadName, sizeof(threadName));
167     if (threadName[0]) {
168       written = fprintf(out, R"(, "threadName":"%s")", threadName);
169     }
170   }
171 
172   if (written > 0) {
173     written = fprintf(out, "}");
174   }
175 
176   if (written < 0) {
177     fprintf(stderr, "TraceLogging: Error while writing.\n");
178     return uint32_t(-1);
179   }
180 
181   return numLoggers++;
182 }
183 
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const184 size_t TraceLoggerGraphState::sizeOfExcludingThis(
185     mozilla::MallocSizeOf mallocSizeOf) const {
186   return 0;
187 }
188 
EnsureTraceLoggerGraphState()189 static bool EnsureTraceLoggerGraphState() {
190   if (MOZ_LIKELY(traceLoggerGraphState)) {
191     return true;
192   }
193 
194   traceLoggerGraphState = js_new<TraceLoggerGraphState>();
195   if (!traceLoggerGraphState) {
196     return false;
197   }
198 
199   if (!traceLoggerGraphState->init()) {
200     js::DestroyTraceLoggerGraphState();
201     return false;
202   }
203 
204   return true;
205 }
206 
SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf)207 size_t js::SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf) {
208   return traceLoggerGraphState
209              ? traceLoggerGraphState->sizeOfIncludingThis(mallocSizeOf)
210              : 0;
211 }
212 
DestroyTraceLoggerGraphState()213 void js::DestroyTraceLoggerGraphState() {
214   if (traceLoggerGraphState) {
215     js_delete(traceLoggerGraphState);
216     traceLoggerGraphState = nullptr;
217   }
218 }
219 
init(uint64_t startTimestamp,bool graphFileEnabled)220 bool TraceLoggerGraph::init(uint64_t startTimestamp, bool graphFileEnabled) {
221   auto fail = MakeScopeExit([&] { failed = true; });
222 
223   if (graphFileEnabled) {
224     if (!EnsureTraceLoggerGraphState()) {
225       return false;
226     }
227 
228     uint32_t loggerId = traceLoggerGraphState->nextLoggerId();
229     if (loggerId == uint32_t(-1)) {
230       return false;
231     }
232 
233     uint32_t pid = traceLoggerGraphState->pid();
234 
235     js::UniqueChars dictFilename =
236         AllocTraceLogFilename("tl-dict.%u.%u.json", pid, loggerId);
237     dictFile = fopen(dictFilename.get(), "w");
238     if (!dictFile) {
239       return false;
240     }
241     auto cleanupDict = MakeScopeExit([&] {
242       fclose(dictFile);
243       dictFile = nullptr;
244     });
245 
246     js::UniqueChars treeFilename =
247         AllocTraceLogFilename("tl-tree.%u.%u.tl", pid, loggerId);
248     treeFile = fopen(treeFilename.get(), "w+b");
249     if (!treeFile) {
250       return false;
251     }
252     auto cleanupTree = MakeScopeExit([&] {
253       fclose(treeFile);
254       treeFile = nullptr;
255     });
256 
257     js::UniqueChars eventFilename =
258         AllocTraceLogFilename("tl-event.%u.%u.tl", pid, loggerId);
259     eventFile = fopen(eventFilename.get(), "wb");
260     if (!eventFile) {
261       return false;
262     }
263     auto cleanupEvent = MakeScopeExit([&] {
264       fclose(eventFile);
265       eventFile = nullptr;
266     });
267 
268     if (fprintf(dictFile, "[") < 0) {
269       fprintf(stderr, "TraceLogging: Error while writing.\n");
270       return false;
271     }
272 
273     cleanupDict.release();
274     cleanupTree.release();
275     cleanupEvent.release();
276   }
277 
278   if (!tree.init()) {
279     return false;
280   }
281   if (!stack.init()) {
282     return false;
283   }
284 
285   // Create the top tree node and corresponding first stack item.
286   TreeEntry& treeEntry = tree.pushUninitialized();
287   treeEntry.setStart(startTimestamp);
288   treeEntry.setStop(0);
289   treeEntry.setTextId(0);
290   treeEntry.setHasChildren(false);
291   treeEntry.setNextId(0);
292 
293   StackEntry& stackEntry = stack.pushUninitialized();
294   stackEntry.setTreeId(0);
295   stackEntry.setLastChildId(0);
296   stackEntry.setActive(true);
297 
298   fail.release();
299 
300   return true;
301 }
302 
~TraceLoggerGraph()303 TraceLoggerGraph::~TraceLoggerGraph() {
304   // Write dictionary to disk
305   if (dictFile) {
306     int written = fprintf(dictFile, "]");
307     if (written < 0) {
308       fprintf(stderr, "TraceLogging: Error while writing.\n");
309     }
310     fclose(dictFile);
311 
312     dictFile = nullptr;
313   }
314 
315   if (!failed && treeFile) {
316     // Make sure every start entry has a corresponding stop value.
317     // We temporarily enable logging for this. Stop doesn't need any extra data,
318     // so is safe to do even when we have encountered OOM.
319     enabled = true;
320     while (stack.size() > 1) {
321       stopEvent(0);
322     }
323     enabled = false;
324   }
325 
326   if (!failed && !flush()) {
327     fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
328     enabled = false;
329     failed = true;
330   }
331 
332   if (treeFile) {
333     fclose(treeFile);
334     treeFile = nullptr;
335   }
336 
337   if (eventFile) {
338     fclose(eventFile);
339     eventFile = nullptr;
340   }
341 }
342 
flush()343 bool TraceLoggerGraph::flush() {
344   MOZ_ASSERT(!failed);
345 
346   if (treeFile) {
347     // Format data in big endian.
348     for (size_t i = 0; i < tree.size(); i++) {
349       entryToBigEndian(&tree[i]);
350     }
351 
352     int success = fseek(treeFile, 0, SEEK_END);
353     if (success != 0) {
354       return false;
355     }
356 
357     size_t bytesWritten =
358         fwrite(tree.data(), sizeof(TreeEntry), tree.size(), treeFile);
359     if (bytesWritten < tree.size()) {
360       return false;
361     }
362 
363     treeOffset += tree.size();
364     tree.clear();
365   }
366 
367   return true;
368 }
369 
entryToBigEndian(TreeEntry * entry)370 void TraceLoggerGraph::entryToBigEndian(TreeEntry* entry) {
371   entry->start_ = NativeEndian::swapToBigEndian(entry->start_);
372   entry->stop_ = NativeEndian::swapToBigEndian(entry->stop_);
373   uint32_t data = (entry->u.s.textId_ << 1) + entry->u.s.hasChildren_;
374   entry->u.value_ = NativeEndian::swapToBigEndian(data);
375   entry->nextId_ = NativeEndian::swapToBigEndian(entry->nextId_);
376 }
377 
entryToSystemEndian(TreeEntry * entry)378 void TraceLoggerGraph::entryToSystemEndian(TreeEntry* entry) {
379   entry->start_ = NativeEndian::swapFromBigEndian(entry->start_);
380   entry->stop_ = NativeEndian::swapFromBigEndian(entry->stop_);
381 
382   uint32_t data = NativeEndian::swapFromBigEndian(entry->u.value_);
383   entry->u.s.textId_ = data >> 1;
384   entry->u.s.hasChildren_ = data & 0x1;
385 
386   entry->nextId_ = NativeEndian::swapFromBigEndian(entry->nextId_);
387 }
388 
startEvent(uint32_t id,uint64_t timestamp)389 void TraceLoggerGraph::startEvent(uint32_t id, uint64_t timestamp) {
390   if (failed || enabled == 0) {
391     return;
392   }
393 
394   if (!tree.hasSpaceForAdd()) {
395     if (tree.size() >= treeSizeFlushLimit() || !tree.ensureSpaceBeforeAdd()) {
396       if (!treeFile) {
397         disable(timestamp);
398         return;
399       }
400 
401       if (!flush()) {
402         fprintf(stderr, "TraceLogging: Couldn't write the data to disk.\n");
403         enabled = false;
404         failed = true;
405         return;
406       }
407     }
408   }
409 
410   if (!startEventInternal(id, timestamp)) {
411     fprintf(stderr, "TraceLogging: Failed to start an event.\n");
412     enabled = false;
413     failed = true;
414     return;
415   }
416 }
417 
getActiveAncestor()418 TraceLoggerGraph::StackEntry& TraceLoggerGraph::getActiveAncestor() {
419   uint32_t parentId = stack.lastEntryId();
420   while (!stack[parentId].active()) {
421     parentId--;
422   }
423   return stack[parentId];
424 }
425 
startEventInternal(uint32_t id,uint64_t timestamp)426 bool TraceLoggerGraph::startEventInternal(uint32_t id, uint64_t timestamp) {
427   if (!stack.ensureSpaceBeforeAdd()) {
428     return false;
429   }
430 
431   // Patch up the tree to be correct. There are two scenarios:
432   // 1) Parent has no children yet. So update parent to include children.
433   // 2) Parent has already children. Update last child to link to the new
434   //    child.
435   StackEntry& parent = getActiveAncestor();
436 #ifdef DEBUG
437   TreeEntry entry;
438   if (!getTreeEntry(parent.treeId(), &entry)) {
439     return false;
440   }
441 #endif
442 
443   if (parent.lastChildId() == 0) {
444     MOZ_ASSERT(!entry.hasChildren());
445     MOZ_ASSERT(parent.treeId() == treeOffset + tree.size() - 1);
446 
447     if (!updateHasChildren(parent.treeId())) {
448       return false;
449     }
450   } else {
451     MOZ_ASSERT(entry.hasChildren());
452 
453     if (!updateNextId(parent.lastChildId(), tree.size() + treeOffset)) {
454       return false;
455     }
456   }
457 
458   // Add a new tree entry.
459   TreeEntry& treeEntry = tree.pushUninitialized();
460   treeEntry.setStart(timestamp);
461   treeEntry.setStop(0);
462   treeEntry.setTextId(id);
463   treeEntry.setHasChildren(false);
464   treeEntry.setNextId(0);
465 
466   // Add a new stack entry.
467   StackEntry& stackEntry = stack.pushUninitialized();
468   stackEntry.setTreeId(tree.lastEntryId() + treeOffset);
469   stackEntry.setLastChildId(0);
470   stackEntry.setActive(true);
471 
472   // Set the last child of the parent to this newly added entry.
473   parent.setLastChildId(tree.lastEntryId() + treeOffset);
474 
475   return true;
476 }
477 
stopEvent(uint32_t id,uint64_t timestamp)478 void TraceLoggerGraph::stopEvent(uint32_t id, uint64_t timestamp) {
479 #ifdef DEBUG
480   if (id != TraceLogger_Scripts && id != TraceLogger_Engine &&
481       stack.size() > 1 && stack.lastEntry().active()) {
482     TreeEntry entry;
483     MOZ_ASSERT(getTreeEntry(stack.lastEntry().treeId(), &entry));
484     MOZ_ASSERT(entry.textId() == id);
485   }
486 #endif
487 
488   stopEvent(timestamp);
489 }
490 
stopEvent(uint64_t timestamp)491 void TraceLoggerGraph::stopEvent(uint64_t timestamp) {
492   if (enabled && stack.lastEntry().active()) {
493     if (!updateStop(stack.lastEntry().treeId(), timestamp)) {
494       fprintf(stderr, "TraceLogging: Failed to stop an event.\n");
495       enabled = false;
496       failed = true;
497       return;
498     }
499   }
500   if (stack.size() == 1) {
501     if (!enabled) {
502       return;
503     }
504 
505     // Forcefully disable logging. We have no stack information anymore.
506     logTimestamp(TraceLogger_Disable, timestamp);
507     return;
508   }
509   stack.pop();
510 }
511 
logTimestamp(uint32_t id,uint64_t timestamp)512 void TraceLoggerGraph::logTimestamp(uint32_t id, uint64_t timestamp) {
513   if (failed) {
514     return;
515   }
516 
517   if (id == TraceLogger_Enable) {
518     enabled = true;
519   }
520 
521   if (!enabled) {
522     return;
523   }
524 
525   if (id == TraceLogger_Disable) {
526     disable(timestamp);
527   }
528 
529   if (!eventFile) {
530     return;
531   }
532 
533   // Format data in big endian
534   timestamp = NativeEndian::swapToBigEndian(timestamp);
535   id = NativeEndian::swapToBigEndian(id);
536 
537   // The layout of the event log in the log file is:
538   // [timestamp, textId]
539   size_t itemsWritten = 0;
540   itemsWritten += fwrite(&timestamp, sizeof(uint64_t), 1, eventFile);
541   itemsWritten += fwrite(&id, sizeof(uint32_t), 1, eventFile);
542   if (itemsWritten < 2) {
543     failed = true;
544     enabled = false;
545   }
546 }
547 
getTreeEntry(uint32_t treeId,TreeEntry * entry)548 bool TraceLoggerGraph::getTreeEntry(uint32_t treeId, TreeEntry* entry) {
549   // Entry is still in memory
550   if (treeId >= treeOffset) {
551     *entry = tree[treeId - treeOffset];
552     return true;
553   }
554 
555   // If treeFile is null and treeOffset is non-zero then something is wrong
556   if (!treeFile) {
557     return false;
558   }
559 
560   // Entry has been flushed to disk. Look it up.
561   int success = fseek(treeFile, treeId * sizeof(TreeEntry), SEEK_SET);
562   if (success != 0) {
563     return false;
564   }
565 
566   size_t itemsRead = fread((void*)entry, sizeof(TreeEntry), 1, treeFile);
567   if (itemsRead < 1) {
568     return false;
569   }
570 
571   entryToSystemEndian(entry);
572   return true;
573 }
574 
saveTreeEntry(uint32_t treeId,TreeEntry * entry)575 bool TraceLoggerGraph::saveTreeEntry(uint32_t treeId, TreeEntry* entry) {
576   MOZ_ASSERT(treeFile != nullptr);
577   int success = fseek(treeFile, treeId * sizeof(TreeEntry), SEEK_SET);
578   if (success != 0) {
579     return false;
580   }
581 
582   entryToBigEndian(entry);
583 
584   size_t itemsWritten = fwrite(entry, sizeof(TreeEntry), 1, treeFile);
585   if (itemsWritten < 1) {
586     return false;
587   }
588 
589   return true;
590 }
591 
updateHasChildren(uint32_t treeId,bool hasChildren)592 bool TraceLoggerGraph::updateHasChildren(uint32_t treeId, bool hasChildren) {
593   if (treeId < treeOffset) {
594     TreeEntry entry;
595     if (!getTreeEntry(treeId, &entry)) {
596       return false;
597     }
598     entry.setHasChildren(hasChildren);
599     if (treeFile && !saveTreeEntry(treeId, &entry)) {
600       return false;
601     }
602     return true;
603   }
604 
605   tree[treeId - treeOffset].setHasChildren(hasChildren);
606   return true;
607 }
608 
updateNextId(uint32_t treeId,uint32_t nextId)609 bool TraceLoggerGraph::updateNextId(uint32_t treeId, uint32_t nextId) {
610   if (treeId < treeOffset) {
611     TreeEntry entry;
612     if (!getTreeEntry(treeId, &entry)) {
613       return false;
614     }
615     entry.setNextId(nextId);
616     if (treeFile && !saveTreeEntry(treeId, &entry)) {
617       return false;
618     }
619     return true;
620   }
621 
622   tree[treeId - treeOffset].setNextId(nextId);
623   return true;
624 }
625 
updateStop(uint32_t treeId,uint64_t timestamp)626 bool TraceLoggerGraph::updateStop(uint32_t treeId, uint64_t timestamp) {
627   if (treeId < treeOffset) {
628     TreeEntry entry;
629     if (!getTreeEntry(treeId, &entry)) {
630       return false;
631     }
632     entry.setStop(timestamp);
633     if (treeFile && !saveTreeEntry(treeId, &entry)) {
634       return false;
635     }
636     return true;
637   }
638 
639   tree[treeId - treeOffset].setStop(timestamp);
640   return true;
641 }
642 
disable(uint64_t timestamp)643 void TraceLoggerGraph::disable(uint64_t timestamp) {
644   MOZ_ASSERT(enabled);
645   while (stack.size() > 1) {
646     stopEvent(timestamp);
647   }
648 
649   enabled = false;
650 }
651 
log(ContinuousSpace<EventEntry> & events,mozilla::TimeStamp startTime)652 void TraceLoggerGraph::log(ContinuousSpace<EventEntry>& events,
653                            mozilla::TimeStamp startTime) {
654   for (uint32_t i = 0; i < events.size(); i++) {
655     mozilla::TimeDuration delta = events[i].time - startTime;
656     uint64_t timeOffset = static_cast<uint64_t>(delta.ToMicroseconds());
657 
658     if (events[i].textId == TraceLogger_Stop) {
659       stopEvent(timeOffset);
660     } else if (TLTextIdIsTreeEvent(events[i].textId)) {
661       startEvent(events[i].textId, timeOffset);
662     } else {
663       logTimestamp(events[i].textId, timeOffset);
664     }
665   }
666 }
667 
addTextId(uint32_t id,const char * text)668 void TraceLoggerGraph::addTextId(uint32_t id, const char* text) {
669   mozilla::Maybe<uint32_t> line = mozilla::Nothing();
670   mozilla::Maybe<uint32_t> column = mozilla::Nothing();
671   addTextId(id, text, line, column);
672 }
673 
addTextId(uint32_t id,const char * text,mozilla::Maybe<uint32_t> & line,mozilla::Maybe<uint32_t> & column)674 void TraceLoggerGraph::addTextId(uint32_t id, const char* text,
675                                  mozilla::Maybe<uint32_t>& line,
676                                  mozilla::Maybe<uint32_t>& column) {
677   if (failed) {
678     return;
679   }
680 
681   // Assume ids are given in order. Which is currently true.
682   MOZ_ASSERT(id == nextTextId_);
683   nextTextId_++;
684 
685   if (id > 0) {
686     int written = fprintf(dictFile, ",\n");
687     if (written < 0) {
688       failed = true;
689       return;
690     }
691   }
692 
693   js::UniqueChars str;
694   if (line && column) {
695     str = JS_smprintf("script %s:%u:%u", text, *line, *column);
696   } else if (line) {
697     str = JS_smprintf("script %s:%u", text, *line);
698   } else {
699     str = JS_smprintf("%s", text);
700   }
701 
702   if (!js::FileEscapedString(dictFile, str.get(), strlen(str.get()), '"')) {
703     failed = true;
704   }
705   str.reset();
706 }
707 
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const708 size_t TraceLoggerGraph::sizeOfExcludingThis(
709     mozilla::MallocSizeOf mallocSizeOf) const {
710   size_t size = 0;
711   size += tree.sizeOfExcludingThis(mallocSizeOf);
712   size += stack.sizeOfExcludingThis(mallocSizeOf);
713   return size;
714 }
715 
sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const716 size_t TraceLoggerGraph::sizeOfIncludingThis(
717     mozilla::MallocSizeOf mallocSizeOf) const {
718   return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
719 }
720 
721 #undef getpid
722