VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3SampleReport.cpp

Last change on this file was 99739, checked in by vboxsync, 12 months ago

*: doxygen corrections (mostly about removing @returns from functions returning void).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.9 KB
Line 
1/* $Id: DBGFR3SampleReport.cpp 99739 2023-05-11 01:01:08Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Sample report creation.
4 */
5
6/*
7 * Copyright (C) 2021-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/** @page pg_dbgf_sample_report DBGFR3SampleReport - Sample Report Interface
30 *
31 * @todo
32 */
33
34
35/*********************************************************************************************************************************
36* Header Files *
37*********************************************************************************************************************************/
38#define LOG_GROUP LOG_GROUP_DBGF
39#include <VBox/vmm/dbgf.h>
40#include "DBGFInternal.h"
41#include <VBox/vmm/mm.h>
42#include <VBox/vmm/uvm.h>
43#include <VBox/vmm/vm.h>
44#include <VBox/err.h>
45#include <VBox/log.h>
46
47#include <iprt/assert.h>
48#include <iprt/semaphore.h>
49#include <iprt/list.h>
50#include <iprt/mem.h>
51#include <iprt/time.h>
52#include <iprt/timer.h>
53#include <iprt/sort.h>
54#include <iprt/string.h>
55#include <iprt/stream.h>
56
57
58/*********************************************************************************************************************************
59* Defined Constants And Macros *
60*********************************************************************************************************************************/
61
62/** Maximum stack frame depth. */
63#define DBGF_SAMPLE_REPORT_FRAME_DEPTH_MAX 64
64
65
66/*********************************************************************************************************************************
67* Structures and Typedefs *
68*********************************************************************************************************************************/
69
70/**
71 * Sample report state.
72 */
73typedef enum DBGFSAMPLEREPORTSTATE
74{
75 /** Invalid state do not use. */
76 DBGFSAMPLEREPORTSTATE_INVALID = 0,
77 /** The sample report is ready to run. */
78 DBGFSAMPLEREPORTSTATE_READY,
79 /** The sampple process is running currently. */
80 DBGFSAMPLEREPORTSTATE_RUNNING,
81 /** The sample process is about to stop. */
82 DBGFSAMPLEREPORTSTATE_STOPPING,
83 /** 32bit hack. */
84 DBGFSAMPLEREPORTSTATE_32BIT_HACK = 0x7fffffff
85} DBGFSAMPLEREPORTSTATE;
86
87/** Pointer to a single sample frame. */
88typedef struct DBGFSAMPLEFRAME *PDBGFSAMPLEFRAME;
89
90/**
91 * Frame information.
92 */
93typedef struct DBGFSAMPLEFRAME
94{
95 /** Frame address. */
96 DBGFADDRESS AddrFrame;
97 /** Number of times this frame was encountered. */
98 uint64_t cSamples;
99 /** Pointer to the array of frames below in the call stack. */
100 PDBGFSAMPLEFRAME paFrames;
101 /** Number of valid entries in the frams array. */
102 uint64_t cFramesValid;
103 /** Maximum number of entries in the frames array. */
104 uint64_t cFramesMax;
105} DBGFSAMPLEFRAME;
106typedef const DBGFSAMPLEFRAME *PCDBGFSAMPLEFRAME;
107
108
109/**
110 * Per VCPU sample report data.
111 */
112typedef struct DBGFSAMPLEREPORTVCPU
113{
114 /** The root frame. */
115 DBGFSAMPLEFRAME FrameRoot;
116} DBGFSAMPLEREPORTVCPU;
117/** Pointer to the per VCPU sample report data. */
118typedef DBGFSAMPLEREPORTVCPU *PDBGFSAMPLEREPORTVCPU;
119/** Pointer to const per VCPU sample report data. */
120typedef const DBGFSAMPLEREPORTVCPU *PCDBGFSAMPLEREPORTVCPU;
121
122
123/**
124 * Internal sample report instance data.
125 */
126typedef struct DBGFSAMPLEREPORTINT
127{
128 /** References hold for this trace module. */
129 volatile uint32_t cRefs;
130 /** The user mode VM handle. */
131 PUVM pUVM;
132 /** State the sample report is currently in. */
133 volatile DBGFSAMPLEREPORTSTATE enmState;
134 /** Flags passed during report creation. */
135 uint32_t fFlags;
136 /** The timer handle for the sample report collector. */
137 PRTTIMER hTimer;
138 /** The sample interval in microseconds. */
139 uint32_t cSampleIntervalUs;
140 /** THe progress callback if set. */
141 PFNDBGFPROGRESS pfnProgress;
142 /** Opaque user data passed with the progress callback. */
143 void *pvProgressUser;
144 /** Number of microseconds left for sampling. */
145 uint64_t cSampleUsLeft;
146 /** The report created after sampling was stopped. */
147 char *pszReport;
148 /** Number of EMTs having a guest sample operation queued. */
149 volatile uint32_t cEmtsActive;
150 /** Array of per VCPU samples collected. */
151 DBGFSAMPLEREPORTVCPU aCpus[1];
152} DBGFSAMPLEREPORTINT;
153/** Pointer to a const internal trace module instance data. */
154typedef DBGFSAMPLEREPORTINT *PDBGFSAMPLEREPORTINT;
155/** Pointer to a const internal trace module instance data. */
156typedef const DBGFSAMPLEREPORTINT *PCDBGFSAMPLEREPORTINT;
157
158
159/**
160 * Structure to pass to DBGFR3Info() and for doing all other
161 * output during fatal dump.
162 */
163typedef struct DBGFSAMPLEREPORTINFOHLP
164{
165 /** The helper core. */
166 DBGFINFOHLP Core;
167 /** Pointer to the allocated character buffer. */
168 char *pachBuf;
169 /** Number of bytes allocated for the character buffer. */
170 size_t cbBuf;
171 /** Offset into the character buffer. */
172 size_t offBuf;
173} DBGFSAMPLEREPORTINFOHLP, *PDBGFSAMPLEREPORTINFOHLP;
174/** Pointer to a DBGFSAMPLEREPORTINFOHLP structure. */
175typedef const DBGFSAMPLEREPORTINFOHLP *PCDBGFSAMPLEREPORTINFOHLP;
176
177
178/*********************************************************************************************************************************
179* Internal Functions *
180*********************************************************************************************************************************/
181
182/**
183 * Print formatted string.
184 *
185 * @param pHlp Pointer to this structure.
186 * @param pszFormat The format string.
187 * @param ... Arguments.
188 */
189static DECLCALLBACK(void) dbgfR3SampleReportInfoHlp_pfnPrintf(PCDBGFINFOHLP pHlp, const char *pszFormat, ...)
190{
191 va_list args;
192 va_start(args, pszFormat);
193 pHlp->pfnPrintfV(pHlp, pszFormat, args);
194 va_end(args);
195}
196
197
198/**
199 * Print formatted string.
200 *
201 * @param pHlp Pointer to this structure.
202 * @param pszFormat The format string.
203 * @param args Argument list.
204 */
205static DECLCALLBACK(void) dbgfR3SampleReportInfoHlp_pfnPrintfV(PCDBGFINFOHLP pHlp, const char *pszFormat, va_list args)
206{
207 PDBGFSAMPLEREPORTINFOHLP pMyHlp = (PDBGFSAMPLEREPORTINFOHLP)pHlp;
208
209 va_list args2;
210 va_copy(args2, args);
211 ssize_t cch = RTStrPrintf2V(&pMyHlp->pachBuf[pMyHlp->offBuf], pMyHlp->cbBuf - pMyHlp->offBuf, pszFormat, args2);
212 if (cch < 0)
213 {
214 /* Try increase the buffer. */
215 char *pachBufNew = (char *)RTMemRealloc(pMyHlp->pachBuf, pMyHlp->cbBuf + RT_MAX(_4K, -cch));
216 if (pachBufNew)
217 {
218 pMyHlp->pachBuf = pachBufNew;
219 pMyHlp->cbBuf += RT_MAX(_4K, -cch);
220 cch = RTStrPrintf2V(&pMyHlp->pachBuf[pMyHlp->offBuf], pMyHlp->cbBuf - pMyHlp->offBuf, pszFormat, args2);
221 Assert(cch > 0);
222 pMyHlp->offBuf += cch;
223 }
224 }
225 else
226 pMyHlp->offBuf += cch;
227 va_end(args2);
228}
229
230
231/**
232 * Initializes the sample report output helper.
233 *
234 * @param pHlp The structure to initialize.
235 */
236static void dbgfR3SampleReportInfoHlpInit(PDBGFSAMPLEREPORTINFOHLP pHlp)
237{
238 RT_BZERO(pHlp, sizeof(*pHlp));
239
240 pHlp->Core.pfnPrintf = dbgfR3SampleReportInfoHlp_pfnPrintf;
241 pHlp->Core.pfnPrintfV = dbgfR3SampleReportInfoHlp_pfnPrintfV;
242 pHlp->Core.pfnGetOptError = DBGFR3InfoGenericGetOptError;
243
244 pHlp->pachBuf = (char *)RTMemAllocZ(_4K);
245 if (pHlp->pachBuf)
246 pHlp->cbBuf = _4K;
247}
248
249
250/**
251 * Deletes the sample report output helper.
252 *
253 * @param pHlp The structure to delete.
254 */
255static void dbgfR3SampleReportInfoHlpDelete(PDBGFSAMPLEREPORTINFOHLP pHlp)
256{
257 if (pHlp->pachBuf)
258 RTMemFree(pHlp->pachBuf);
259}
260
261
262/**
263 * Frees the given frame and all its descendants.
264 *
265 * @param pFrame The frame to free.
266 */
267static void dbgfR3SampleReportFrameFree(PDBGFSAMPLEFRAME pFrame)
268{
269 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
270 dbgfR3SampleReportFrameFree(&pFrame->paFrames[i]); /** @todo Recursion... */
271
272 MMR3HeapFree(pFrame->paFrames);
273 memset(pFrame, 0, sizeof(*pFrame));
274}
275
276
277/**
278 * Destroys the given sample report freeing all allocated resources.
279 *
280 * @param pThis The sample report instance data.
281 */
282static void dbgfR3SampleReportDestroy(PDBGFSAMPLEREPORTINT pThis)
283{
284 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
285 dbgfR3SampleReportFrameFree(&pThis->aCpus[i].FrameRoot);
286 MMR3HeapFree(pThis);
287}
288
289
290/**
291 * Returns the frame belonging to the given address or NULL if not found.
292 *
293 * @returns Pointer to the descendant frame or NULL if not found.
294 * @param pFrame The frame to look for descendants with the matching address.
295 * @param pAddr The guest address to search for.
296 */
297static PDBGFSAMPLEFRAME dbgfR3SampleReportFrameFindByAddr(PCDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
298{
299 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
300 if (!memcmp(pAddr, &pFrame->paFrames[i].AddrFrame, sizeof(*pAddr)))
301 return &pFrame->paFrames[i];
302
303 return NULL;
304}
305
306
307/**
308 * Adds the given address to as a descendant to the given frame.
309 *
310 * @returns Pointer to the newly inserted frame identified by the given address.
311 * @param pUVM The usermode VM handle.
312 * @param pFrame The frame to add the new one to as a descendant.
313 * @param pAddr The guest address to add.
314 */
315static PDBGFSAMPLEFRAME dbgfR3SampleReportAddFrameByAddr(PUVM pUVM, PDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
316{
317 if (pFrame->cFramesValid == pFrame->cFramesMax)
318 {
319 uint32_t cFramesMaxNew = pFrame->cFramesMax + 10;
320 PDBGFSAMPLEFRAME paFramesNew = NULL;
321 if (pFrame->paFrames)
322 paFramesNew = (PDBGFSAMPLEFRAME)MMR3HeapRealloc(pFrame->paFrames, sizeof(*pFrame->paFrames) * cFramesMaxNew);
323 else
324 paFramesNew = (PDBGFSAMPLEFRAME)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF, sizeof(*pFrame->paFrames) * cFramesMaxNew);
325
326 if (!paFramesNew)
327 return NULL;
328
329 pFrame->cFramesMax = cFramesMaxNew;
330 pFrame->paFrames = paFramesNew;
331 }
332
333 PDBGFSAMPLEFRAME pFrameNew = &pFrame->paFrames[pFrame->cFramesValid++];
334 pFrameNew->AddrFrame = *pAddr;
335 pFrameNew->cSamples = 1;
336 pFrameNew->paFrames = NULL;
337 pFrameNew->cFramesMax = 0;
338 pFrameNew->cFramesValid = 0;
339 return pFrameNew;
340}
341
342
343/**
344 * @copydoc FNRTSORTCMP
345 */
346static DECLCALLBACK(int) dbgfR3SampleReportFrameSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
347{
348 RT_NOREF(pvUser);
349 PCDBGFSAMPLEFRAME pFrame1 = (PCDBGFSAMPLEFRAME)pvElement1;
350 PCDBGFSAMPLEFRAME pFrame2 = (PCDBGFSAMPLEFRAME)pvElement2;
351
352 if (pFrame1->cSamples < pFrame2->cSamples)
353 return 1;
354 if (pFrame1->cSamples > pFrame2->cSamples)
355 return -1;
356
357 return 0;
358}
359
360
361/**
362 * Dumps a single given frame to the release log.
363 *
364 * @param pHlp The debug info helper used for printing.
365 * @param pUVM The usermode VM handle.
366 * @param pFrame The frame to dump.
367 * @param idxFrame The frame number.
368 */
369static void dbgfR3SampleReportDumpFrame(PCDBGFINFOHLP pHlp, PUVM pUVM, PCDBGFSAMPLEFRAME pFrame, uint32_t idxFrame)
370{
371 RTGCINTPTR offDisp;
372 RTDBGMOD hMod;
373 RTDBGSYMBOL SymPC;
374
375 if (DBGFR3AddrIsValid(pUVM, &pFrame->AddrFrame))
376 {
377 int rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_GLOBAL, &pFrame->AddrFrame,
378 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
379 &offDisp, &SymPC, &hMod);
380 if (RT_SUCCESS(rc))
381 {
382 const char *pszModName = hMod != NIL_RTDBGMOD ? RTDbgModName(hMod) : NULL;
383
384 pHlp->pfnPrintf(pHlp,
385 "%*s%RU64 %s+%llx (%s) [%RGv]\n", idxFrame * 4, " ",
386 pFrame->cSamples,
387 SymPC.szName, offDisp,
388 hMod ? pszModName : "",
389 pFrame->AddrFrame.FlatPtr);
390 RTDbgModRelease(hMod);
391 }
392 else
393 pHlp->pfnPrintf(pHlp, "%*s%RU64 %RGv\n", idxFrame * 4, " ", pFrame->cSamples, pFrame->AddrFrame.FlatPtr);
394 }
395 else
396 pHlp->pfnPrintf(pHlp, "%*s%RU64 %RGv\n", idxFrame * 4, " ", pFrame->cSamples, pFrame->AddrFrame.FlatPtr);
397
398 /* Sort by sample count. */
399 RTSortShell(pFrame->paFrames, pFrame->cFramesValid, sizeof(*pFrame->paFrames), dbgfR3SampleReportFrameSortCmp, NULL);
400
401 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
402 dbgfR3SampleReportDumpFrame(pHlp, pUVM, &pFrame->paFrames[i], idxFrame + 1);
403}
404
405
406/**
407 * Worker for dbgfR3SampleReportTakeSample(), doing the work in an EMT rendezvous point on
408 * each VCPU.
409 *
410 * @param pThis Pointer to the sample report instance.
411 */
412static DECLCALLBACK(void) dbgfR3SampleReportSample(PDBGFSAMPLEREPORTINT pThis)
413{
414 PVM pVM = pThis->pUVM->pVM;
415 PVMCPU pVCpu = VMMGetCpu(pVM);
416
417 PCDBGFSTACKFRAME pFrameFirst;
418 int rc = DBGFR3StackWalkBegin(pThis->pUVM, pVCpu->idCpu, DBGFCODETYPE_GUEST, &pFrameFirst);
419 if (RT_SUCCESS(rc))
420 {
421 DBGFADDRESS aFrameAddresses[DBGF_SAMPLE_REPORT_FRAME_DEPTH_MAX];
422 uint32_t idxFrame = 0;
423
424 PDBGFSAMPLEFRAME pFrame = &pThis->aCpus[pVCpu->idCpu].FrameRoot;
425 pFrame->cSamples++;
426
427 for (PCDBGFSTACKFRAME pStackFrame = pFrameFirst;
428 pStackFrame && idxFrame < RT_ELEMENTS(aFrameAddresses);
429 pStackFrame = DBGFR3StackWalkNext(pStackFrame))
430 {
431 if (pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE)
432 {
433 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &pStackFrame->AddrPC);
434 if (!pFrameNext)
435 pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &pStackFrame->AddrPC);
436 else
437 pFrameNext->cSamples++;
438
439 pFrame = pFrameNext;
440 }
441 else
442 aFrameAddresses[idxFrame] = pStackFrame->AddrPC;
443
444 idxFrame++;
445 }
446
447 DBGFR3StackWalkEnd(pFrameFirst);
448
449 if (!(pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE))
450 {
451 /* Walk the frame stack backwards and construct the call stack. */
452 while (idxFrame--)
453 {
454 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &aFrameAddresses[idxFrame]);
455 if (!pFrameNext)
456 pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &aFrameAddresses[idxFrame]);
457 else
458 pFrameNext->cSamples++;
459
460 pFrame = pFrameNext;
461 }
462 }
463 }
464 else
465 LogRelMax(10, ("Sampling guest stack on VCPU %u failed with rc=%Rrc\n", pVCpu->idCpu, rc));
466
467 /* Last EMT finishes the report when sampling was stopped. */
468 uint32_t cEmtsActive = ASMAtomicDecU32(&pThis->cEmtsActive);
469 if ( ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_STOPPING
470 && !cEmtsActive)
471 {
472 rc = RTTimerDestroy(pThis->hTimer); AssertRC(rc); RT_NOREF(rc);
473 pThis->hTimer = NULL;
474
475 DBGFSAMPLEREPORTINFOHLP Hlp;
476 PCDBGFINFOHLP pHlp = &Hlp.Core;
477
478 dbgfR3SampleReportInfoHlpInit(&Hlp);
479
480 /* Some early dump code. */
481 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
482 {
483 PCDBGFSAMPLEREPORTVCPU pSampleVCpu = &pThis->aCpus[i];
484
485 pHlp->pfnPrintf(pHlp, "Sample report for vCPU %u:\n", i);
486 dbgfR3SampleReportDumpFrame(pHlp, pThis->pUVM, &pSampleVCpu->FrameRoot, 0);
487 }
488
489 /* Shameless copy from VMMGuruMeditation.cpp */
490 static struct
491 {
492 const char *pszInfo;
493 const char *pszArgs;
494 } const aInfo[] =
495 {
496 { "mappings", NULL },
497 { "mode", "all" },
498 { "handlers", "phys virt hyper stats" },
499 { "timers", NULL },
500 { "activetimers", NULL },
501 };
502 for (unsigned i = 0; i < RT_ELEMENTS(aInfo); i++)
503 {
504 pHlp->pfnPrintf(pHlp,
505 "!!\n"
506 "!! {%s, %s}\n"
507 "!!\n",
508 aInfo[i].pszInfo, aInfo[i].pszArgs);
509 DBGFR3Info(pVM->pUVM, aInfo[i].pszInfo, aInfo[i].pszArgs, pHlp);
510 }
511
512 /* All other info items */
513 DBGFR3InfoMulti(pVM,
514 "*",
515 "mappings|hma|cpum|cpumguest|cpumguesthwvirt|cpumguestinstr|cpumhyper|cpumhost|cpumvmxfeat|mode|cpuid"
516 "|pgmpd|pgmcr3|timers|activetimers|handlers|help|cfgm",
517 "!!\n"
518 "!! {%s}\n"
519 "!!\n",
520 pHlp);
521
522
523 /* done */
524 pHlp->pfnPrintf(pHlp,
525 "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
526
527 if (pThis->pszReport)
528 RTMemFree(pThis->pszReport);
529 pThis->pszReport = Hlp.pachBuf;
530 Hlp.pachBuf = NULL;
531 dbgfR3SampleReportInfoHlpDelete(&Hlp);
532
533 ASMAtomicXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY);
534
535 if (pThis->pfnProgress)
536 {
537 pThis->pfnProgress(pThis->pvProgressUser, 100);
538 pThis->pfnProgress = NULL;
539 pThis->pvProgressUser = NULL;
540 }
541
542 DBGFR3SampleReportRelease(pThis);
543 }
544}
545
546
547/**
548 * @copydoc FNRTTIMER
549 */
550static DECLCALLBACK(void) dbgfR3SampleReportTakeSample(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
551{
552 PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
553
554 if (pThis->cSampleUsLeft != UINT32_MAX)
555 {
556 int rc = VINF_SUCCESS;
557 uint64_t cUsSampled = iTick * pThis->cSampleIntervalUs; /** @todo Wrong if the timer resolution is different from what we've requested. */
558
559 /* Update progress. */
560 if (pThis->pfnProgress)
561 rc = pThis->pfnProgress(pThis->pvProgressUser, cUsSampled * 99 / pThis->cSampleUsLeft);
562
563 if ( cUsSampled >= pThis->cSampleUsLeft
564 || rc == VERR_DBGF_CANCELLED)
565 {
566 /*
567 * Let the EMTs do one last round in order to be able to destroy the timer (can't do this on the timer thread)
568 * and gather information from the devices.
569 */
570 ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
571 DBGFSAMPLEREPORTSTATE_RUNNING);
572
573 rc = RTTimerStop(pTimer); AssertRC(rc); RT_NOREF(rc);
574 }
575 }
576
577 ASMAtomicAddU32(&pThis->cEmtsActive, pThis->pUVM->cCpus);
578
579 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
580 {
581 int rc = VMR3ReqCallVoidNoWait(pThis->pUVM->pVM, i, (PFNRT)dbgfR3SampleReportSample, 1, pThis);
582 AssertRC(rc);
583 if (RT_FAILURE(rc))
584 ASMAtomicDecU32(&pThis->cEmtsActive);
585 }
586}
587
588
589/**
590 * Creates a new sample report instance for the specified VM.
591 *
592 * @returns VBox status code.
593 * @param pUVM The usermode VM handle.
594 * @param cSampleIntervalUs The sample interval in micro seconds.
595 * @param fFlags Combination of DBGF_SAMPLE_REPORT_F_XXX.
596 * @param phSample Where to return the handle to the sample report on success.
597 */
598VMMR3DECL(int) DBGFR3SampleReportCreate(PUVM pUVM, uint32_t cSampleIntervalUs, uint32_t fFlags, PDBGFSAMPLEREPORT phSample)
599{
600 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
601 AssertReturn(!(fFlags & ~DBGF_SAMPLE_REPORT_F_VALID_MASK), VERR_INVALID_PARAMETER);
602 AssertPtrReturn(phSample, VERR_INVALID_POINTER);
603
604 int rc = VINF_SUCCESS;
605 PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF,
606 RT_UOFFSETOF_DYN(DBGFSAMPLEREPORTINT, aCpus[pUVM->cCpus]));
607 if (RT_LIKELY(pThis))
608 {
609 pThis->cRefs = 1;
610 pThis->pUVM = pUVM;
611 pThis->fFlags = fFlags;
612 pThis->cSampleIntervalUs = cSampleIntervalUs;
613 pThis->enmState = DBGFSAMPLEREPORTSTATE_READY;
614 pThis->cEmtsActive = 0;
615
616 for (uint32_t i = 0; i < pUVM->cCpus; i++)
617 {
618 pThis->aCpus[i].FrameRoot.paFrames = NULL;
619 pThis->aCpus[i].FrameRoot.cSamples = 0;
620 pThis->aCpus[i].FrameRoot.cFramesValid = 0;
621 pThis->aCpus[i].FrameRoot.cFramesMax = 0;
622 }
623
624 *phSample = pThis;
625 return VINF_SUCCESS;
626 }
627 else
628 rc = VERR_NO_MEMORY;
629
630 return rc;
631}
632
633
634/**
635 * Retains a reference to the given sample report handle.
636 *
637 * @returns New reference count.
638 * @param hSample Sample report handle.
639 */
640VMMR3DECL(uint32_t) DBGFR3SampleReportRetain(DBGFSAMPLEREPORT hSample)
641{
642 PDBGFSAMPLEREPORTINT pThis = hSample;
643 AssertPtrReturn(pThis, UINT32_MAX);
644
645 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
646 AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
647 return cRefs;
648}
649
650
651/**
652 * Release a given sample report handle reference.
653 *
654 * @returns New reference count, on 0 the sample report instance is destroyed.
655 * @param hSample Sample report handle.
656 */
657VMMR3DECL(uint32_t) DBGFR3SampleReportRelease(DBGFSAMPLEREPORT hSample)
658{
659 PDBGFSAMPLEREPORTINT pThis = hSample;
660 if (!pThis)
661 return 0;
662 AssertPtrReturn(pThis, UINT32_MAX);
663 AssertReturn(ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_READY,
664 0);
665
666 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
667 AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
668 if (cRefs == 0)
669 dbgfR3SampleReportDestroy(pThis);
670 return cRefs;
671}
672
673
674/**
675 * Starts collecting samples for the given sample report.
676 *
677 * @returns VBox status code.
678 * @param hSample Sample report handle.
679 * @param cSampleUs Number of microseconds to sample at the interval given during creation.
680 * Use UINT32_MAX to sample for an indefinite amount of time.
681 * @param pfnProgress Optional progress callback.
682 * @param pvUser Opaque user data to pass to the progress callback.
683 */
684VMMR3DECL(int) DBGFR3SampleReportStart(DBGFSAMPLEREPORT hSample, uint64_t cSampleUs, PFNDBGFPROGRESS pfnProgress, void *pvUser)
685{
686 PDBGFSAMPLEREPORTINT pThis = hSample;
687
688 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
689 AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_RUNNING, DBGFSAMPLEREPORTSTATE_READY),
690 VERR_INVALID_STATE);
691
692 pThis->pfnProgress = pfnProgress;
693 pThis->pvProgressUser = pvUser;
694 pThis->cSampleUsLeft = cSampleUs;
695
696 /* Try to detect the guest OS first so we can get more accurate symbols and addressing. */
697 char szName[64];
698 int rc = DBGFR3OSDetect(pThis->pUVM, &szName[0], sizeof(szName));
699 if (RT_SUCCESS(rc))
700 {
701 LogRel(("DBGF/SampleReport: Detected guest OS \"%s\"\n", szName));
702 char szVersion[512];
703 int rc2 = DBGFR3OSQueryNameAndVersion(pThis->pUVM, NULL, 0, szVersion, sizeof(szVersion));
704 if (RT_SUCCESS(rc2))
705 LogRel(("DBGF/SampleReport: Version : \"%s\"\n", szVersion));
706 }
707 else
708 LogRel(("DBGF/SampleReport: Couldn't detect guest operating system rc=%Rcr\n", rc));
709
710 /*
711 * We keep an additional reference to ensure that the sample report stays alive,
712 * it will be dropped when the sample process is stopped.
713 */
714 DBGFR3SampleReportRetain(pThis);
715
716 rc = RTTimerCreateEx(&pThis->hTimer, pThis->cSampleIntervalUs * 1000,
717 RTTIMER_FLAGS_CPU_ANY | RTTIMER_FLAGS_HIGH_RES,
718 dbgfR3SampleReportTakeSample, pThis);
719 if (RT_SUCCESS(rc))
720 rc = RTTimerStart(pThis->hTimer, 0 /*u64First*/);
721 if (RT_FAILURE(rc))
722 {
723 if (pThis->hTimer)
724 {
725 int rc2 = RTTimerDestroy(pThis->hTimer);
726 AssertRC(rc2); RT_NOREF(rc2);
727 pThis->hTimer = NULL;
728 }
729
730 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY,
731 DBGFSAMPLEREPORTSTATE_RUNNING);
732 Assert(fXchg); RT_NOREF(fXchg);
733 DBGFR3SampleReportRelease(pThis);
734 }
735
736 return rc;
737}
738
739
740/**
741 * Stops collecting samples for the given sample report.
742 *
743 * @returns VBox status code.
744 * @param hSample Sample report handle.
745 */
746VMMR3DECL(int) DBGFR3SampleReportStop(DBGFSAMPLEREPORT hSample)
747{
748 PDBGFSAMPLEREPORTINT pThis = hSample;
749
750 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
751 AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
752 DBGFSAMPLEREPORTSTATE_RUNNING),
753 VERR_INVALID_STATE);
754 return VINF_SUCCESS;
755}
756
757
758/**
759 * Dumps the current sample report to the given file.
760 *
761 * @returns VBox status code.
762 * @retval VERR_INVALID_STATE if nothing was sampled so far for reporting.
763 * @param hSample Sample report handle.
764 * @param pszFilename The filename to dump the report to.
765 */
766VMMR3DECL(int) DBGFR3SampleReportDumpToFile(DBGFSAMPLEREPORT hSample, const char *pszFilename)
767{
768 PDBGFSAMPLEREPORTINT pThis = hSample;
769
770 AssertReturn(pThis->pszReport, VERR_INVALID_STATE);
771
772 PRTSTREAM hStream;
773 int rc = RTStrmOpen(pszFilename, "w", &hStream);
774 if (RT_SUCCESS(rc))
775 {
776 rc = RTStrmPutStr(hStream, pThis->pszReport);
777 RTStrmClose(hStream);
778 }
779
780 return rc;
781}
782
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use