VirtualBox

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

Last change on this file since 75244 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette