VirtualBox

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

Last change on this file since 99051 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.0 KB
Line 
1/* $Id: DBGFR3SampleReport.cpp 98103 2023-01-17 14:15:46Z 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 * @returns nothing.
266 * @param pFrame The frame to free.
267 */
268static void dbgfR3SampleReportFrameFree(PDBGFSAMPLEFRAME pFrame)
269{
270 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
271 dbgfR3SampleReportFrameFree(&pFrame->paFrames[i]); /** @todo Recursion... */
272
273 MMR3HeapFree(pFrame->paFrames);
274 memset(pFrame, 0, sizeof(*pFrame));
275}
276
277
278/**
279 * Destroys the given sample report freeing all allocated resources.
280 *
281 * @returns nothing.
282 * @param pThis The sample report instance data.
283 */
284static void dbgfR3SampleReportDestroy(PDBGFSAMPLEREPORTINT pThis)
285{
286 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
287 dbgfR3SampleReportFrameFree(&pThis->aCpus[i].FrameRoot);
288 MMR3HeapFree(pThis);
289}
290
291
292/**
293 * Returns the frame belonging to the given address or NULL if not found.
294 *
295 * @returns Pointer to the descendant frame or NULL if not found.
296 * @param pFrame The frame to look for descendants with the matching address.
297 * @param pAddr The guest address to search for.
298 */
299static PDBGFSAMPLEFRAME dbgfR3SampleReportFrameFindByAddr(PCDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
300{
301 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
302 if (!memcmp(pAddr, &pFrame->paFrames[i].AddrFrame, sizeof(*pAddr)))
303 return &pFrame->paFrames[i];
304
305 return NULL;
306}
307
308
309/**
310 * Adds the given address to as a descendant to the given frame.
311 *
312 * @returns Pointer to the newly inserted frame identified by the given address.
313 * @param pUVM The usermode VM handle.
314 * @param pFrame The frame to add the new one to as a descendant.
315 * @param pAddr The guest address to add.
316 */
317static PDBGFSAMPLEFRAME dbgfR3SampleReportAddFrameByAddr(PUVM pUVM, PDBGFSAMPLEFRAME pFrame, PCDBGFADDRESS pAddr)
318{
319 if (pFrame->cFramesValid == pFrame->cFramesMax)
320 {
321 uint32_t cFramesMaxNew = pFrame->cFramesMax + 10;
322 PDBGFSAMPLEFRAME paFramesNew = NULL;
323 if (pFrame->paFrames)
324 paFramesNew = (PDBGFSAMPLEFRAME)MMR3HeapRealloc(pFrame->paFrames, sizeof(*pFrame->paFrames) * cFramesMaxNew);
325 else
326 paFramesNew = (PDBGFSAMPLEFRAME)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF, sizeof(*pFrame->paFrames) * cFramesMaxNew);
327
328 if (!paFramesNew)
329 return NULL;
330
331 pFrame->cFramesMax = cFramesMaxNew;
332 pFrame->paFrames = paFramesNew;
333 }
334
335 PDBGFSAMPLEFRAME pFrameNew = &pFrame->paFrames[pFrame->cFramesValid++];
336 pFrameNew->AddrFrame = *pAddr;
337 pFrameNew->cSamples = 1;
338 pFrameNew->paFrames = NULL;
339 pFrameNew->cFramesMax = 0;
340 pFrameNew->cFramesValid = 0;
341 return pFrameNew;
342}
343
344
345/**
346 * @copydoc FNRTSORTCMP
347 */
348static DECLCALLBACK(int) dbgfR3SampleReportFrameSortCmp(void const *pvElement1, void const *pvElement2, void *pvUser)
349{
350 RT_NOREF(pvUser);
351 PCDBGFSAMPLEFRAME pFrame1 = (PCDBGFSAMPLEFRAME)pvElement1;
352 PCDBGFSAMPLEFRAME pFrame2 = (PCDBGFSAMPLEFRAME)pvElement2;
353
354 if (pFrame1->cSamples < pFrame2->cSamples)
355 return 1;
356 if (pFrame1->cSamples > pFrame2->cSamples)
357 return -1;
358
359 return 0;
360}
361
362
363/**
364 * Dumps a single given frame to the release log.
365 *
366 * @returns nothing.
367 * @param pHlp The debug info helper used for printing.
368 * @param pUVM The usermode VM handle.
369 * @param pFrame The frame to dump.
370 * @param idxFrame The frame number.
371 */
372static void dbgfR3SampleReportDumpFrame(PCDBGFINFOHLP pHlp, PUVM pUVM, PCDBGFSAMPLEFRAME pFrame, uint32_t idxFrame)
373{
374 RTGCINTPTR offDisp;
375 RTDBGMOD hMod;
376 RTDBGSYMBOL SymPC;
377
378 if (DBGFR3AddrIsValid(pUVM, &pFrame->AddrFrame))
379 {
380 int rc = DBGFR3AsSymbolByAddr(pUVM, DBGF_AS_GLOBAL, &pFrame->AddrFrame,
381 RTDBGSYMADDR_FLAGS_LESS_OR_EQUAL | RTDBGSYMADDR_FLAGS_SKIP_ABS_IN_DEFERRED,
382 &offDisp, &SymPC, &hMod);
383 if (RT_SUCCESS(rc))
384 {
385 const char *pszModName = hMod != NIL_RTDBGMOD ? RTDbgModName(hMod) : NULL;
386
387 pHlp->pfnPrintf(pHlp,
388 "%*s%RU64 %s+%llx (%s) [%RGv]\n", idxFrame * 4, " ",
389 pFrame->cSamples,
390 SymPC.szName, offDisp,
391 hMod ? pszModName : "",
392 pFrame->AddrFrame.FlatPtr);
393 RTDbgModRelease(hMod);
394 }
395 else
396 pHlp->pfnPrintf(pHlp, "%*s%RU64 %RGv\n", idxFrame * 4, " ", pFrame->cSamples, pFrame->AddrFrame.FlatPtr);
397 }
398 else
399 pHlp->pfnPrintf(pHlp, "%*s%RU64 %RGv\n", idxFrame * 4, " ", pFrame->cSamples, pFrame->AddrFrame.FlatPtr);
400
401 /* Sort by sample count. */
402 RTSortShell(pFrame->paFrames, pFrame->cFramesValid, sizeof(*pFrame->paFrames), dbgfR3SampleReportFrameSortCmp, NULL);
403
404 for (uint32_t i = 0; i < pFrame->cFramesValid; i++)
405 dbgfR3SampleReportDumpFrame(pHlp, pUVM, &pFrame->paFrames[i], idxFrame + 1);
406}
407
408
409/**
410 * Worker for dbgfR3SampleReportTakeSample(), doing the work in an EMT rendezvous point on
411 * each VCPU.
412 *
413 * @returns nothing.
414 * @param pThis Pointer to the sample report instance.
415 */
416static DECLCALLBACK(void) dbgfR3SampleReportSample(PDBGFSAMPLEREPORTINT pThis)
417{
418 PVM pVM = pThis->pUVM->pVM;
419 PVMCPU pVCpu = VMMGetCpu(pVM);
420
421 PCDBGFSTACKFRAME pFrameFirst;
422 int rc = DBGFR3StackWalkBegin(pThis->pUVM, pVCpu->idCpu, DBGFCODETYPE_GUEST, &pFrameFirst);
423 if (RT_SUCCESS(rc))
424 {
425 DBGFADDRESS aFrameAddresses[DBGF_SAMPLE_REPORT_FRAME_DEPTH_MAX];
426 uint32_t idxFrame = 0;
427
428 PDBGFSAMPLEFRAME pFrame = &pThis->aCpus[pVCpu->idCpu].FrameRoot;
429 pFrame->cSamples++;
430
431 for (PCDBGFSTACKFRAME pStackFrame = pFrameFirst;
432 pStackFrame && idxFrame < RT_ELEMENTS(aFrameAddresses);
433 pStackFrame = DBGFR3StackWalkNext(pStackFrame))
434 {
435 if (pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE)
436 {
437 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &pStackFrame->AddrPC);
438 if (!pFrameNext)
439 pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &pStackFrame->AddrPC);
440 else
441 pFrameNext->cSamples++;
442
443 pFrame = pFrameNext;
444 }
445 else
446 aFrameAddresses[idxFrame] = pStackFrame->AddrPC;
447
448 idxFrame++;
449 }
450
451 DBGFR3StackWalkEnd(pFrameFirst);
452
453 if (!(pThis->fFlags & DBGF_SAMPLE_REPORT_F_STACK_REVERSE))
454 {
455 /* Walk the frame stack backwards and construct the call stack. */
456 while (idxFrame--)
457 {
458 PDBGFSAMPLEFRAME pFrameNext = dbgfR3SampleReportFrameFindByAddr(pFrame, &aFrameAddresses[idxFrame]);
459 if (!pFrameNext)
460 pFrameNext = dbgfR3SampleReportAddFrameByAddr(pThis->pUVM, pFrame, &aFrameAddresses[idxFrame]);
461 else
462 pFrameNext->cSamples++;
463
464 pFrame = pFrameNext;
465 }
466 }
467 }
468 else
469 LogRelMax(10, ("Sampling guest stack on VCPU %u failed with rc=%Rrc\n", pVCpu->idCpu, rc));
470
471 /* Last EMT finishes the report when sampling was stopped. */
472 uint32_t cEmtsActive = ASMAtomicDecU32(&pThis->cEmtsActive);
473 if ( ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_STOPPING
474 && !cEmtsActive)
475 {
476 rc = RTTimerDestroy(pThis->hTimer); AssertRC(rc); RT_NOREF(rc);
477 pThis->hTimer = NULL;
478
479 DBGFSAMPLEREPORTINFOHLP Hlp;
480 PCDBGFINFOHLP pHlp = &Hlp.Core;
481
482 dbgfR3SampleReportInfoHlpInit(&Hlp);
483
484 /* Some early dump code. */
485 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
486 {
487 PCDBGFSAMPLEREPORTVCPU pSampleVCpu = &pThis->aCpus[i];
488
489 pHlp->pfnPrintf(pHlp, "Sample report for vCPU %u:\n", i);
490 dbgfR3SampleReportDumpFrame(pHlp, pThis->pUVM, &pSampleVCpu->FrameRoot, 0);
491 }
492
493 /* Shameless copy from VMMGuruMeditation.cpp */
494 static struct
495 {
496 const char *pszInfo;
497 const char *pszArgs;
498 } const aInfo[] =
499 {
500 { "mappings", NULL },
501 { "mode", "all" },
502 { "handlers", "phys virt hyper stats" },
503 { "timers", NULL },
504 { "activetimers", NULL },
505 };
506 for (unsigned i = 0; i < RT_ELEMENTS(aInfo); i++)
507 {
508 pHlp->pfnPrintf(pHlp,
509 "!!\n"
510 "!! {%s, %s}\n"
511 "!!\n",
512 aInfo[i].pszInfo, aInfo[i].pszArgs);
513 DBGFR3Info(pVM->pUVM, aInfo[i].pszInfo, aInfo[i].pszArgs, pHlp);
514 }
515
516 /* All other info items */
517 DBGFR3InfoMulti(pVM,
518 "*",
519 "mappings|hma|cpum|cpumguest|cpumguesthwvirt|cpumguestinstr|cpumhyper|cpumhost|cpumvmxfeat|mode|cpuid"
520 "|pgmpd|pgmcr3|timers|activetimers|handlers|help|cfgm",
521 "!!\n"
522 "!! {%s}\n"
523 "!!\n",
524 pHlp);
525
526
527 /* done */
528 pHlp->pfnPrintf(pHlp,
529 "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
530
531 if (pThis->pszReport)
532 RTMemFree(pThis->pszReport);
533 pThis->pszReport = Hlp.pachBuf;
534 Hlp.pachBuf = NULL;
535 dbgfR3SampleReportInfoHlpDelete(&Hlp);
536
537 ASMAtomicXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY);
538
539 if (pThis->pfnProgress)
540 {
541 pThis->pfnProgress(pThis->pvProgressUser, 100);
542 pThis->pfnProgress = NULL;
543 pThis->pvProgressUser = NULL;
544 }
545
546 DBGFR3SampleReportRelease(pThis);
547 }
548}
549
550
551/**
552 * @copydoc FNRTTIMER
553 */
554static DECLCALLBACK(void) dbgfR3SampleReportTakeSample(PRTTIMER pTimer, void *pvUser, uint64_t iTick)
555{
556 PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)pvUser;
557
558 if (pThis->cSampleUsLeft != UINT32_MAX)
559 {
560 int rc = VINF_SUCCESS;
561 uint64_t cUsSampled = iTick * pThis->cSampleIntervalUs; /** @todo Wrong if the timer resolution is different from what we've requested. */
562
563 /* Update progress. */
564 if (pThis->pfnProgress)
565 rc = pThis->pfnProgress(pThis->pvProgressUser, cUsSampled * 99 / pThis->cSampleUsLeft);
566
567 if ( cUsSampled >= pThis->cSampleUsLeft
568 || rc == VERR_DBGF_CANCELLED)
569 {
570 /*
571 * Let the EMTs do one last round in order to be able to destroy the timer (can't do this on the timer thread)
572 * and gather information from the devices.
573 */
574 ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
575 DBGFSAMPLEREPORTSTATE_RUNNING);
576
577 rc = RTTimerStop(pTimer); AssertRC(rc); RT_NOREF(rc);
578 }
579 }
580
581 ASMAtomicAddU32(&pThis->cEmtsActive, pThis->pUVM->cCpus);
582
583 for (uint32_t i = 0; i < pThis->pUVM->cCpus; i++)
584 {
585 int rc = VMR3ReqCallVoidNoWait(pThis->pUVM->pVM, i, (PFNRT)dbgfR3SampleReportSample, 1, pThis);
586 AssertRC(rc);
587 if (RT_FAILURE(rc))
588 ASMAtomicDecU32(&pThis->cEmtsActive);
589 }
590}
591
592
593/**
594 * Creates a new sample report instance for the specified VM.
595 *
596 * @returns VBox status code.
597 * @param pUVM The usermode VM handle.
598 * @param cSampleIntervalUs The sample interval in micro seconds.
599 * @param fFlags Combination of DBGF_SAMPLE_REPORT_F_XXX.
600 * @param phSample Where to return the handle to the sample report on success.
601 */
602VMMR3DECL(int) DBGFR3SampleReportCreate(PUVM pUVM, uint32_t cSampleIntervalUs, uint32_t fFlags, PDBGFSAMPLEREPORT phSample)
603{
604 UVM_ASSERT_VALID_EXT_RETURN(pUVM, VERR_INVALID_VM_HANDLE);
605 AssertReturn(!(fFlags & ~DBGF_SAMPLE_REPORT_F_VALID_MASK), VERR_INVALID_PARAMETER);
606 AssertPtrReturn(phSample, VERR_INVALID_POINTER);
607
608 int rc = VINF_SUCCESS;
609 PDBGFSAMPLEREPORTINT pThis = (PDBGFSAMPLEREPORTINT)MMR3HeapAllocZU(pUVM, MM_TAG_DBGF,
610 RT_UOFFSETOF_DYN(DBGFSAMPLEREPORTINT, aCpus[pUVM->cCpus]));
611 if (RT_LIKELY(pThis))
612 {
613 pThis->cRefs = 1;
614 pThis->pUVM = pUVM;
615 pThis->fFlags = fFlags;
616 pThis->cSampleIntervalUs = cSampleIntervalUs;
617 pThis->enmState = DBGFSAMPLEREPORTSTATE_READY;
618 pThis->cEmtsActive = 0;
619
620 for (uint32_t i = 0; i < pUVM->cCpus; i++)
621 {
622 pThis->aCpus[i].FrameRoot.paFrames = NULL;
623 pThis->aCpus[i].FrameRoot.cSamples = 0;
624 pThis->aCpus[i].FrameRoot.cFramesValid = 0;
625 pThis->aCpus[i].FrameRoot.cFramesMax = 0;
626 }
627
628 *phSample = pThis;
629 return VINF_SUCCESS;
630 }
631 else
632 rc = VERR_NO_MEMORY;
633
634 return rc;
635}
636
637
638/**
639 * Retains a reference to the given sample report handle.
640 *
641 * @returns New reference count.
642 * @param hSample Sample report handle.
643 */
644VMMR3DECL(uint32_t) DBGFR3SampleReportRetain(DBGFSAMPLEREPORT hSample)
645{
646 PDBGFSAMPLEREPORTINT pThis = hSample;
647 AssertPtrReturn(pThis, UINT32_MAX);
648
649 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
650 AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
651 return cRefs;
652}
653
654
655/**
656 * Release a given sample report handle reference.
657 *
658 * @returns New reference count, on 0 the sample report instance is destroyed.
659 * @param hSample Sample report handle.
660 */
661VMMR3DECL(uint32_t) DBGFR3SampleReportRelease(DBGFSAMPLEREPORT hSample)
662{
663 PDBGFSAMPLEREPORTINT pThis = hSample;
664 if (!pThis)
665 return 0;
666 AssertPtrReturn(pThis, UINT32_MAX);
667 AssertReturn(ASMAtomicReadU32((volatile uint32_t *)&pThis->enmState) == DBGFSAMPLEREPORTSTATE_READY,
668 0);
669
670 uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
671 AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
672 if (cRefs == 0)
673 dbgfR3SampleReportDestroy(pThis);
674 return cRefs;
675}
676
677
678/**
679 * Starts collecting samples for the given sample report.
680 *
681 * @returns VBox status code.
682 * @param hSample Sample report handle.
683 * @param cSampleUs Number of microseconds to sample at the interval given during creation.
684 * Use UINT32_MAX to sample for an indefinite amount of time.
685 * @param pfnProgress Optional progress callback.
686 * @param pvUser Opaque user data to pass to the progress callback.
687 */
688VMMR3DECL(int) DBGFR3SampleReportStart(DBGFSAMPLEREPORT hSample, uint64_t cSampleUs, PFNDBGFPROGRESS pfnProgress, void *pvUser)
689{
690 PDBGFSAMPLEREPORTINT pThis = hSample;
691
692 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
693 AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_RUNNING, DBGFSAMPLEREPORTSTATE_READY),
694 VERR_INVALID_STATE);
695
696 pThis->pfnProgress = pfnProgress;
697 pThis->pvProgressUser = pvUser;
698 pThis->cSampleUsLeft = cSampleUs;
699
700 /* Try to detect the guest OS first so we can get more accurate symbols and addressing. */
701 char szName[64];
702 int rc = DBGFR3OSDetect(pThis->pUVM, &szName[0], sizeof(szName));
703 if (RT_SUCCESS(rc))
704 {
705 LogRel(("DBGF/SampleReport: Detected guest OS \"%s\"\n", szName));
706 char szVersion[512];
707 int rc2 = DBGFR3OSQueryNameAndVersion(pThis->pUVM, NULL, 0, szVersion, sizeof(szVersion));
708 if (RT_SUCCESS(rc2))
709 LogRel(("DBGF/SampleReport: Version : \"%s\"\n", szVersion));
710 }
711 else
712 LogRel(("DBGF/SampleReport: Couldn't detect guest operating system rc=%Rcr\n", rc));
713
714 /*
715 * We keep an additional reference to ensure that the sample report stays alive,
716 * it will be dropped when the sample process is stopped.
717 */
718 DBGFR3SampleReportRetain(pThis);
719
720 rc = RTTimerCreateEx(&pThis->hTimer, pThis->cSampleIntervalUs * 1000,
721 RTTIMER_FLAGS_CPU_ANY | RTTIMER_FLAGS_HIGH_RES,
722 dbgfR3SampleReportTakeSample, pThis);
723 if (RT_SUCCESS(rc))
724 rc = RTTimerStart(pThis->hTimer, 0 /*u64First*/);
725 if (RT_FAILURE(rc))
726 {
727 if (pThis->hTimer)
728 {
729 int rc2 = RTTimerDestroy(pThis->hTimer);
730 AssertRC(rc2); RT_NOREF(rc2);
731 pThis->hTimer = NULL;
732 }
733
734 bool fXchg = ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_READY,
735 DBGFSAMPLEREPORTSTATE_RUNNING);
736 Assert(fXchg); RT_NOREF(fXchg);
737 DBGFR3SampleReportRelease(pThis);
738 }
739
740 return rc;
741}
742
743
744/**
745 * Stops collecting samples for the given sample report.
746 *
747 * @returns VBox status code.
748 * @param hSample Sample report handle.
749 */
750VMMR3DECL(int) DBGFR3SampleReportStop(DBGFSAMPLEREPORT hSample)
751{
752 PDBGFSAMPLEREPORTINT pThis = hSample;
753
754 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
755 AssertReturn(ASMAtomicCmpXchgU32((volatile uint32_t *)&pThis->enmState, DBGFSAMPLEREPORTSTATE_STOPPING,
756 DBGFSAMPLEREPORTSTATE_RUNNING),
757 VERR_INVALID_STATE);
758 return VINF_SUCCESS;
759}
760
761
762/**
763 * Dumps the current sample report to the given file.
764 *
765 * @returns VBox status code.
766 * @retval VERR_INVALID_STATE if nothing was sampled so far for reporting.
767 * @param hSample Sample report handle.
768 * @param pszFilename The filename to dump the report to.
769 */
770VMMR3DECL(int) DBGFR3SampleReportDumpToFile(DBGFSAMPLEREPORT hSample, const char *pszFilename)
771{
772 PDBGFSAMPLEREPORTINT pThis = hSample;
773
774 AssertReturn(pThis->pszReport, VERR_INVALID_STATE);
775
776 PRTSTREAM hStream;
777 int rc = RTStrmOpen(pszFilename, "w", &hStream);
778 if (RT_SUCCESS(rc))
779 {
780 rc = RTStrmPutStr(hStream, pThis->pszReport);
781 RTStrmClose(hStream);
782 }
783
784 return rc;
785}
786
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