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