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