VirtualBox

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

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

VMM/DBGFR3SampleReport: Remove Windows specific changes and change the way how the sampling is started from the timer callback to avoid possible deadlocks between the timer callback and the EMTs doing the sampling

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