VirtualBox

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

Last change on this file since 94319 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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

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