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