VirtualBox

Ignore:
Timestamp:
Apr 6, 2011 7:46:15 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
71042
Message:

IPRT: Implemented the memory tracker.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • TabularUnified trunk/src/VBox/Runtime/common/alloc/memtracker.cpp

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

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