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