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(×tamp, 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