VirtualBox

Changeset 89682 in vbox for trunk/src/VBox/VMM


Ignore:
Timestamp:
Jun 14, 2021 2:29:35 PM (4 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
145130
Message:

VMM/DBGFR3SampleReport: More work on the sample creation code, bugref:10025

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/VMM/VMMR3/DBGFR3SampleReport.cpp

    r89625 r89682  
    3838#include <iprt/semaphore.h>
    3939#include <iprt/list.h>
     40#include <iprt/mem.h>
    4041#include <iprt/time.h>
    4142#include <iprt/timer.h>
     
    4748*********************************************************************************************************************************/
    4849
    49 
     50/** Maximum stack frame depth. */
     51#define DBGF_SAMPLE_REPORT_FRAME_DEPTH_MAX 64
    5052
    5153/*********************************************************************************************************************************
    5254*   Structures and Typedefs                                                                                                      *
    5355*********************************************************************************************************************************/
     56
     57/**
     58 * Sample report state.
     59 */
     60typedef enum DBGFSAMPLEREPORTSTATE
     61{
     62    /** Invalid state do not use. */
     63    DBGFSAMPLEREPORTSTATE_INVALID = 0,
     64    /** The sample report is ready to run. */
     65    DBGFSAMPLEREPORTSTATE_READY,
     66    /** The sampple process is running currently. */
     67    DBGFSAMPLEREPORTSTATE_RUNNING,
     68    /** The sample process is about to stop. */
     69    DBGFSAMPLEREPORTSTATE_STOPPING,
     70    /** 32bit hack. */
     71    DBGFSAMPLEREPORTSTATE_32BIT_HACK = 0x7fffffff
     72} DBGFSAMPLEREPORTSTATE;
    5473
    5574/** Pointer to a single sample frame. */
     
    98117    /** The user mode VM handle. */
    99118    PUVM                             pUVM;
     119    /** State the sample report is currently in. */
     120    volatile DBGFSAMPLEREPORTSTATE   enmState;
    100121    /** Flags passed during report creation. */
    101122    uint32_t                         fFlags;
    102123    /** The timer handle for the sample report collector. */
    103124    PRTTIMER                         hTimer;
     125    /** The sample interval in microseconds. */
     126    uint32_t                         cSampleIntervalUs;
     127    /** THe progress callback if set. */
     128    PFNDBGFPROGRESS                  pfnProgress;
     129    /** Opaque user data passed with the progress callback. */
     130    void                             *pvProgressUser;
     131    /** Number of microseconds left for sampling. */
     132    uint64_t                         cSampleUsLeft;
    104133    /** Array of per VCPU samples collected. */
    105134    DBGFSAMPLEREPORTVCPU             aCpus[1];
     
    111140
    112141
     142/**
     143 * Structure to pass to DBGFR3Info() and for doing all other
     144 * output during fatal dump.
     145 */
     146typedef struct DBGFSAMPLEREPORTINFOHLP
     147{
     148    /** The helper core. */
     149    DBGFINFOHLP Core;
     150    /** Pointer to the allocated character buffer. */
     151    char        *pachBuf;
     152    /** Number of bytes allocated for the character buffer. */
     153    size_t      cbBuf;
     154    /** Offset into the character buffer. */
     155    size_t      offBuf;
     156} DBGFSAMPLEREPORTINFOHLP, *PDBGFSAMPLEREPORTINFOHLP;
     157/** Pointer to a DBGFSAMPLEREPORTINFOHLP structure. */
     158typedef const DBGFSAMPLEREPORTINFOHLP *PCDBGFSAMPLEREPORTINFOHLP;
     159
     160
    113161/*********************************************************************************************************************************
    114162*   Internal Functions                                                                                                           *
     
    116164
    117165/**
     166 * Print formatted string.
     167 *
     168 * @param   pHlp        Pointer to this structure.
     169 * @param   pszFormat   The format string.
     170 * @param   ...         Arguments.
     171 */
     172static DECLCALLBACK(void) dbgfR3SampleReportInfoHlp_pfnPrintf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
     173{
     174    va_list args;
     175    va_start(args, pszFormat);
     176    pHlp->pfnPrintfV(pHlp, pszFormat, args);
     177    va_end(args);
     178}
     179
     180
     181/**
     182 * Print formatted string.
     183 *
     184 * @param   pHlp        Pointer to this structure.
     185 * @param   pszFormat   The format string.
     186 * @param   args        Argument list.
     187 */
     188static DECLCALLBACK(void) dbgfR3SampleReportInfoHlp_pfnPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list args)
     189{
     190    PDBGFSAMPLEREPORTINFOHLP pMyHlp = (PDBGFSAMPLEREPORTINFOHLP)pHlp;
     191
     192    va_list args2;
     193    va_copy(args2, args);
     194    ssize_t cch = RTStrPrintf2V(&pMyHlp->pachBuf[pMyHlp->offBuf], pMyHlp->cbBuf - pMyHlp->offBuf, pszFormat, args2);
     195    if (cch < 0)
     196    {
     197        /* Try increase the buffer. */
     198        char *pachBufNew = (char *)RTMemRealloc(pMyHlp->pachBuf, pMyHlp->cbBuf + RT_MAX(_4K, -cch));
     199        if (pachBufNew)
     200        {
     201            pMyHlp->pachBuf = pachBufNew;
     202            pMyHlp->cbBuf  += RT_MAX(_4K, -cch);
     203            cch = RTStrPrintf2V(&pMyHlp->pachBuf[pMyHlp->offBuf], pMyHlp->cbBuf - pMyHlp->offBuf, pszFormat, args2);
     204            Assert(cch > 0);
     205            pMyHlp->offBuf += cch;
     206        }
     207    }
     208    else
     209        pMyHlp->offBuf += cch;
     210    va_end(args2);
     211}
     212
     213
     214/**
     215 * Initializes the sample report output helper.
     216 *
     217 * @param   pHlp        The structure to initialize.
     218 */
     219static void dbgfR3SampleReportInfoHlpInit(PDBGFSAMPLEREPORTINFOHLP pHlp)
     220{
     221    RT_BZERO(pHlp, sizeof(*pHlp));
     222
     223    pHlp->Core.pfnPrintf      = dbgfR3SampleReportInfoHlp_pfnPrintf;
     224    pHlp->Core.pfnPrintfV     = dbgfR3SampleReportInfoHlp_pfnPrintfV;
     225    pHlp->Core.pfnGetOptError = DBGFR3InfoGenricGetOptError;
     226
     227    pHlp->pachBuf = (char *)RTMemAllocZ(_4K);
     228    if (pHlp->pachBuf)
     229        pHlp->cbBuf = _4K;
     230}
     231
     232
     233/**
     234 * Deletes the sample report output helper.
     235 *
     236 * @param   pHlp        The structure to delete.
     237 */
     238static void dbgfR3SampleReportInfoHlpDelete(PDBGFSAMPLEREPORTINFOHLP pHlp)
     239{
     240    if (pHlp->pachBuf)
     241        RTMemFree(pHlp->pachBuf);
     242}
     243
     244
     245/**
     246 * Frees the given frame and all its descendants.
     247 *
     248 * @returns nothing.
     249 * @param   pFrame                  The frame to free.
     250 */
     251static void dbgfR3SampleReportFrameFree(PDBGFSAMPLEFRAME pFrame)
     252{
     253    for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
     254        dbgfR3SampleReportFrameFree(&pFrame->paFrames[i]); /** @todo Recursion... */
     255
     256    MMR3HeapFree(pFrame->paFrames);
     257    memset(pFrame, 0, sizeof(*pFrame));
     258}
     259
     260
     261/**
    118262 * Destroys the given sample report freeing all allocated resources.
    119263 *
     
    123267static void dbgfR3SampleReportDestroy(PDBGFSAMPLEREPORTINT pThis)
    124268{
    125     int rc = RTTimerDestroy(pThis->hTimer); AssertRC(rc); RT_NOREF(rc);
     269    for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
     270        dbgfR3SampleReportFrameFree(&pThis->aCpus[i].FrameRoot);
    126271    MMR3HeapFree(pThis);
    127272}
    128273
    129274
     275/**
     276 * Returns the frame belonging to the given address or NULL if not found.
     277 *
     278 * @returns Pointer to the descendant frame or NULL if not found.
     279 * @param   pFrame                  The frame to look for descendants with the matching address.
     280 * @param   pAddr                   The guest address to search for.
     281 */
    130282static PDBGFSAMPLEFRAME dbgfR3SampleReportFrameFindByAddr(PCDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
    131283{
     
    138290
    139291
     292/**
     293 * Adds the given address to as a descendant to the given frame.
     294 *
     295 * @returns Pointer to the newly inserted frame identified by the given address.
     296 * @param   pUVM                    The usermode VM handle.
     297 * @param   pFrame                  The frame to add the new one to as a descendant.
     298 * @param   pAddr                   The guest address to add.
     299 */
    140300static PDBGFSAMPLEFRAME dbgfR3SampleReportAddFrameByAddr(PUVM pUVM, PDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
    141301{
     
    167327
    168328/**
    169  * Worker for dbgfR3SampleReportTakeSample(), doing the work in an EMT rendezvous point on
    170  * each CCPU.
    171  *
    172  * @returns Strict VBox status code.
    173  * @param   pVM                     The VM instance data.
    174  * @param   pVCpu                   The virtual CPU we execute on.
    175  * @param   pvUser                  Opaque user data.
    176  */
    177 static DECLCALLBACK(VBOXSTRICTRC) dbgfR3SampleReportSample(PVM pVM, PVMCPU pVCpu, void *pvUser)
    178 {
    179     RT_NOREF(pVM);
    180     PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
    181 
    182     PCDBGFSTACKFRAME pFrameFirst;
    183     int rc = DBGFR3StackWalkBegin(pThis->pUVM, pVCpu->idCpu, DBGFCODETYPE_GUEST, &pFrameFirst);
    184     if (RT_SUCCESS(rc))
    185     {
    186         /* As we want the final call stacks to be from top to bottom we have to reverse the addresses. */
    187         DBGFADDRESS aFrameAddresses[512];
    188         uint32_t idxFrameMax = 0;
    189 
    190         PDBGFSAMPLEFRAME pFrame = &pThis->aCpus[pVCpu->idCpu].FrameRoot;
    191         pFrame->cSamples++;
    192 
    193         for (PCDBGFSTACKFRAME pStackFrame = pFrameFirst;
    194              pStackFrame && idxFrameMax < RT_ELEMENTS(aFrameAddresses);
    195              pStackFrame = DBGFR3StackWalkNext(pStackFrame))
    196         {
    197             if (pThis->fFlags & DBGF_SAMPLE_REPORT_F_UPSIDE_DOWN)
    198             {
    199                 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &pStackFrame->AddrPC);
    200                 if (!pFrameNext)
    201                     pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &pStackFrame->AddrPC);
    202                 else
    203                     pFrameNext->cSamples++;
    204 
    205                 pFrame = pFrameNext;
    206             }
    207             else
    208                 aFrameAddresses[idxFrameMax++] = pStackFrame->AddrPC;
    209         }
    210 
    211         DBGFR3StackWalkEnd(pFrameFirst);
    212 
    213         if (!(pThis->fFlags & DBGF_SAMPLE_REPORT_F_UPSIDE_DOWN))
    214         {
    215             /* Walk the frame stack backwards and construct the call stack. */
    216             while (idxFrameMax--)
    217             {
    218                 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &aFrameAddresses[idxFrameMax]);
    219                 if (!pFrameNext)
    220                     pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &aFrameAddresses[idxFrameMax]);
    221                 else
    222                     pFrameNext->cSamples++;
    223 
    224                 pFrame = pFrameNext;
    225             }
    226         }
    227     }
    228     else
    229         LogRelMax(10, ("Sampling guest stack on VCPU %u failed with rc=%Rrc\n", pVCpu->idCpu, rc));
    230 
    231     return VINF_SUCCESS;
    232 }
    233 
    234 
    235 /**
    236  * @copydoc FNRTTIMER
    237  */
    238 static DECLCALLBACK(void) dbgfR3SampleReportTakeSample(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
    239 {
    240     RT_NOREF(pTimer, iTick);
    241     PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
    242 
    243     int rc = VMMR3EmtRendezvous(pThis->pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_ALL_AT_ONCE,
    244                                  dbgfR3SampleReportSample, pThis);
    245     RT_NOREF(rc);
    246 }
    247 
    248 
    249 /**
    250  * Creates a new sample report instance for the specified VM.
    251  *
    252  * @returns VBox status code.
     329 * Dumps a single given frame to the release log.
     330 *
     331 * @returns nothing.
    253332 * @param   pUVM                    The usermode VM handle.
    254  * @param   cSampleIntervalUs       The sample interval in micro seconds.
    255  * @param   fFlags                  Combination of DBGF_SAMPLE_REPORT_F_XXX.
    256  * @param   phSample                Where to return the handle to the sample report on success.
    257  */
    258 VMMR3DECL(int) DBGFR3SampleReportCreate(PUVM pUVM, uint32_t cSampleIntervalUs, uint32_t fFlags, PDBGFSAMPLEREPORT phSample)
    259 {
    260     UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
    261     AssertReturn(!(fFlags & ~DBGF_AMPLE_REPORT_F_VALID_MASK), VERR_INVALID_PARAMETER);
    262     AssertPtrReturn(phSample, VERR_INVALID_POINTER);
    263 
    264     int rc = VINF_SUCCESS;
    265     PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF,
    266                                                                        RT_UOFFSETOF_DYN(DBGFSAMPLEREPORTINT, aCpus[pUVM->cCpus]));
    267     if (RT_LIKELY(pThis))
    268     {
    269         pThis->cRefs  = 1;
    270         pThis->pUVM   = pUVM;
    271         pThis->fFlags = fFlags;
    272 
    273         for (uint32_t i = 0; i < pUVM->cCpus; i++)
    274         {
    275             pThis->aCpus[i].FrameRoot.paFrames     = NULL;
    276             pThis->aCpus[i].FrameRoot.cSamples     = 0;
    277             pThis->aCpus[i].FrameRoot.cFramesValid = 0;
    278             pThis->aCpus[i].FrameRoot.cFramesMax   = 0;
    279         }
    280 
    281         rc = RTTimerCreateEx(&pThis->hTimer, (uint64_t)cSampleIntervalUs * 1000,
    282                              RTTIMER_FLAGS_CPU_ANY | RTTIMER_FLAGS_HIGH_RES,
    283                              dbgfR3SampleReportTakeSample, pThis);
    284         if (RT_SUCCESS(rc))
    285         {
    286             *phSample = pThis;
    287             return VINF_SUCCESS;
    288         }
    289 
    290         MMR3HeapFree(pThis);
    291     }
    292     else
    293         rc = VERR_NO_MEMORY;
    294 
    295     return rc;
    296 }
    297 
    298 
    299 /**
    300  * Retains a reference to the given sample report handle.
    301  *
    302  * @returns New reference count.
    303  * @param   hSample                 Sample report handle.
    304  */
    305 VMMR3DECL(uint32_t) DBGFR3SampleReportRetain(DBGFSAMPLEREPORT hSample)
    306 {
    307     PDBGFSAMPLEREPORTINT pThis = hSample;
    308     AssertPtrReturn(pThis, UINT32_MAX);
    309 
    310     uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
    311     AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
    312     return cRefs;
    313 }
    314 
    315 
    316 /**
    317  * Release a given sample report handle reference.
    318  *
    319  * @returns New reference count, on 0 the sample report instance is destroyed.
    320  * @param   hSample                 Sample report handle.
    321  */
    322 VMMR3DECL(uint32_t) DBGFR3SampleReportRelease(DBGFSAMPLEREPORT hSample)
    323 {
    324     PDBGFSAMPLEREPORTINT pThis = hSample;
    325     if (!pThis)
    326         return 0;
    327     AssertPtrReturn(pThis, UINT32_MAX);
    328 
    329     uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
    330     AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
    331     if (cRefs == 0)
    332         dbgfR3SampleReportDestroy(pThis);
    333     return cRefs;
    334 }
    335 
    336 
    337 /**
    338  * Starts collecting samples for the given sample report.
    339  *
    340  * @returns VBox status code.
    341  * @param   hSample                 Sample report handle.
    342  */
    343 VMMR3DECL(int) DBGFR3SampleReportStart(DBGFSAMPLEREPORT hSample)
    344 {
    345     PDBGFSAMPLEREPORTINT pThis = hSample;
    346 
    347     /* Try to detect the guest OS first so we can get more accurate symbols and addressing. */
    348     char szName[64];
    349     int rc = DBGFR3OSDetect(pThis->pUVM, &szName[0], sizeof(szName));
    350     if (RT_SUCCESS(rc))
    351     {
    352         LogRel(("DBGF/SampleReport: Detected guest OS \"%s\"\n", szName));
    353         char szVersion[512];
    354         int rc2 = DBGFR3OSQueryNameAndVersion(pThis->pUVM, NULL, 0, szVersion, sizeof(szVersion));
    355         if (RT_SUCCESS(rc2))
    356             LogRel(("DBGF/SampleReport: Version : \"%s\"\n", szVersion));
    357     }
    358     else
    359         LogRel(("DBGF/SampleReport: Couldn't detect guest operating system rc=%Rcr\n", rc));
    360 
    361     rc = RTTimerStart(pThis->hTimer, 0 /*u64First*/);
    362     return rc;
    363 }
    364 
    365 
     333 * @param   pFrame                  The frame to dump.
     334 * @param   idxFrame                The frame number.
     335 */
    366336static void dbgfR3SampleReportDumpFrame(PUVM pUVM, PCDBGFSAMPLEFRAME pFrame, uint32_t idxFrame)
    367337{
     
    398368
    399369/**
    400  * Stops collecting samples for the given sample report.
     370 * Worker for dbgfR3SampleReportTakeSample(), doing the work in an EMT rendezvous point on
     371 * each VCPU.
     372 *
     373 * @returns Strict VBox status code.
     374 * @param   pVM                     The VM instance data.
     375 * @param   pVCpu                   The virtual CPU we execute on.
     376 * @param   pvUser                  Opaque user data.
     377 */
     378static DECLCALLBACK(VBOXSTRICTRC) dbgfR3SampleReportSample(PVM pVM, PVMCPU pVCpu, void *pvUser)
     379{
     380    RT_NOREF(pVM);
     381    PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
     382
     383    PCDBGFSTACKFRAME pFrameFirst;
     384    int rc = DBGFR3StackWalkBegin(pThis->pUVM, pVCpu->idCpu, DBGFCODETYPE_GUEST, &pFrameFirst);
     385    if (RT_SUCCESS(rc))
     386    {
     387        DBGFADDRESS aFrameAddresses[DBGF_SAMPLE_REPORT_FRAME_DEPTH_MAX];
     388        uint32_t idxFrame = 0;
     389
     390        PDBGFSAMPLEFRAME pFrame = &pThis->aCpus[pVCpu->idCpu].FrameRoot;
     391        pFrame->cSamples++;
     392
     393        for (PCDBGFSTACKFRAME pStackFrame = pFrameFirst;
     394             pStackFrame && idxFrame < RT_ELEMENTS(aFrameAddresses);
     395             pStackFrame = DBGFR3StackWalkNext(pStackFrame))
     396        {
     397            if (pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE)
     398            {
     399                PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &pStackFrame->AddrPC);
     400                if (!pFrameNext)
     401                    pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &pStackFrame->AddrPC);
     402                else
     403                    pFrameNext->cSamples++;
     404
     405                pFrame = pFrameNext;
     406            }
     407            else
     408                aFrameAddresses[idxFrame] = pStackFrame->AddrPC;
     409
     410            idxFrame++;
     411        }
     412
     413        DBGFR3StackWalkEnd(pFrameFirst);
     414
     415        if (!(pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE))
     416        {
     417            /* Walk the frame stack backwards and construct the call stack. */
     418            while (idxFrame--)
     419            {
     420                PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &aFrameAddresses[idxFrame]);
     421                if (!pFrameNext)
     422                    pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &aFrameAddresses[idxFrame]);
     423                else
     424                    pFrameNext->cSamples++;
     425
     426                pFrame = pFrameNext;
     427            }
     428        }
     429    }
     430    else
     431        LogRelMax(10, ("Sampling guest stack on VCPU %u failed with rc=%Rrc\n", pVCpu->idCpu, rc));
     432
     433    if (pVCpu->idCpu == 0)
     434    {
     435        /* Destroy the timer if requested. */
     436        if (ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_STOPPING)
     437        {
     438            rc = RTTimerStop(pThis->hTimer); AssertRC(rc); RT_NOREF(rc);
     439            rc = RTTimerDestroy(pThis->hTimer); AssertRC(rc); RT_NOREF(rc);
     440            pThis->hTimer = NULL;
     441
     442#if 1
     443            /* Some early dump code. */
     444            for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
     445            {
     446                PCDBGFSAMPLEREPORTVCPU pSampleVCpu = &pThis->aCpus[i];
     447
     448                LogRel(("Sample report for vCPU %u:\n", i));
     449                dbgfR3SampleReportDumpFrame(pThis->pUVM, &pSampleVCpu->FrameRoot, 0);
     450            }
     451
     452            DBGFSAMPLEREPORTINFOHLP Hlp;
     453            PCDBGFINFOHLP           pHlp = &Hlp.Core;
     454
     455            dbgfR3SampleReportInfoHlpInit(&Hlp);
     456
     457            /* Shameless copy from VMMGuruMeditation.cpp */
     458            static struct
     459            {
     460                const char *pszInfo;
     461                const char *pszArgs;
     462            } const     aInfo[] =
     463            {
     464                { "mappings",        NULL },
     465                { "mode",            "all" },
     466                { "handlers",        "phys virt hyper stats" },
     467                { "timers",          NULL },
     468                { "activetimers",    NULL },
     469            };
     470            for (unsigned i = 0; i < RT_ELEMENTS(aInfo); i++)
     471            {
     472                pHlp->pfnPrintf(pHlp,
     473                                "!!\n"
     474                                "!! {%s, %s}\n"
     475                                "!!\n",
     476                                aInfo[i].pszInfo, aInfo[i].pszArgs);
     477                DBGFR3Info(pVM->pUVM, aInfo[i].pszInfo, aInfo[i].pszArgs, pHlp);
     478            }
     479
     480            /* All other info items */
     481            DBGFR3InfoMulti(pVM,
     482                            "*",
     483                            "mappings|hma|cpum|cpumguest|cpumguesthwvirt|cpumguestinstr|cpumhyper|cpumhost|cpumvmxfeat|mode|cpuid"
     484                            "|pgmpd|pgmcr3|timers|activetimers|handlers|help|cfgm",
     485                            "!!\n"
     486                            "!! {%s}\n"
     487                            "!!\n",
     488                            pHlp);
     489
     490
     491            /* done */
     492            pHlp->pfnPrintf(pHlp,
     493                            "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
     494
     495            LogRel(("%s", Hlp.pachBuf));
     496            dbgfR3SampleReportInfoHlpDelete(&Hlp);
     497#endif
     498
     499            if (pThis->pfnProgress)
     500            {
     501                pThis->pfnProgress(pThis->pvProgressUser, 100);
     502                pThis->pfnProgress    = NULL;
     503                pThis->pvProgressUser = NULL;
     504            }
     505
     506            ASMAtomicXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY);
     507            DBGFR3SampleReportRelease(pThis);
     508        }
     509    }
     510
     511    return VINF_SUCCESS;
     512}
     513
     514
     515/**
     516 * @copydoc FNRTTIMER
     517 */
     518static DECLCALLBACK(void) dbgfR3SampleReportTakeSample(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
     519{
     520    RT_NOREF(pTimer);
     521    PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
     522
     523    if (pThis->cSampleUsLeft != UINT32_MAX)
     524    {
     525        int rc = VINF_SUCCESS;
     526        uint64_t cUsSampled = iTick * pThis->cSampleIntervalUs; /** @todo Wrong if the timer resolution is different from what we've requested. */
     527
     528        /* Update progress. */
     529        if (pThis->pfnProgress)
     530            rc = pThis->pfnProgress(pThis->pvProgressUser, cUsSampled * 99 / pThis->cSampleUsLeft);
     531
     532        if (   cUsSampled >= pThis->cSampleUsLeft
     533            || rc == VERR_DBGF_CANCELLED)
     534        {
     535            /*
     536             * Let the EMTs do one last round in order to be able to destroy the timer (can't do this on the timer thread)
     537             * and gather information from the devices.
     538             */
     539            ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
     540                                DBGFSAMPLEREPORTSTATE_RUNNING);
     541        }
     542    }
     543
     544    int rc = VMMR3EmtRendezvous(pThis->pUVM->pVM, VMMEMTRENDEZVOUS_FLAGS_TYPE_DESCENDING,
     545                                dbgfR3SampleReportSample, pThis);
     546    AssertRC(rc); RT_NOREF(rc);
     547}
     548
     549
     550/**
     551 * Creates a new sample report instance for the specified VM.
     552 *
     553 * @returns VBox status code.
     554 * @param   pUVM                    The usermode VM handle.
     555 * @param   cSampleIntervalUs       The sample interval in micro seconds.
     556 * @param   fFlags                  Combination of DBGF_SAMPLE_REPORT_F_XXX.
     557 * @param   phSample                Where to return the handle to the sample report on success.
     558 */
     559VMMR3DECL(int) DBGFR3SampleReportCreate(PUVM pUVM, uint32_t cSampleIntervalUs, uint32_t fFlags, PDBGFSAMPLEREPORT phSample)
     560{
     561    UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
     562    AssertReturn(!(fFlags & ~DBGF_SAMPLE_REPORT_F_VALID_MASK), VERR_INVALID_PARAMETER);
     563    AssertPtrReturn(phSample, VERR_INVALID_POINTER);
     564
     565    int rc = VINF_SUCCESS;
     566    PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF,
     567                                                                       RT_UOFFSETOF_DYN(DBGFSAMPLEREPORTINT, aCpus[pUVM->cCpus]));
     568    if (RT_LIKELY(pThis))
     569    {
     570        pThis->cRefs             = 1;
     571        pThis->pUVM              = pUVM;
     572        pThis->fFlags            = fFlags;
     573        pThis->cSampleIntervalUs = cSampleIntervalUs;
     574        pThis->enmState          = DBGFSAMPLEREPORTSTATE_READY;
     575
     576        for (uint32_t i = 0; i < pUVM->cCpus; i++)
     577        {
     578            pThis->aCpus[i].FrameRoot.paFrames     = NULL;
     579            pThis->aCpus[i].FrameRoot.cSamples     = 0;
     580            pThis->aCpus[i].FrameRoot.cFramesValid = 0;
     581            pThis->aCpus[i].FrameRoot.cFramesMax   = 0;
     582        }
     583
     584        *phSample = pThis;
     585        return VINF_SUCCESS;
     586
     587        MMR3HeapFree(pThis);
     588    }
     589    else
     590        rc = VERR_NO_MEMORY;
     591
     592    return rc;
     593}
     594
     595
     596/**
     597 * Retains a reference to the given sample report handle.
     598 *
     599 * @returns New reference count.
     600 * @param   hSample                 Sample report handle.
     601 */
     602VMMR3DECL(uint32_t) DBGFR3SampleReportRetain(DBGFSAMPLEREPORT hSample)
     603{
     604    PDBGFSAMPLEREPORTINT pThis = hSample;
     605    AssertPtrReturn(pThis, UINT32_MAX);
     606
     607    uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
     608    AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
     609    return cRefs;
     610}
     611
     612
     613/**
     614 * Release a given sample report handle reference.
     615 *
     616 * @returns New reference count, on 0 the sample report instance is destroyed.
     617 * @param   hSample                 Sample report handle.
     618 *
     619 * @note Can't be called from the progress callback passed during creation.
     620 */
     621VMMR3DECL(uint32_t) DBGFR3SampleReportRelease(DBGFSAMPLEREPORT hSample)
     622{
     623    PDBGFSAMPLEREPORTINT pThis = hSample;
     624    if (!pThis)
     625        return 0;
     626    AssertPtrReturn(pThis, UINT32_MAX);
     627    AssertReturn(ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_READY,
     628                 VERR_INVALID_STATE);
     629
     630    uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
     631    AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
     632    if (cRefs == 0)
     633        dbgfR3SampleReportDestroy(pThis);
     634    return cRefs;
     635}
     636
     637
     638/**
     639 * Starts collecting samples for the given sample report.
    401640 *
    402641 * @returns VBox status code.
    403642 * @param   hSample                 Sample report handle.
     643 * @param   cSampleUs               Number of microseconds to sample at the interval given during creation.
     644 *                                  Use UINT32_MAX to sample for an indefinite amount of time.
     645 * @param   pfnProgress             Optional progress callback.
     646 * @param   pvUser                  Opaque user data to pass to the progress callback.
     647 */
     648VMMR3DECL(int) DBGFR3SampleReportStart(DBGFSAMPLEREPORT hSample, uint64_t cSampleUs, PFNDBGFPROGRESS pfnProgress, void *pvUser)
     649{
     650    PDBGFSAMPLEREPORTINT pThis = hSample;
     651
     652    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     653    AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_RUNNING, DBGFSAMPLEREPORTSTATE_READY),
     654                 VERR_INVALID_STATE);
     655
     656    pThis->pfnProgress    = pfnProgress;
     657    pThis->pvProgressUser = pvUser;
     658    pThis->cSampleUsLeft  = cSampleUs;
     659
     660    /* Try to detect the guest OS first so we can get more accurate symbols and addressing. */
     661    char szName[64];
     662    int rc = DBGFR3OSDetect(pThis->pUVM, &szName[0], sizeof(szName));
     663    if (RT_SUCCESS(rc))
     664    {
     665        LogRel(("DBGF/SampleReport: Detected guest OS \"%s\"\n", szName));
     666        char szVersion[512];
     667        int rc2 = DBGFR3OSQueryNameAndVersion(pThis->pUVM, NULL, 0, szVersion, sizeof(szVersion));
     668        if (RT_SUCCESS(rc2))
     669            LogRel(("DBGF/SampleReport: Version : \"%s\"\n", szVersion));
     670    }
     671    else
     672        LogRel(("DBGF/SampleReport: Couldn't detect guest operating system rc=%Rcr\n", rc));
     673
     674    /*
     675     * We keep an additional reference to ensure that the sample report stays alive,
     676     * it will be dropped when the sample process is stopped.
     677     */
     678    DBGFR3SampleReportRetain(pThis);
     679
     680    rc = RTTimerCreateEx(&pThis->hTimer, pThis->cSampleIntervalUs * 1000,
     681                         RTTIMER_FLAGS_CPU_ANY | RTTIMER_FLAGS_HIGH_RES,
     682                         dbgfR3SampleReportTakeSample, pThis);
     683    if (RT_SUCCESS(rc))
     684        rc = RTTimerStart(pThis->hTimer, 0 /*u64First*/);
     685
     686    if (RT_FAILURE(rc))
     687    {
     688        bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY,
     689                                         DBGFSAMPLEREPORTSTATE_RUNNING);
     690        Assert(fXchg); RT_NOREF(fXchg);
     691    }
     692
     693    return rc;
     694}
     695
     696
     697/**
     698 * Stops collecting samples for the given sample report.
     699 *
     700 * @returns VBox status code.
     701 * @param   hSample                 Sample report handle.
    404702 */
    405703VMMR3DECL(int) DBGFR3SampleReportStop(DBGFSAMPLEREPORT hSample)
     
    407705    PDBGFSAMPLEREPORTINT pThis = hSample;
    408706
    409     int rc = RTTimerStop(pThis->hTimer);
    410 
    411 #if 1
    412     /* Some early dump code. */
    413     for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
    414     {
    415         PCDBGFSAMPLEREPORTVCPU pSampleVCpu = &pThis->aCpus[i];
    416 
    417         LogRel(("Sample report for vCPU %u:\n", i));
    418         dbgfR3SampleReportDumpFrame(pThis->pUVM, &pSampleVCpu->FrameRoot, 0);
    419     }
    420 #endif
    421 
    422     return rc;
     707    AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
     708    AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
     709                                     DBGFSAMPLEREPORTSTATE_RUNNING),
     710                 VERR_INVALID_STATE);
     711    return VINF_SUCCESS;
    423712}
    424713
     
    436725    return VINF_SUCCESS;
    437726}
     727
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