VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/alloc/memtracker.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 46.4 KB
Line 
1/* $Id: memtracker.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Memory Tracker & Leak Detector.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/memtracker.h>
42#include "internal/iprt.h"
43
44#include <iprt/asm.h>
45#include <iprt/assert.h>
46#include <iprt/avl.h>
47#include <iprt/critsect.h>
48#ifdef IN_RING3
49# include <iprt/file.h>
50#endif
51#include <iprt/errcore.h>
52#include <iprt/list.h>
53#include <iprt/log.h>
54#include <iprt/mem.h>
55#include <iprt/semaphore.h>
56#include <iprt/string.h>
57#include <iprt/thread.h>
58
59#include "internal/file.h"
60#include "internal/magics.h"
61#include "internal/strhash.h"
62
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67/** Pointer to a memory tracker instance */
68typedef struct RTMEMTRACKERINT *PRTMEMTRACKERINT;
69
70/**
71 * Memory tracker statistics.
72 */
73typedef struct RTMEMTRACKERSTATS
74{
75 /** Array of method calls. */
76 uint64_t volatile acMethodCalls[RTMEMTRACKERMETHOD_END];
77 /** The number of times this user freed or reallocated a memory block
78 * orignally allocated by someone else. */
79 uint64_t volatile cUserChanges;
80 /** The total number of bytes allocated ever. */
81 uint64_t volatile cbTotalAllocated;
82 /** The total number of blocks allocated ever. */
83 uint64_t volatile cTotalAllocatedBlocks;
84 /** The number of bytes currently allocated. */
85 size_t volatile cbAllocated;
86 /** The number of blocks currently allocated. */
87 size_t volatile cAllocatedBlocks;
88} RTMEMTRACKERSTATS;
89/** Pointer to memory tracker statistics. */
90typedef RTMEMTRACKERSTATS *PRTMEMTRACKERSTATS;
91
92
93/**
94 * Memory tracker user data.
95 */
96typedef struct RTMEMTRACKERUSER
97{
98 /** Entry in the user list (RTMEMTRACKERINT::UserList). */
99 RTLISTNODE ListEntry;
100 /** Pointer to the tracker. */
101 PRTMEMTRACKERINT pTracker;
102 /** Critical section protecting the memory list. */
103 RTCRITSECT CritSect;
104 /** The list of memory allocated by this user (RTMEMTRACKERHDR). */
105 RTLISTANCHOR MemoryList;
106 /** Positive numbers indicates recursion.
107 * Negative numbers are used for the global user since that is shared by
108 * more than one thread. */
109 int32_t volatile cInTracker;
110 /** The user identifier. */
111 uint32_t idUser;
112 /** The statistics for this user. */
113 RTMEMTRACKERSTATS Stats;
114 /** The user (thread) name. */
115 char szName[32];
116} RTMEMTRACKERUSER;
117/** Pointer to memory tracker per user data. */
118typedef RTMEMTRACKERUSER *PRTMEMTRACKERUSER;
119
120
121/**
122 * Memory tracker per tag statistics.
123 */
124typedef struct RTMEMTRACKERTAG
125{
126 /** AVL node core for lookup by hash. */
127 AVLU32NODECORE Core;
128 /** Tag list entry for flat traversal while dumping. */
129 RTLISTNODE ListEntry;
130 /** Pointer to the next tag with the same hash (collisions). */
131 PRTMEMTRACKERTAG pNext;
132 /** The tag statistics. */
133 RTMEMTRACKERSTATS Stats;
134 /** The tag name length. */
135 size_t cchTag;
136 /** The tag string. */
137 char szTag[1];
138} RTMEMTRACKERTAG;
139
140
141/**
142 * The memory tracker instance.
143 */
144typedef struct RTMEMTRACKERINT
145{
146 /** Cross roads semaphore separating dumping and normal operation.
147 * - NS - normal tracking.
148 * - EW - dumping tracking data. */
149 RTSEMXROADS hXRoads;
150
151 /** Critical section protecting the user list and tag database. */
152 RTCRITSECT CritSect;
153 /** List of RTMEMTRACKERUSER records. */
154 RTLISTANCHOR UserList;
155 /** The next user identifier number. */
156 uint32_t idUserNext;
157 /** The TLS index used for the per thread user records. */
158 RTTLS iTls;
159 /** Cross roads semaphore used to protect the tag database.
160 * - NS - lookup.
161 * - EW + critsect - insertion.
162 * @todo Replaced this by a read-write semaphore. */
163 RTSEMXROADS hXRoadsTagDb;
164 /** The root of the tag lookup database. */
165 AVLU32TREE TagDbRoot;
166 /** List of RTMEMTRACKERTAG records. */
167 RTLISTANCHOR TagList;
168#if ARCH_BITS == 32
169 /** Alignment padding. */
170 uint32_t u32Alignment;
171#endif
172 /** The global user record (fallback). */
173 RTMEMTRACKERUSER FallbackUser;
174 /** The global statistics. */
175 RTMEMTRACKERSTATS GlobalStats;
176 /** The number of busy (recursive) allocations. */
177 uint64_t volatile cBusyAllocs;
178 /** The number of busy (recursive) frees. */
179 uint64_t volatile cBusyFrees;
180 /** The number of tags. */
181 uint32_t cTags;
182 /** The number of users. */
183 uint32_t cUsers;
184} RTMEMTRACKERINT;
185AssertCompileMemberAlignment(RTMEMTRACKERINT, FallbackUser, 8);
186
187
188/**
189 * Output callback structure.
190 */
191typedef struct RTMEMTRACKEROUTPUT
192{
193 /** The printf like callback. */
194 DECLCALLBACKMEMBER(void, pfnPrintf,(struct RTMEMTRACKEROUTPUT *pThis, const char *pszFormat, ...));
195
196 /** The data. */
197 union
198 {
199 RTFILE hFile;
200 } uData;
201} RTMEMTRACKEROUTPUT;
202/** Pointer to a memory tracker output callback structure. */
203typedef RTMEMTRACKEROUTPUT *PRTMEMTRACKEROUTPUT;
204
205
206/*********************************************************************************************************************************
207* Global Variables *
208*********************************************************************************************************************************/
209/** Pointer to the default memory tracker. */
210static PRTMEMTRACKERINT g_pDefaultTracker = NULL;
211
212
213/**
214 * Creates a memory tracker.
215 *
216 * @returns IRPT status code.
217 * @param ppTracker Where to return the tracker instance.
218 */
219static int rtMemTrackerCreate(PRTMEMTRACKERINT *ppTracker)
220{
221 PRTMEMTRACKERINT pTracker = (PRTMEMTRACKERINT)RTMemAllocZ(sizeof(*pTracker));
222 if (!pTracker)
223 return VERR_NO_MEMORY;
224
225 /*
226 * Create locks and stuff.
227 */
228 int rc = RTCritSectInitEx(&pTracker->CritSect,
229 RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_BOOTSTRAP_HACK,
230 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
231 if (RT_SUCCESS(rc))
232 {
233 rc = RTSemXRoadsCreate(&pTracker->hXRoads);
234 if (RT_SUCCESS(rc))
235 {
236 rc = RTSemXRoadsCreate(&pTracker->hXRoadsTagDb);
237 if (RT_SUCCESS(rc))
238 {
239 rc = RTTlsAllocEx(&pTracker->iTls, NULL);
240 if (RT_SUCCESS(rc))
241 {
242 rc = RTCritSectInitEx(&pTracker->FallbackUser.CritSect,
243 RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_BOOTSTRAP_HACK,
244 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
245 if (RT_SUCCESS(rc))
246 {
247 /*
248 * Initialize the rest of the structure.
249 */
250 RTListInit(&pTracker->UserList);
251 RTListInit(&pTracker->TagList);
252 RTListInit(&pTracker->FallbackUser.ListEntry);
253 RTListInit(&pTracker->FallbackUser.MemoryList);
254 pTracker->FallbackUser.pTracker = pTracker;
255 pTracker->FallbackUser.cInTracker = INT32_MIN / 2;
256 pTracker->FallbackUser.idUser = pTracker->idUserNext++;
257 strcpy(pTracker->FallbackUser.szName, "fallback");
258
259 *ppTracker = pTracker;
260 return VINF_SUCCESS;
261 }
262
263 RTTlsFree(pTracker->iTls);
264 }
265 RTSemXRoadsDestroy(pTracker->hXRoadsTagDb);
266 }
267 RTSemXRoadsDestroy(pTracker->hXRoads);
268 }
269 RTCritSectDelete(&pTracker->CritSect);
270 }
271 return rc;
272}
273
274
275/**
276 * Gets the user record to use.
277 *
278 * @returns Pointer to a user record.
279 * @param pTracker The tracker instance.
280 */
281static PRTMEMTRACKERUSER rtMemTrackerGetUser(PRTMEMTRACKERINT pTracker)
282{
283 /* ASSUMES that RTTlsGet and RTTlsSet will not reenter. */
284 PRTMEMTRACKERUSER pUser = (PRTMEMTRACKERUSER)RTTlsGet(pTracker->iTls);
285 if (RT_UNLIKELY(!pUser))
286 {
287 /*
288 * Is the thread currently initializing or terminating?
289 * If so, don't try add any user record for it as RTThread may barf or
290 * we might not get the thread name.
291 */
292 if (!RTThreadIsSelfAlive())
293 return &pTracker->FallbackUser;
294
295 /*
296 * Allocate and initialize a new user record for this thread.
297 *
298 * We install the fallback user record while doing the allocation and
299 * locking so that we can deal with recursions.
300 */
301 int rc = RTTlsSet(pTracker->iTls, &pTracker->FallbackUser);
302 if (RT_SUCCESS(rc))
303 {
304 pUser = (PRTMEMTRACKERUSER)RTMemAllocZ(sizeof(*pUser));
305 if (pUser)
306 {
307 rc = RTCritSectInitEx(&pUser->CritSect,
308 RTCRITSECT_FLAGS_NO_LOCK_VAL | RTCRITSECT_FLAGS_NO_NESTING | RTCRITSECT_FLAGS_BOOTSTRAP_HACK,
309 NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
310 if (RT_SUCCESS(rc))
311 {
312 RTListInit(&pUser->ListEntry);
313 RTListInit(&pUser->MemoryList);
314 pUser->pTracker = pTracker;
315 pUser->cInTracker = 1;
316
317 const char *pszName = RTThreadSelfName();
318 if (pszName)
319 RTStrCopy(pUser->szName, sizeof(pUser->szName), pszName);
320
321 /*
322 * Register the new user record.
323 */
324 rc = RTTlsSet(pTracker->iTls, pUser);
325 if (RT_SUCCESS(rc))
326 {
327 RTCritSectEnter(&pTracker->CritSect);
328
329 pUser->idUser = pTracker->idUserNext++;
330 RTListAppend(&pTracker->UserList, &pUser->ListEntry);
331 pTracker->cUsers++;
332
333 RTCritSectLeave(&pTracker->CritSect);
334 return pUser;
335 }
336
337 RTCritSectDelete(&pUser->CritSect);
338 }
339 RTMemFree(pUser);
340 }
341 else
342 rc = VERR_NO_MEMORY;
343 }
344
345 /* Failed, user the fallback. */
346 pUser = &pTracker->FallbackUser;
347 }
348
349 ASMAtomicIncS32(&pUser->cInTracker);
350 return pUser;
351}
352
353
354/**
355 * Counterpart to rtMemTrackerGetUser.
356 *
357 * @param pUser The user record to 'put' back.
358 */
359DECLINLINE(void) rtMemTrackerPutUser(PRTMEMTRACKERUSER pUser)
360{
361 ASMAtomicDecS32(&pUser->cInTracker);
362}
363
364
365/**
366 * Get the tag record corresponding to @a pszTag.
367 *
368 * @returns The tag record. This may be NULL if we're out of memory or
369 * if something goes wrong.
370 *
371 * @param pTracker The tracker instance.
372 * @param pUser The user record of the caller. Must NOT be
373 * NULL. This is used to prevent infinite
374 * recursions when allocating a new tag record.
375 * @param pszTag The tag string. Can be NULL.
376 */
377DECLINLINE(PRTMEMTRACKERTAG) rtMemTrackerGetTag(PRTMEMTRACKERINT pTracker, PRTMEMTRACKERUSER pUser, const char *pszTag)
378{
379 AssertPtr(pTracker);
380 AssertPtr(pUser);
381 if (pUser->cInTracker <= 0)
382 return NULL;
383
384 /*
385 * Hash tag string.
386 */
387 size_t cchTag;
388 uint32_t uHash;
389 if (pszTag)
390 uHash = sdbmN(pszTag, 260, &cchTag);
391 else
392 {
393 pszTag = "";
394 cchTag = 0;
395 uHash = 0;
396 }
397
398 /*
399 * Look up the tag.
400 */
401 RTSemXRoadsNSEnter(pTracker->hXRoadsTagDb);
402 PRTMEMTRACKERTAG pTag = (PRTMEMTRACKERTAG)RTAvlU32Get(&pTracker->TagDbRoot, uHash);
403 while ( pTag
404 && ( pTag->cchTag != cchTag
405 || memcmp(pTag->szTag, pszTag, cchTag)) )
406 pTag = pTag->pNext;
407 RTSemXRoadsNSLeave(pTracker->hXRoadsTagDb);
408
409 /*
410 * Create a new tag record if not found.
411 */
412 if (RT_UNLIKELY(!pTag))
413 {
414 pTag = (PRTMEMTRACKERTAG)RTMemAllocZVar(RT_UOFFSETOF_DYN(RTMEMTRACKERTAG, szTag[cchTag + 1]));
415 if (pTag)
416 {
417 pTag->Core.Key = uHash;
418 pTag->cchTag = cchTag;
419 memcpy(pTag->szTag, pszTag, cchTag + 1);
420
421 RTSemXRoadsEWEnter(pTracker->hXRoadsTagDb);
422 RTCritSectEnter(&pTracker->CritSect);
423
424 void *pvFreeMe = NULL;
425 PRTMEMTRACKERTAG pHeadTag = (PRTMEMTRACKERTAG)RTAvlU32Get(&pTracker->TagDbRoot, uHash);
426 if (!pHeadTag)
427 {
428 RTAvlU32Insert(&pTracker->TagDbRoot, &pTag->Core);
429 RTListAppend(&pTracker->TagList, &pTag->ListEntry);
430 pTracker->cTags++;
431 }
432 else
433 {
434 PRTMEMTRACKERTAG pTag2 = pHeadTag;
435 while ( pTag2
436 && ( pTag2->cchTag != cchTag
437 || memcmp(pTag2->szTag, pszTag, cchTag)) )
438 pTag2 = pTag2->pNext;
439 if (RT_LIKELY(!pTag2))
440 {
441 pTag->pNext = pHeadTag->pNext;
442 pHeadTag->pNext = pTag;
443 RTListAppend(&pTracker->TagList, &pTag->ListEntry);
444 pTracker->cTags++;
445 }
446 else
447 {
448 pvFreeMe = pTag;
449 pTag = pTag2;
450 }
451 }
452
453 RTCritSectLeave(&pTracker->CritSect);
454 RTSemXRoadsEWLeave(pTracker->hXRoadsTagDb);
455
456 if (RT_LIKELY(pvFreeMe))
457 RTMemFree(pvFreeMe);
458 }
459 }
460
461 return pTag;
462}
463
464
465/**
466 * Counterpart to rtMemTrackerGetTag.
467 *
468 * @param pTag The tag record to 'put' back.
469 */
470DECLINLINE(void) rtMemTrackerPutTag(PRTMEMTRACKERTAG pTag)
471{
472 NOREF(pTag);
473}
474
475
476/**
477 * Record an allocation call.
478 *
479 * @param pStats The statistics record.
480 * @param cbUser The size of the allocation.
481 * @param enmMethod The allocation method.
482 */
483DECLINLINE(void) rtMemTrackerStateRecordAlloc(PRTMEMTRACKERSTATS pStats, size_t cbUser, RTMEMTRACKERMETHOD enmMethod)
484{
485 ASMAtomicAddU64(&pStats->cbTotalAllocated, cbUser);
486 ASMAtomicIncU64(&pStats->cTotalAllocatedBlocks);
487 ASMAtomicAddZ(&pStats->cbAllocated, cbUser);
488 ASMAtomicIncZ(&pStats->cAllocatedBlocks);
489 ASMAtomicIncU64(&pStats->acMethodCalls[enmMethod]);
490}
491
492
493/**
494 * Record a free call.
495 *
496 * @param pStats The statistics record.
497 * @param cbUser The size of the allocation.
498 * @param enmMethod The free method.
499 */
500DECLINLINE(void) rtMemTrackerStateRecordFree(PRTMEMTRACKERSTATS pStats, size_t cbUser, RTMEMTRACKERMETHOD enmMethod)
501{
502 ASMAtomicSubZ(&pStats->cbAllocated, cbUser);
503 ASMAtomicDecZ(&pStats->cAllocatedBlocks);
504 ASMAtomicIncU64(&pStats->acMethodCalls[enmMethod]);
505}
506
507
508/**
509 * Internal RTMemTrackerHdrAlloc and RTMemTrackerHdrAllocEx worker.
510 *
511 * @returns Pointer to the user data allocation.
512 * @param pTracker The tracker instance. Can be NULL.
513 * @param pv The pointer to the allocated memory. This
514 * includes room for the header.
515 * @param cbUser The size requested by the user.
516 * @param pszTag The tag string.
517 * @param pvCaller The return address.
518 * @param enmMethod The allocation method.
519 */
520static void *rtMemTrackerHdrAllocEx(PRTMEMTRACKERINT pTracker, void *pv, size_t cbUser,
521 const char *pszTag, void *pvCaller, RTMEMTRACKERMETHOD enmMethod)
522{
523 /*
524 * Check input.
525 */
526 if (!pv)
527 return NULL;
528 AssertReturn(enmMethod > RTMEMTRACKERMETHOD_INVALID && enmMethod < RTMEMTRACKERMETHOD_END, NULL);
529
530 /*
531 * Initialize the header.
532 */
533 PRTMEMTRACKERHDR pHdr = (PRTMEMTRACKERHDR)pv;
534
535 pHdr->uMagic = RTMEMTRACKERHDR_MAGIC;
536 pHdr->cbUser = cbUser;
537 RTListInit(&pHdr->ListEntry);
538 pHdr->pUser = NULL;
539 pHdr->pszTag = pszTag;
540 pHdr->pTag = NULL;
541 pHdr->pvCaller = pvCaller;
542 pHdr->pvUser = pHdr + 1;
543 pHdr->uReserved = 0;
544
545 /*
546 * Add it to the tracker if we've got one.
547 */
548 if (pTracker)
549 {
550 PRTMEMTRACKERUSER pUser = rtMemTrackerGetUser(pTracker);
551 if (pUser->cInTracker == 1)
552 {
553 RTSemXRoadsNSEnter(pTracker->hXRoads);
554
555 /* Get the tag and update it's statistics. */
556 PRTMEMTRACKERTAG pTag = rtMemTrackerGetTag(pTracker, pUser, pszTag);
557 if (pTag)
558 {
559 pHdr->pTag = pTag;
560 rtMemTrackerStateRecordAlloc(&pTag->Stats, cbUser, enmMethod);
561 rtMemTrackerPutTag(pTag);
562 }
563
564 /* Link the header and update the user statistics. */
565 RTCritSectEnter(&pUser->CritSect);
566 RTListAppend(&pUser->MemoryList, &pHdr->ListEntry);
567 RTCritSectLeave(&pUser->CritSect);
568
569 pHdr->pUser = pUser;
570 rtMemTrackerStateRecordAlloc(&pUser->Stats, cbUser, enmMethod);
571
572 /* Update the global statistics. */
573 rtMemTrackerStateRecordAlloc(&pTracker->GlobalStats, cbUser, enmMethod);
574
575 RTSemXRoadsNSLeave(pTracker->hXRoads);
576 }
577 else
578 ASMAtomicIncU64(&pTracker->cBusyAllocs);
579 rtMemTrackerPutUser(pUser);
580 }
581
582 return pHdr + 1;
583}
584
585
586/**
587 * Internal worker for rtMemTrackerHdrFreeEx and rtMemTrackerHdrReallocPrep.
588 *
589 * @returns Pointer to the original block.
590 * @param pTracker The tracker instance. Can be NULL.
591 * @param pvUser Pointer to the user memory.
592 * @param cbUser The size of the user memory or 0.
593 * @param pszTag The tag to associate the free with.
594 * @param pvCaller The return address.
595 * @param enmMethod The free method.
596 * @param uDeadMagic The dead magic value to use.
597 */
598static void *rtMemTrackerHdrFreeCommon(PRTMEMTRACKERINT pTracker, void *pvUser, size_t cbUser,
599 const char *pszTag, void *pvCaller, RTMEMTRACKERMETHOD enmMethod,
600 size_t uDeadMagic)
601{
602 PRTMEMTRACKERHDR pHdr = (PRTMEMTRACKERHDR)pvUser - 1;
603 AssertReturn(pHdr->uMagic == RTMEMTRACKERHDR_MAGIC, NULL);
604 Assert(pHdr->cbUser == cbUser || !cbUser); NOREF(cbUser);
605 Assert(pHdr->pvUser == pvUser);
606
607 AssertReturn(enmMethod > RTMEMTRACKERMETHOD_INVALID && enmMethod < RTMEMTRACKERMETHOD_END, NULL);
608
609 /*
610 * First mark it as free.
611 */
612 pHdr->uMagic = uDeadMagic;
613
614 /*
615 * If there is a association with a user, we need to unlink it and update
616 * the statistics.
617 *
618 * A note on the locking here. We don't take the crossroads semaphore when
619 * reentering the memory tracker on the same thread because we may be
620 * holding it in a different direction and would therefore deadlock.
621 */
622 PRTMEMTRACKERUSER pMemUser = pHdr->pUser;
623 if (pMemUser)
624 {
625 Assert(pMemUser->pTracker == pTracker); Assert(pTracker);
626 PRTMEMTRACKERUSER pCallingUser = rtMemTrackerGetUser(pTracker);
627 bool const fTakeXRoadsLock = pCallingUser->cInTracker <= 1;
628 if (fTakeXRoadsLock)
629 RTSemXRoadsNSEnter(pTracker->hXRoads);
630
631 RTCritSectEnter(&pMemUser->CritSect);
632 RTListNodeRemove(&pHdr->ListEntry);
633 RTCritSectLeave(&pMemUser->CritSect);
634
635 if (pCallingUser == pMemUser)
636 rtMemTrackerStateRecordFree(&pCallingUser->Stats, pHdr->cbUser, enmMethod);
637 else
638 {
639 ASMAtomicIncU64(&pCallingUser->Stats.cUserChanges);
640 ASMAtomicIncU64(&pCallingUser->Stats.acMethodCalls[enmMethod]);
641
642 ASMAtomicSubU64(&pMemUser->Stats.cbTotalAllocated, cbUser);
643 ASMAtomicSubZ(&pMemUser->Stats.cbAllocated, cbUser);
644 }
645
646 rtMemTrackerStateRecordFree(&pTracker->GlobalStats, pHdr->cbUser, enmMethod);
647
648 /** @todo we're currently ignoring pszTag, consider how to correctly
649 * attribute the free operation if the tags differ - if it
650 * makes sense at all... */
651 NOREF(pszTag);
652 if (pHdr->pTag)
653 rtMemTrackerStateRecordFree(&pHdr->pTag->Stats, pHdr->cbUser, enmMethod);
654
655
656 if (fTakeXRoadsLock)
657 RTSemXRoadsNSLeave(pTracker->hXRoads);
658 rtMemTrackerPutUser(pCallingUser);
659 }
660 else
661 {
662 /*
663 * No tracked. This may happen even when pTracker != NULL when the same
664 * thread reenters the tracker when allocating tracker structures or memory
665 * in some subroutine like threading and locking.
666 */
667 Assert(!pHdr->pTag);
668 if (pTracker)
669 ASMAtomicIncU64(&pTracker->cBusyFrees);
670 }
671
672 NOREF(pvCaller); /* Intended for We may later do some use-after-free tracking. */
673 return pHdr;
674}
675
676
677/**
678 * Internal worker for RTMemTrackerHdrReallocPrep and
679 * RTMemTrackerHdrReallocPrepEx.
680 *
681 * @returns Pointer to the actual allocation.
682 * @param pTracker The tracker instance. Can be NULL.
683 * @param pvOldUser The user memory.
684 * @param cbOldUser The size of the user memory, 0 if unknown.
685 * @param pszTag The tag string.
686 * @param pvCaller The return address.
687 */
688static void *rtMemTrackerHdrReallocPrepEx(PRTMEMTRACKERINT pTracker, void *pvOldUser, size_t cbOldUser,
689 const char *pszTag, void *pvCaller)
690{
691 if (!pvOldUser)
692 return NULL;
693 return rtMemTrackerHdrFreeCommon(pTracker, pvOldUser, cbOldUser, pszTag, pvCaller,
694 RTMEMTRACKERMETHOD_REALLOC_PREP, RTMEMTRACKERHDR_MAGIC_REALLOC);
695}
696
697
698/**
699 * Internal worker for RTMemTrackerHdrReallocDone and
700 * RTMemTrackerHdrReallocDoneEx.
701 *
702 * @returns Pointer to the actual allocation.
703 * @param pTracker The tracker instance. Can be NULL.
704 * @param pvNew The new memory chunk. Can be NULL.
705 * @param cbNewUser The size of the new memory chunk.
706 * @param pvOldUser Pointer to the old user memory.
707 * @param pszTag The tag string.
708 * @param pvCaller The return address.
709 */
710static void *rtMemTrackerHdrReallocDoneEx(PRTMEMTRACKERINT pTracker, void *pvNew, size_t cbNewUser,
711 void *pvOldUser, const char *pszTag, void *pvCaller)
712{
713 /* Succeeded? */
714 if (pvNew)
715 return rtMemTrackerHdrAllocEx(pTracker, pvNew, cbNewUser, pszTag, pvCaller, RTMEMTRACKERMETHOD_REALLOC_DONE);
716
717 /* Failed or just realloc to zero? */
718 if (cbNewUser)
719 {
720 PRTMEMTRACKERHDR pHdr = (PRTMEMTRACKERHDR)pvOldUser - 1;
721 AssertReturn(pHdr->uMagic == RTMEMTRACKERHDR_MAGIC_REALLOC, NULL);
722
723 return rtMemTrackerHdrAllocEx(pTracker, pHdr, pHdr->cbUser, pszTag, pvCaller, RTMEMTRACKERMETHOD_REALLOC_FAILED);
724 }
725
726 /* Tealloc to zero bytes, i.e. free. */
727 return NULL;
728}
729
730
731/**
732 * Internal worker for RTMemTrackerHdrFree and RTMemTrackerHdrFreeEx.
733 *
734 * @returns Pointer to the actual allocation.
735 * @param pTracker The tracker instance. Can be NULL.
736 * @param pvUser The user memory.
737 * @param cbUser The size of the user memory, 0 if unknown.
738 * @param pszTag The tag string.
739 * @param pvCaller The return address.
740 * @param enmMethod The free method.
741 */
742static void *rtMemTrackerHdrFreeEx(PRTMEMTRACKERINT pTracker, void *pvUser, size_t cbUser,
743 const char *pszTag, void *pvCaller, RTMEMTRACKERMETHOD enmMethod)
744{
745 if (!pvUser)
746 return NULL;
747 return rtMemTrackerHdrFreeCommon(pTracker, pvUser, cbUser, pszTag, pvCaller, enmMethod, RTMEMTRACKERHDR_MAGIC_FREE);
748}
749
750
751/**
752 * Prints a statistics record.
753 *
754 * @param pStats The record.
755 * @param pOutput The output callback table.
756 * @param fVerbose Whether to print in terse or verbose form.
757 */
758DECLINLINE(void) rtMemTrackerDumpOneStatRecord(PRTMEMTRACKERSTATS pStats, PRTMEMTRACKEROUTPUT pOutput, bool fVerbose)
759{
760 if (fVerbose)
761 {
762 pOutput->pfnPrintf(pOutput,
763 " Currently allocated: %7zu blocks, %8zu bytes\n"
764 " Total allocation sum: %7RU64 blocks, %8RU64 bytes\n"
765 ,
766 pStats->cAllocatedBlocks,
767 pStats->cbAllocated,
768 pStats->cTotalAllocatedBlocks,
769 pStats->cbTotalAllocated);
770 pOutput->pfnPrintf(pOutput,
771 " Alloc: %7RU64 AllocZ: %7RU64 Free: %7RU64 User Chg: %7RU64\n"
772 " RPrep: %7RU64 RDone: %7RU64 RFail: %7RU64\n"
773 " New: %7RU64 New[]: %7RU64 Delete: %7RU64 Delete[]: %7RU64\n"
774 ,
775 pStats->acMethodCalls[RTMEMTRACKERMETHOD_ALLOC],
776 pStats->acMethodCalls[RTMEMTRACKERMETHOD_ALLOCZ],
777 pStats->acMethodCalls[RTMEMTRACKERMETHOD_FREE],
778 pStats->cUserChanges,
779 pStats->acMethodCalls[RTMEMTRACKERMETHOD_REALLOC_PREP],
780 pStats->acMethodCalls[RTMEMTRACKERMETHOD_REALLOC_DONE],
781 pStats->acMethodCalls[RTMEMTRACKERMETHOD_REALLOC_FAILED],
782 pStats->acMethodCalls[RTMEMTRACKERMETHOD_NEW],
783 pStats->acMethodCalls[RTMEMTRACKERMETHOD_NEW_ARRAY],
784 pStats->acMethodCalls[RTMEMTRACKERMETHOD_DELETE],
785 pStats->acMethodCalls[RTMEMTRACKERMETHOD_DELETE_ARRAY]);
786 }
787 else
788 {
789 pOutput->pfnPrintf(pOutput, " %zu bytes in %zu blocks\n",
790 pStats->cbAllocated, pStats->cAllocatedBlocks);
791 }
792}
793
794
795/**
796 * Internal worker that dumps all the memory tracking data.
797 *
798 * @param pTracker The tracker instance. Can be NULL.
799 * @param pOutput The output callback table.
800 */
801static void rtMemTrackerDumpAllWorker(PRTMEMTRACKERINT pTracker, PRTMEMTRACKEROUTPUT pOutput)
802{
803 if (!pTracker)
804 return;
805
806 /*
807 * We use the EW direction to make sure the lists, trees and statistics
808 * does not change while we're working.
809 */
810 PRTMEMTRACKERUSER pUser = rtMemTrackerGetUser(pTracker);
811 RTSemXRoadsEWEnter(pTracker->hXRoads);
812
813 /* Global statistics.*/
814 pOutput->pfnPrintf(pOutput, "*** Global statistics ***\n");
815 rtMemTrackerDumpOneStatRecord(&pTracker->GlobalStats, pOutput, true);
816 pOutput->pfnPrintf(pOutput, " Busy Allocs: %4RU64 Busy Frees: %4RU64 Tags: %3u Users: %3u\n",
817 pTracker->cBusyAllocs, pTracker->cBusyFrees, pTracker->cTags, pTracker->cUsers);
818
819 /* Per tag statistics. */
820 pOutput->pfnPrintf(pOutput, "\n*** Tag statistics ***\n");
821 PRTMEMTRACKERTAG pTag, pNextTag;
822 RTListForEachSafe(&pTracker->TagList, pTag, pNextTag, RTMEMTRACKERTAG, ListEntry)
823 {
824 pOutput->pfnPrintf(pOutput, "Tag: %s\n", pTag->szTag);
825 rtMemTrackerDumpOneStatRecord(&pTag->Stats, pOutput, true);
826 pOutput->pfnPrintf(pOutput, "\n", pTag->szTag);
827 }
828
829 /* Per user statistics & blocks. */
830 pOutput->pfnPrintf(pOutput, "\n*** User statistics ***\n");
831 PRTMEMTRACKERUSER pCurUser, pNextUser;
832 RTListForEachSafe(&pTracker->UserList, pCurUser, pNextUser, RTMEMTRACKERUSER, ListEntry)
833 {
834 pOutput->pfnPrintf(pOutput, "User #%u: %s%s (cInTracker=%d)\n",
835 pCurUser->idUser,
836 pCurUser->szName,
837 pUser == pCurUser ? " (me)" : "",
838 pCurUser->cInTracker);
839 rtMemTrackerDumpOneStatRecord(&pCurUser->Stats, pOutput, true);
840
841 PRTMEMTRACKERHDR pCurHdr, pNextHdr;
842 RTListForEachSafe(&pCurUser->MemoryList, pCurHdr, pNextHdr, RTMEMTRACKERHDR, ListEntry)
843 {
844 if (pCurHdr->pTag)
845 pOutput->pfnPrintf(pOutput,
846 " %zu bytes at %p by %p with tag %s\n"
847 "%.*Rhxd\n"
848 "\n",
849 pCurHdr->cbUser, pCurHdr->pvUser, pCurHdr->pvCaller, pCurHdr->pTag->szTag,
850 RT_MIN(pCurHdr->cbUser, 16*3), pCurHdr->pvUser);
851 else
852 pOutput->pfnPrintf(pOutput,
853 " %zu bytes at %p by %p without a tag\n"
854 "%.*Rhxd\n"
855 "\n",
856 pCurHdr->cbUser, pCurHdr->pvUser, pCurHdr->pvCaller,
857 RT_MIN(pCurHdr->cbUser, 16*3), pCurHdr->pvUser);
858 }
859 pOutput->pfnPrintf(pOutput, "\n", pTag->szTag);
860 }
861
862 /* Repeat the global statistics. */
863 pOutput->pfnPrintf(pOutput, "*** Global statistics (reprise) ***\n");
864 rtMemTrackerDumpOneStatRecord(&pTracker->GlobalStats, pOutput, true);
865 pOutput->pfnPrintf(pOutput, " Busy Allocs: %4RU64 Busy Frees: %4RU64 Tags: %3u Users: %3u\n",
866 pTracker->cBusyAllocs, pTracker->cBusyFrees, pTracker->cTags, pTracker->cUsers);
867
868 RTSemXRoadsEWLeave(pTracker->hXRoads);
869 rtMemTrackerPutUser(pUser);
870}
871
872
873/**
874 * Internal worker that dumps the memory tracking statistics.
875 *
876 * @param pTracker The tracker instance. Can be NULL.
877 * @param pOutput The output callback table.
878 * @param fVerbose Whether to the verbose or quiet.
879 */
880static void rtMemTrackerDumpStatsWorker(PRTMEMTRACKERINT pTracker, PRTMEMTRACKEROUTPUT pOutput, bool fVerbose)
881{
882 if (!pTracker)
883 return;
884
885 /*
886 * We use the EW direction to make sure the lists, trees and statistics
887 * does not change while we're working.
888 */
889 PRTMEMTRACKERUSER pUser = rtMemTrackerGetUser(pTracker);
890 RTSemXRoadsEWEnter(pTracker->hXRoads);
891
892 /* Global statistics.*/
893 pOutput->pfnPrintf(pOutput, "*** Global statistics ***\n");
894 rtMemTrackerDumpOneStatRecord(&pTracker->GlobalStats, pOutput, fVerbose);
895 if (fVerbose)
896 pOutput->pfnPrintf(pOutput, " Busy Allocs: %4RU64 Busy Frees: %4RU64 Tags: %3u Users: %3u\n",
897 pTracker->cBusyAllocs, pTracker->cBusyFrees, pTracker->cTags, pTracker->cUsers);
898
899 /* Per tag statistics. */
900 pOutput->pfnPrintf(pOutput, "\n*** Tag statistics ***\n");
901 PRTMEMTRACKERTAG pTag, pNextTag;
902 RTListForEachSafe(&pTracker->TagList, pTag, pNextTag, RTMEMTRACKERTAG, ListEntry)
903 {
904 if ( fVerbose
905 || pTag->Stats.cbAllocated)
906 {
907 pOutput->pfnPrintf(pOutput, "Tag: %s\n", pTag->szTag);
908 rtMemTrackerDumpOneStatRecord(&pTag->Stats, pOutput, fVerbose);
909 if (fVerbose)
910 pOutput->pfnPrintf(pOutput, "\n", pTag->szTag);
911 }
912 }
913
914 /* Per user statistics. */
915 pOutput->pfnPrintf(pOutput, "\n*** User statistics ***\n");
916 PRTMEMTRACKERUSER pCurUser, pNextUser;
917 RTListForEachSafe(&pTracker->UserList, pCurUser, pNextUser, RTMEMTRACKERUSER, ListEntry)
918 {
919 if ( fVerbose
920 || pCurUser->Stats.cbAllocated
921 || pCurUser == pUser)
922 {
923 pOutput->pfnPrintf(pOutput, "User #%u: %s%s (cInTracker=%d)\n",
924 pCurUser->idUser,
925 pCurUser->szName,
926 pUser == pCurUser ? " (me)" : "",
927 pCurUser->cInTracker);
928 rtMemTrackerDumpOneStatRecord(&pCurUser->Stats, pOutput, fVerbose);
929 if (fVerbose)
930 pOutput->pfnPrintf(pOutput, "\n", pTag->szTag);
931 }
932 }
933
934 if (fVerbose)
935 {
936 /* Repeat the global statistics. */
937 pOutput->pfnPrintf(pOutput, "*** Global statistics (reprise) ***\n");
938 rtMemTrackerDumpOneStatRecord(&pTracker->GlobalStats, pOutput, fVerbose);
939 pOutput->pfnPrintf(pOutput, " Busy Allocs: %4RU64 Busy Frees: %4RU64 Tags: %3u Users: %3u\n",
940 pTracker->cBusyAllocs, pTracker->cBusyFrees, pTracker->cTags, pTracker->cUsers);
941 }
942
943 RTSemXRoadsEWLeave(pTracker->hXRoads);
944 rtMemTrackerPutUser(pUser);
945}
946
947
948/**
949 * @callback_method_impl{RTMEMTRACKEROUTPUT::pfnPrintf, Outputting to the release log}
950 */
951static DECLCALLBACK(void) rtMemTrackerDumpLogOutput(PRTMEMTRACKEROUTPUT pThis, const char *pszFormat, ...)
952{
953 NOREF(pThis);
954 va_list va;
955 va_start(va, pszFormat);
956 RTLogPrintfV(pszFormat, va);
957 va_end(va);
958}
959
960
961/**
962 * Internal worker for RTMemTrackerDumpAllToLog and RTMemTrackerDumpAllToLogEx.
963 *
964 * @param pTracker The tracker instance. Can be NULL.
965 */
966static void rtMemTrackerDumpAllToLogEx(PRTMEMTRACKERINT pTracker)
967{
968 RTMEMTRACKEROUTPUT Output;
969 Output.pfnPrintf = rtMemTrackerDumpLogOutput;
970 rtMemTrackerDumpAllWorker(pTracker, &Output);
971}
972
973
974/**
975 * Internal worker for RTMemTrackerDumpStatsToLog and
976 * RTMemTrackerDumpStatsToLogEx.
977 *
978 * @param pTracker The tracker instance. Can be NULL.
979 * @param fVerbose Whether to print all the stats or just the ones
980 * relevant to hunting leaks.
981 */
982static void rtMemTrackerDumpStatsToLogEx(PRTMEMTRACKERINT pTracker, bool fVerbose)
983{
984 RTMEMTRACKEROUTPUT Output;
985 Output.pfnPrintf = rtMemTrackerDumpLogOutput;
986 rtMemTrackerDumpStatsWorker(pTracker, &Output, fVerbose);
987}
988
989
990/**
991 * @callback_method_impl{RTMEMTRACKEROUTPUT::pfnPrintf, Outputting to the release log}
992 */
993static DECLCALLBACK(void) rtMemTrackerDumpLogRelOutput(PRTMEMTRACKEROUTPUT pThis, const char *pszFormat, ...)
994{
995 NOREF(pThis);
996 va_list va;
997 va_start(va, pszFormat);
998 RTLogRelPrintfV(pszFormat, va);
999 va_end(va);
1000}
1001
1002
1003/**
1004 * Internal worker for RTMemTrackerDumpStatsToLog and
1005 * RTMemTrackerDumpStatsToLogEx.
1006 *
1007 * @param pTracker The tracker instance. Can be NULL.
1008 */
1009static void rtMemTrackerDumpAllToLogRelEx(PRTMEMTRACKERINT pTracker)
1010{
1011 RTMEMTRACKEROUTPUT Output;
1012 Output.pfnPrintf = rtMemTrackerDumpLogRelOutput;
1013 rtMemTrackerDumpAllWorker(pTracker, &Output);
1014}
1015
1016
1017/**
1018 * Internal worker for RTMemTrackerDumpStatsToLogRel and
1019 * RTMemTrackerDumpStatsToLogRelEx.
1020 *
1021 * @param pTracker The tracker instance. Can be NULL.
1022 * @param fVerbose Whether to print all the stats or just the ones
1023 * relevant to hunting leaks.
1024 */
1025static void rtMemTrackerDumpStatsToLogRelEx(PRTMEMTRACKERINT pTracker, bool fVerbose)
1026{
1027 RTMEMTRACKEROUTPUT Output;
1028 Output.pfnPrintf = rtMemTrackerDumpLogRelOutput;
1029 rtMemTrackerDumpStatsWorker(pTracker, &Output, fVerbose);
1030}
1031
1032#ifdef IN_RING3
1033
1034/**
1035 * @callback_method_impl{RTMEMTRACKEROUTPUT::pfnPrintf, Outputting to file}
1036 */
1037static DECLCALLBACK(void) rtMemTrackerDumpFileOutput(PRTMEMTRACKEROUTPUT pThis, const char *pszFormat, ...)
1038{
1039 va_list va;
1040 va_start(va, pszFormat);
1041 char szOutput[_4K];
1042 size_t cchOutput = RTStrPrintfV(szOutput, sizeof(szOutput), pszFormat, va);
1043 va_end(va);
1044 RTFileWrite(pThis->uData.hFile, szOutput, cchOutput, NULL);
1045}
1046
1047
1048/**
1049 * Internal work that dumps the memory tracking statistics to a file handle.
1050 *
1051 * @param pTracker The tracker instance. Can be NULL.
1052 * @param fVerbose Whether to print all the stats or just the ones
1053 * relevant to hunting leaks.
1054 * @param hFile The file handle. Can be NIL_RTFILE.
1055 */
1056static void rtMemTrackerDumpStatsToFileHandle(PRTMEMTRACKERINT pTracker, bool fVerbose, RTFILE hFile)
1057{
1058 if (hFile == NIL_RTFILE)
1059 return;
1060 RTMEMTRACKEROUTPUT Output;
1061 Output.pfnPrintf = rtMemTrackerDumpFileOutput;
1062 Output.uData.hFile = hFile;
1063 rtMemTrackerDumpStatsWorker(pTracker, &Output, fVerbose);
1064}
1065
1066
1067/**
1068 * Internal work that dumps all the memory tracking information to a file
1069 * handle.
1070 *
1071 * @param pTracker The tracker instance. Can be NULL.
1072 * @param hFile The file handle. Can be NIL_RTFILE.
1073 */
1074static void rtMemTrackerDumpAllToFileHandle(PRTMEMTRACKERINT pTracker, RTFILE hFile)
1075{
1076 if (hFile == NIL_RTFILE)
1077 return;
1078 RTMEMTRACKEROUTPUT Output;
1079 Output.pfnPrintf = rtMemTrackerDumpFileOutput;
1080 Output.uData.hFile = hFile;
1081 rtMemTrackerDumpAllWorker(pTracker, &Output);
1082}
1083
1084
1085/**
1086 * Internal worker for RTMemTrackerDumpStatsToStdOut and
1087 * RTMemTrackerDumpStatsToStdOutEx.
1088 *
1089 * @param pTracker The tracker instance. Can be NULL.
1090 * @param fVerbose Whether to print all the stats or just the ones
1091 * relevant to hunting leaks.
1092 */
1093static void rtMemTrackerDumpStatsToStdOutEx(PRTMEMTRACKERINT pTracker, bool fVerbose)
1094{
1095 rtMemTrackerDumpStatsToFileHandle(pTracker, fVerbose, rtFileGetStandard(RTHANDLESTD_OUTPUT));
1096}
1097
1098
1099/**
1100 * Internal worker for RTMemTrackerDumpAllToStdOut and
1101 * RTMemTrackerDumpAllToStdOutEx.
1102 *
1103 * @param pTracker The tracker instance. Can be NULL.
1104 */
1105static void rtMemTrackerDumpAllToStdOutEx(PRTMEMTRACKERINT pTracker)
1106{
1107 rtMemTrackerDumpAllToFileHandle(pTracker, rtFileGetStandard(RTHANDLESTD_OUTPUT));
1108}
1109
1110
1111/**
1112 * Internal worker for RTMemTrackerDumpStatsToStdErr and
1113 * RTMemTrackerDumpStatsToStdErrEx.
1114 *
1115 * @param pTracker The tracker instance. Can be NULL.
1116 * @param fVerbose Whether to print all the stats or just the ones
1117 * relevant to hunting leaks.
1118 */
1119static void rtMemTrackerDumpStatsToStdErrEx(PRTMEMTRACKERINT pTracker, bool fVerbose)
1120{
1121 rtMemTrackerDumpStatsToFileHandle(pTracker, fVerbose, rtFileGetStandard(RTHANDLESTD_ERROR));
1122}
1123
1124
1125/**
1126 * Internal worker for RTMemTrackerDumpAllToStdErr and
1127 * RTMemTrackerDumpAllToStdErrEx.
1128 *
1129 * @param pTracker The tracker instance. Can be NULL.
1130 */
1131static void rtMemTrackerDumpAllToStdErrEx(PRTMEMTRACKERINT pTracker)
1132{
1133 rtMemTrackerDumpAllToFileHandle(pTracker, rtFileGetStandard(RTHANDLESTD_ERROR));
1134}
1135
1136
1137/**
1138 * Internal worker for RTMemTrackerDumpStatsToFile and
1139 * RTMemTrackerDumpStatsToFileEx.
1140 *
1141 * @param pTracker The tracker instance. Can be NULL.
1142 * @param fVerbose Whether to print all the stats or just the ones
1143 * relevant to hunting leaks.
1144 * @param pszFilename The name of the output file.
1145 */
1146static void rtMemTrackerDumpStatsToFileEx(PRTMEMTRACKERINT pTracker, bool fVerbose, const char *pszFilename)
1147{
1148 if (!pTracker)
1149 return;
1150
1151 /** @todo this is borked. */
1152 RTFILE hFile;
1153 int rc = RTFileOpen(&hFile, pszFilename,
1154 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
1155 | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
1156 if (RT_FAILURE(rc))
1157 return;
1158 rtMemTrackerDumpStatsToFileHandle(pTracker, fVerbose, hFile);
1159 RTFileClose(hFile);
1160}
1161
1162
1163/**
1164 * Internal worker for RTMemTrackerDumpAllToFile and
1165 * RTMemTrackerDumpAllToFileEx.
1166 *
1167 * @param pTracker The tracker instance. Can be NULL.
1168 * @param pszFilename The name of the output file.
1169 */
1170static void rtMemTrackerDumpAllToFileEx(PRTMEMTRACKERINT pTracker, const char *pszFilename)
1171{
1172 if (!pTracker)
1173 return;
1174
1175 RTFILE hFile;
1176 int rc = RTFileOpen(&hFile, pszFilename,
1177 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_NONE
1178 | (0600 << RTFILE_O_CREATE_MODE_SHIFT));
1179 if (RT_FAILURE(rc))
1180 return;
1181 rtMemTrackerDumpAllToFileHandle(pTracker, hFile);
1182 RTFileClose(hFile);
1183}
1184
1185#endif /* IN_RING3 */
1186
1187
1188
1189/*
1190 *
1191 *
1192 * Default tracker.
1193 * Default tracker.
1194 * Default tracker.
1195 * Default tracker.
1196 * Default tracker.
1197 *
1198 *
1199 */
1200
1201
1202/**
1203 * Handles the lazy initialization when g_pDefaultTracker is NULL.
1204 *
1205 * @returns The newly created default tracker or NULL.
1206 */
1207static PRTMEMTRACKERINT rtMemTrackerLazyInitDefaultTracker(void)
1208{
1209 /*
1210 * Don't attempt initialize before RTThread has been initialized.
1211 */
1212 if (!RTThreadIsInitialized())
1213 return NULL;
1214
1215 /*
1216 * Only one initialization at a time. For now we'll ASSUME that there
1217 * won't be thread ending up here at the same time, only the same
1218 * reentering from the allocator when creating the tracker.
1219 */
1220 static volatile bool s_fInitialized = false;
1221 if (ASMAtomicXchgBool(&s_fInitialized, true))
1222 return g_pDefaultTracker;
1223
1224 PRTMEMTRACKERINT pTracker = NULL; /* gcc sucks. */
1225 int rc = rtMemTrackerCreate(&pTracker);
1226 if (RT_FAILURE(rc))
1227 return NULL;
1228
1229 g_pDefaultTracker = pTracker;
1230 return pTracker;
1231}
1232
1233
1234
1235RTDECL(void *) RTMemTrackerHdrAlloc(void *pv, size_t cb, const char *pszTag, void *pvCaller, RTMEMTRACKERMETHOD enmMethod)
1236{
1237 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1238 if (RT_UNLIKELY(!pTracker))
1239 pTracker = rtMemTrackerLazyInitDefaultTracker();
1240 return rtMemTrackerHdrAllocEx(pTracker, pv, cb, pszTag, pvCaller, enmMethod);
1241}
1242
1243
1244RTDECL(void *) RTMemTrackerHdrReallocPrep(void *pvOldUser, size_t cbOldUser, const char *pszTag, void *pvCaller)
1245{
1246 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1247 if (RT_UNLIKELY(!pTracker))
1248 pTracker = rtMemTrackerLazyInitDefaultTracker();
1249 return rtMemTrackerHdrReallocPrepEx(pTracker, pvOldUser, cbOldUser, pszTag, pvCaller);
1250}
1251
1252
1253RTDECL(void *) RTMemTrackerHdrReallocDone(void *pvNew, size_t cbNewUser, void *pvOld, const char *pszTag, void *pvCaller)
1254{
1255 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1256 if (RT_UNLIKELY(!pTracker))
1257 pTracker = rtMemTrackerLazyInitDefaultTracker();
1258 return rtMemTrackerHdrReallocDoneEx(pTracker, pvNew, cbNewUser, pvOld, pszTag, pvCaller);
1259}
1260
1261
1262RTDECL(void *) RTMemTrackerHdrFree(void *pvUser, size_t cbUser, const char *pszTag, void *pvCaller, RTMEMTRACKERMETHOD enmMethod)
1263{
1264 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1265 if (RT_UNLIKELY(!pTracker))
1266 pTracker = rtMemTrackerLazyInitDefaultTracker();
1267 return rtMemTrackerHdrFreeEx(pTracker, pvUser, cbUser, pszTag, pvCaller, enmMethod);
1268}
1269
1270
1271RTDECL(void) RTMemTrackerDumpAllToLog(void)
1272{
1273 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1274 if (RT_UNLIKELY(!pTracker))
1275 pTracker = rtMemTrackerLazyInitDefaultTracker();
1276 return rtMemTrackerDumpAllToLogEx(pTracker);
1277}
1278
1279
1280RTDECL(void) RTMemTrackerDumpAllToLogRel(void)
1281{
1282 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1283 if (RT_UNLIKELY(!pTracker))
1284 pTracker = rtMemTrackerLazyInitDefaultTracker();
1285 return rtMemTrackerDumpAllToLogRelEx(pTracker);
1286}
1287
1288
1289RTDECL(void) RTMemTrackerDumpAllToStdOut(void)
1290{
1291 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1292 if (RT_UNLIKELY(!pTracker))
1293 pTracker = rtMemTrackerLazyInitDefaultTracker();
1294 return rtMemTrackerDumpAllToStdOutEx(pTracker);
1295}
1296
1297
1298RTDECL(void) RTMemTrackerDumpAllToStdErr(void)
1299{
1300 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1301 if (RT_UNLIKELY(!pTracker))
1302 pTracker = rtMemTrackerLazyInitDefaultTracker();
1303 return rtMemTrackerDumpAllToStdErrEx(pTracker);
1304}
1305
1306
1307RTDECL(void) RTMemTrackerDumpAllToFile(const char *pszFilename)
1308{
1309 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1310 if (RT_UNLIKELY(!pTracker))
1311 pTracker = rtMemTrackerLazyInitDefaultTracker();
1312 return rtMemTrackerDumpAllToFileEx(pTracker, pszFilename);
1313}
1314
1315
1316RTDECL(void) RTMemTrackerDumpStatsToLog(bool fVerbose)
1317{
1318 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1319 if (RT_UNLIKELY(!pTracker))
1320 pTracker = rtMemTrackerLazyInitDefaultTracker();
1321 return rtMemTrackerDumpStatsToLogEx(pTracker, fVerbose);
1322}
1323
1324
1325RTDECL(void) RTMemTrackerDumpStatsToLogRel(bool fVerbose)
1326{
1327 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1328 if (RT_UNLIKELY(!pTracker))
1329 pTracker = rtMemTrackerLazyInitDefaultTracker();
1330 return rtMemTrackerDumpStatsToLogRelEx(pTracker, fVerbose);
1331}
1332
1333
1334RTDECL(void) RTMemTrackerDumpStatsToStdOut(bool fVerbose)
1335{
1336 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1337 if (RT_UNLIKELY(!pTracker))
1338 pTracker = rtMemTrackerLazyInitDefaultTracker();
1339 return rtMemTrackerDumpStatsToStdOutEx(pTracker, fVerbose);
1340}
1341
1342
1343RTDECL(void) RTMemTrackerDumpStatsToStdErr(bool fVerbose)
1344{
1345 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1346 if (RT_UNLIKELY(!pTracker))
1347 pTracker = rtMemTrackerLazyInitDefaultTracker();
1348 return rtMemTrackerDumpStatsToStdErrEx(pTracker, fVerbose);
1349}
1350
1351
1352RTDECL(void) RTMemTrackerDumpStatsToFile(bool fVerbose, const char *pszFilename)
1353{
1354 PRTMEMTRACKERINT pTracker = g_pDefaultTracker;
1355 if (RT_UNLIKELY(!pTracker))
1356 pTracker = rtMemTrackerLazyInitDefaultTracker();
1357 return rtMemTrackerDumpStatsToFileEx(pTracker, fVerbose, pszFilename);
1358}
1359
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use