VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3Tracer.cpp@ 84494

Last change on this file since 84494 was 84494, checked in by vboxsync, 5 years ago

VMM/DBGFTracer: Add events when an event source gets registered/deregistered and proper grouping for events caused by the event source

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1/* $Id: DBGFR3Tracer.cpp 84494 2020-05-25 11:09:52Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, tracing parts.
4 */
5
6/*
7 * Copyright (C) 2020 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGF
23#include "DBGFInternal.h"
24#include <VBox/vmm/pdm.h>
25#include <VBox/vmm/cfgm.h>
26#include <VBox/vmm/uvm.h>
27#include <VBox/vmm/vm.h>
28#include <VBox/vmm/vmm.h>
29#include <VBox/sup.h>
30
31#include <VBox/version.h>
32#include <VBox/log.h>
33#include <VBox/err.h>
34#include <iprt/buildconfig.h>
35#include <iprt/alloc.h>
36#include <iprt/alloca.h>
37#include <iprt/asm.h>
38#include <iprt/assert.h>
39#include <iprt/path.h>
40#include <iprt/semaphore.h>
41#include <iprt/string.h>
42#include <iprt/thread.h>
43#include <iprt/tracelog.h>
44
45
46/*********************************************************************************************************************************
47* Structures and Typedefs *
48*********************************************************************************************************************************/
49
50
51
52/*********************************************************************************************************************************
53* Global Variables *
54*********************************************************************************************************************************/
55/** The event descriptors written to the trace log. */
56
57static const RTTRACELOGEVTDESC g_EvtSrcRegisterEvtDesc =
58{
59 "EvtSrc.Register",
60 "An event soruce was registered",
61 RTTRACELOGEVTSEVERITY_DEBUG,
62 0,
63 NULL
64};
65
66
67static const RTTRACELOGEVTDESC g_EvtSrcDeregisterEvtDesc =
68{
69 "EvtSrc.Deregister",
70 "An event soruce was de-registered",
71 RTTRACELOGEVTSEVERITY_DEBUG,
72 0,
73 NULL
74};
75
76
77static const RTTRACELOGEVTITEMDESC g_DevMmioMapEvtItems[] =
78{
79 {"hMmioRegion", "The MMIO region handle being mapped", RTTRACELOGTYPE_UINT64, 0},
80 {"GCPhysMmioBase", "The guest physical address where the region is mapped", RTTRACELOGTYPE_UINT64, 0}
81};
82
83static const RTTRACELOGEVTDESC g_DevMmioMapEvtDesc =
84{
85 "Dev.MmioMap",
86 "MMIO region of a device is being mapped",
87 RTTRACELOGEVTSEVERITY_DEBUG,
88 RT_ELEMENTS(g_DevMmioMapEvtItems),
89 &g_DevMmioMapEvtItems[0]
90};
91
92
93static const RTTRACELOGEVTITEMDESC g_DevMmioUnmapEvtItems[] =
94{
95 {"hMmioRegion", "The MMIO region handle being unmapped", RTTRACELOGTYPE_UINT64, 0}
96};
97
98static const RTTRACELOGEVTDESC g_DevMmioUnmapEvtDesc =
99{
100 "Dev.MmioUnmap",
101 "MMIO region of a device is being unmapped",
102 RTTRACELOGEVTSEVERITY_DEBUG,
103 RT_ELEMENTS(g_DevMmioUnmapEvtItems),
104 &g_DevMmioUnmapEvtItems[0]
105};
106
107
108static const RTTRACELOGEVTITEMDESC g_DevMmioRwEvtItems[] =
109{
110 {"hMmioRegion", "The MMIO region handle being unmapped", RTTRACELOGTYPE_UINT64, 0},
111 {"offMmio", "The offset in the MMIO region being accessed", RTTRACELOGTYPE_UINT64, 0},
112 {"cbXfer", "Number of bytes being transfered", RTTRACELOGTYPE_UINT64, 0},
113 {"u64Val", "The value read or written", RTTRACELOGTYPE_UINT64, 0},
114};
115
116static const RTTRACELOGEVTDESC g_DevMmioReadEvtDesc =
117{
118 "Dev.MmioRead",
119 "MMIO region of a device is being read",
120 RTTRACELOGEVTSEVERITY_DEBUG,
121 RT_ELEMENTS(g_DevMmioRwEvtItems),
122 &g_DevMmioRwEvtItems[0]
123};
124
125static const RTTRACELOGEVTDESC g_DevMmioWriteEvtDesc =
126{
127 "Dev.MmioWrite",
128 "MMIO region of a device is being written",
129 RTTRACELOGEVTSEVERITY_DEBUG,
130 RT_ELEMENTS(g_DevMmioRwEvtItems),
131 &g_DevMmioRwEvtItems[0]
132};
133
134
135static const RTTRACELOGEVTITEMDESC g_DevMmioFillEvtItems[] =
136{
137 {"hMmioRegion", "The MMIO region handle being unmapped", RTTRACELOGTYPE_UINT64, 0},
138 {"offMmio", "The offset in the MMIO region being accessed", RTTRACELOGTYPE_UINT64, 0},
139 {"cbItem", "Item size in bytes", RTTRACELOGTYPE_UINT32, 0},
140 {"cItems", "Number of items being written", RTTRACELOGTYPE_UINT32, 0},
141 {"u32Val", "The value used for filling", RTTRACELOGTYPE_UINT32, 0},
142};
143
144static const RTTRACELOGEVTDESC g_DevMmioFillEvtDesc =
145{
146 "Dev.MmioFill",
147 "MMIO region of a device is being filled",
148 RTTRACELOGEVTSEVERITY_DEBUG,
149 RT_ELEMENTS(g_DevMmioFillEvtItems),
150 &g_DevMmioFillEvtItems[0]
151};
152
153
154static const RTTRACELOGEVTITEMDESC g_DevIoPortMapEvtItems[] =
155{
156 {"hIoPorts", "The I/O port region handle being mapped", RTTRACELOGTYPE_UINT64, 0},
157 {"IoPortBase", "The I/O port base address where the region is mapped", RTTRACELOGTYPE_UINT16, 0}
158};
159
160static const RTTRACELOGEVTDESC g_DevIoPortMapEvtDesc =
161{
162 "Dev.IoPortMap",
163 "I/O port region of a device is being mapped",
164 RTTRACELOGEVTSEVERITY_DEBUG,
165 RT_ELEMENTS(g_DevIoPortMapEvtItems),
166 &g_DevIoPortMapEvtItems[0]
167};
168
169
170static const RTTRACELOGEVTITEMDESC g_DevIoPortUnmapEvtItems[] =
171{
172 {"hIoPorts", "The I/O port region handle being unmapped", RTTRACELOGTYPE_UINT64, 0}
173};
174
175static const RTTRACELOGEVTDESC g_DevIoPortUnmapEvtDesc =
176{
177 "Dev.IoPortUnmap",
178 "I/O port region of a device is being unmapped",
179 RTTRACELOGEVTSEVERITY_DEBUG,
180 RT_ELEMENTS(g_DevIoPortUnmapEvtItems),
181 &g_DevIoPortUnmapEvtItems[0]
182};
183
184
185static const RTTRACELOGEVTITEMDESC g_DevIoPortRwEvtItems[] =
186{
187 {"hIoPorts", "The MMIO region handle being unmapped", RTTRACELOGTYPE_UINT64, 0},
188 {"offPort", "The offset in the I/O port region being accessed", RTTRACELOGTYPE_UINT16, 0},
189 {"cbXfer", "Number of bytes being transfered", RTTRACELOGTYPE_UINT64, 0},
190 {"u32Val", "The value read or written", RTTRACELOGTYPE_UINT32, 0},
191};
192
193static const RTTRACELOGEVTDESC g_DevIoPortReadEvtDesc =
194{
195 "Dev.IoPortRead",
196 "I/O port region of a device is being read",
197 RTTRACELOGEVTSEVERITY_DEBUG,
198 RT_ELEMENTS(g_DevIoPortRwEvtItems),
199 &g_DevIoPortRwEvtItems[0]
200};
201
202static const RTTRACELOGEVTDESC g_DevIoPortWriteEvtDesc =
203{
204 "Dev.IoPortWrite",
205 "I/O port region of a device is being written",
206 RTTRACELOGEVTSEVERITY_DEBUG,
207 RT_ELEMENTS(g_DevIoPortRwEvtItems),
208 &g_DevIoPortRwEvtItems[0]
209};
210
211
212static const RTTRACELOGEVTITEMDESC g_DevIrqEvtItems[] =
213{
214 {"iIrq", "The IRQ line", RTTRACELOGTYPE_INT32, 0},
215 {"fIrqLvl", "The IRQ level", RTTRACELOGTYPE_INT32, 0}
216};
217
218static const RTTRACELOGEVTDESC g_DevIrqEvtDesc =
219{
220 "Dev.Irq",
221 "Device raised or lowered an IRQ line",
222 RTTRACELOGEVTSEVERITY_DEBUG,
223 RT_ELEMENTS(g_DevIrqEvtItems),
224 &g_DevIrqEvtItems[0]
225};
226
227
228static const RTTRACELOGEVTITEMDESC g_DevIoApicMsiEvtItems[] =
229{
230 {"GCPhys", "Physical guest address being written", RTTRACELOGTYPE_UINT64, 0},
231 {"u32Val", "value being written", RTTRACELOGTYPE_UINT32, 0}
232};
233
234static const RTTRACELOGEVTDESC g_DevIoApicMsiEvtDesc =
235{
236 "Dev.IoApicMsi",
237 "Device sent a MSI event through the I/O APIC",
238 RTTRACELOGEVTSEVERITY_DEBUG,
239 RT_ELEMENTS(g_DevIoApicMsiEvtItems),
240 &g_DevIoApicMsiEvtItems[0]
241};
242
243
244static const RTTRACELOGEVTITEMDESC g_DevGCPhysRwStartEvtItems[] =
245{
246 {"GCPhys", "Physical guest address being accessed", RTTRACELOGTYPE_UINT64, 0},
247 {"cbXfer", "Number of bytes being transfered", RTTRACELOGTYPE_UINT64, 0},
248};
249
250
251static const RTTRACELOGEVTDESC g_DevGCPhysReadEvtDesc =
252{
253 "Dev.GCPhysRead",
254 "Device read data from guest physical memory",
255 RTTRACELOGEVTSEVERITY_DEBUG,
256 RT_ELEMENTS(g_DevGCPhysRwStartEvtItems),
257 &g_DevGCPhysRwStartEvtItems[0]
258};
259
260
261static const RTTRACELOGEVTDESC g_DevGCPhysWriteEvtDesc =
262{
263 "Dev.GCPhysWrite",
264 "Device wrote data to guest physical memory",
265 RTTRACELOGEVTSEVERITY_DEBUG,
266 RT_ELEMENTS(g_DevGCPhysRwStartEvtItems),
267 &g_DevGCPhysRwStartEvtItems[0]
268};
269
270
271static const RTTRACELOGEVTITEMDESC g_DevGCPhysRwDataEvtItems[] =
272{
273 {"abData", "The data being read/written", RTTRACELOGTYPE_RAWDATA, 0}
274};
275
276static const RTTRACELOGEVTDESC g_DevGCPhysRwDataEvtDesc =
277{
278 "Dev.GCPhysRwData",
279 "The data being read or written",
280 RTTRACELOGEVTSEVERITY_DEBUG,
281 RT_ELEMENTS(g_DevGCPhysRwDataEvtItems),
282 &g_DevGCPhysRwDataEvtItems[0]
283};
284
285
286/*********************************************************************************************************************************
287* Internal Functions *
288*********************************************************************************************************************************/
289
290
291/**
292 * Returns an unused guest memory read/write data aggregation structure.
293 *
294 * @returns Pointer to a new aggregation structure or NULL if out of memory.
295 * @param pThis The DBGF tracer instance.
296 */
297static PDBGFTRACERGCPHYSRWAGG dbgfTracerR3EvtGCPhysRwAggNew(PDBGFTRACERINSR3 pThis)
298{
299 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aGstMemRwData); i++)
300 {
301 if (pThis->aGstMemRwData[i].idEvtStart == DBGF_TRACER_EVT_HDR_ID_INVALID)
302 return &pThis->aGstMemRwData[i];
303 }
304
305 return NULL;
306}
307
308
309/**
310 * Find the guest memory read/write data aggregation structure for the given event ID.
311 *
312 * @returns Pointer to a new aggregation structure or NULL if not found.
313 * @param pThis The DBGF tracer instance.
314 * @param idEvtPrev The event ID to look for.
315 */
316static PDBGFTRACERGCPHYSRWAGG dbgfTracerR3EvtGCPhysRwAggFind(PDBGFTRACERINSR3 pThis, uint64_t idEvtPrev)
317{
318 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aGstMemRwData); i++)
319 {
320 if ( pThis->aGstMemRwData[i].idEvtStart != DBGF_TRACER_EVT_HDR_ID_INVALID
321 && pThis->aGstMemRwData[i].idEvtPrev == idEvtPrev)
322 return &pThis->aGstMemRwData[i];
323 }
324
325 return NULL;
326}
327
328
329/**
330 * Starts a new guest memory read/write event.
331 *
332 * @returns VBox status code.
333 * @param pThis The DBGF tracer instance.
334 * @param pEvtHdr The event header.
335 * @param pEvtGCPhysRw The guest memory read/write event descriptor.
336 * @param pEvtDesc The event descriptor written to the trace log.
337 */
338static int dbgfTracerR3EvtGCPhysRwStart(PDBGFTRACERINSR3 pThis, PCDBGFTRACEREVTHDR pEvtHdr,
339 PCDBGFTRACEREVTGCPHYS pEvtGCPhysRw, PCRTTRACELOGEVTDESC pEvtDesc)
340{
341 /* Write out the event header first in any case. */
342 int rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, pEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_START,
343 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtGCPhysRw->GCPhys, pEvtGCPhysRw->cbXfer);
344 if (RT_SUCCESS(rc))
345 {
346 /*
347 * If the amount of data is small enough to fit into the single event descriptor we can skip allocating
348 * an aggregation tracking structure and write the event containing the complete data out immediately.
349 */
350 if (pEvtGCPhysRw->cbXfer <= sizeof(pEvtGCPhysRw->abData))
351 {
352 size_t cbEvtData = pEvtGCPhysRw->cbXfer;
353
354 rc = RTTraceLogWrEvtAdd(pThis->hTraceLog, &g_DevGCPhysRwDataEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_FINISH,
355 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, &pEvtGCPhysRw->abData[0], &cbEvtData);
356 }
357 else
358 {
359 /* Slow path, find an empty aggregation structure. */
360 PDBGFTRACERGCPHYSRWAGG pDataAgg = dbgfTracerR3EvtGCPhysRwAggNew(pThis);
361 if (RT_LIKELY(pDataAgg))
362 {
363 /* Initialize it. */
364 pDataAgg->idEvtStart = pEvtHdr->idEvt;
365 pDataAgg->idEvtPrev = pEvtHdr->idEvt;
366 pDataAgg->cbXfer = pEvtGCPhysRw->cbXfer;
367 pDataAgg->cbLeft = pDataAgg->cbXfer;
368 pDataAgg->offBuf = 0;
369
370 /* Need to reallocate the buffer to hold the complete data? */
371 if (RT_UNLIKELY(pDataAgg->cbBufMax < pDataAgg->cbXfer))
372 {
373 uint8_t *pbBufNew = (uint8_t *)RTMemRealloc(pDataAgg->pbBuf, pDataAgg->cbXfer);
374 if (RT_LIKELY(pbBufNew))
375 {
376 pDataAgg->pbBuf = pbBufNew;
377 pDataAgg->cbBufMax = pDataAgg->cbXfer;
378 }
379 else
380 rc = VERR_NO_MEMORY;
381 }
382
383 if (RT_SUCCESS(rc))
384 {
385 memcpy(pDataAgg->pbBuf, &pEvtGCPhysRw->abData[0], sizeof(pEvtGCPhysRw->abData));
386 pDataAgg->offBuf += sizeof(pEvtGCPhysRw->abData);
387 pDataAgg->cbLeft -= sizeof(pEvtGCPhysRw->abData);
388 }
389 }
390 else
391 rc = VERR_NO_MEMORY;
392
393 if (RT_FAILURE(rc))
394 {
395 LogRelMax(10, ("DBGF: Creating new data aggregation structure for guest memory read/write failed with %Rrc, trace log will not contain data for this event!\n", rc));
396
397 /* Write out the finish event without any data. */
398 size_t cbEvtData = 0;
399 rc = RTTraceLogWrEvtAdd(pThis->hTraceLog, &g_DevGCPhysRwDataEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_FINISH,
400 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, NULL, &cbEvtData);
401 if (pDataAgg) /* Reset the aggregation event. */
402 pDataAgg->idEvtStart = DBGF_TRACER_EVT_HDR_ID_INVALID;
403 }
404 }
405 }
406
407 return rc;
408}
409
410
411/**
412 * Continues a previously started guest memory read/write event.
413 *
414 * @returns VBox status code.
415 * @param pThis The DBGF tracer instance.
416 * @param pEvtHdr The event header.
417 * @param pvData The data to log.
418 */
419static int dbgfTracerR3EvtGCPhysRwContinue(PDBGFTRACERINSR3 pThis, PCDBGFTRACEREVTHDR pEvtHdr, void *pvData)
420{
421 int rc = VINF_SUCCESS;
422 PDBGFTRACERGCPHYSRWAGG pDataAgg = dbgfTracerR3EvtGCPhysRwAggFind(pThis, pEvtHdr->idEvtPrev);
423
424 if (RT_LIKELY(pDataAgg))
425 {
426 size_t cbThisXfer = RT_MIN(pDataAgg->cbLeft, DBGF_TRACER_EVT_PAYLOAD_SZ);
427
428 memcpy(pDataAgg->pbBuf + pDataAgg->offBuf, pvData, cbThisXfer);
429 pDataAgg->offBuf += cbThisXfer;
430 pDataAgg->cbLeft -= cbThisXfer;
431
432 if (!pDataAgg->cbLeft)
433 {
434 /* All data aggregated, write it out and reset the structure. */
435 rc = RTTraceLogWrEvtAdd(pThis->hTraceLog, &g_DevGCPhysRwDataEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_FINISH,
436 pDataAgg->idEvtStart, pEvtHdr->hEvtSrc, pDataAgg->pbBuf, &pDataAgg->cbXfer);
437 pDataAgg->offBuf = 0;
438 pDataAgg->idEvtStart = DBGF_TRACER_EVT_HDR_ID_INVALID;
439 }
440 else
441 pDataAgg->idEvtPrev = pEvtHdr->idEvt; /* So the next event containing more data can find the aggregation structure. */
442 }
443 else /* This can only happen if creating a new structure failed before. */
444 rc = VERR_DBGF_TRACER_IPE_1;
445
446 return rc;
447}
448
449
450/**
451 * Processes the given event.
452 *
453 * @returns VBox status code.
454 * @param pThis The DBGF tracer instance.
455 * @param pEvtHdr The event to process.
456 */
457static int dbgfR3TracerEvtProcess(PDBGFTRACERINSR3 pThis, PDBGFTRACEREVTHDR pEvtHdr)
458{
459 int rc = VINF_SUCCESS;
460
461 LogFlowFunc(("pThis=%p pEvtHdr=%p{idEvt=%llu,enmEvt=%u}\n",
462 pThis, pEvtHdr, pEvtHdr->idEvt, pEvtHdr->enmEvt));
463
464 switch (pEvtHdr->enmEvt)
465 {
466 case DBGFTRACEREVT_SRC_REGISTER:
467 {
468 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_EvtSrcRegisterEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_START,
469 pEvtHdr->hEvtSrc, 0 /*uParentGrpId*/);
470 break;
471 }
472 case DBGFTRACEREVT_SRC_DEREGISTER:
473 {
474 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_EvtSrcDeregisterEvtDesc, RTTRACELOG_WR_ADD_EVT_F_GRP_FINISH,
475 pEvtHdr->hEvtSrc, 0 /*uParentGrpId*/);
476 break;
477 }
478 case DBGFTRACEREVT_MMIO_MAP:
479 {
480 PCDBGFTRACEREVTMMIOMAP pEvtMmioMap = (PCDBGFTRACEREVTMMIOMAP)(pEvtHdr + 1);
481
482 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevMmioMapEvtDesc, 0 /*fFlags*/,
483 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtMmioMap->hMmioRegion, pEvtMmioMap->GCPhysMmioBase);
484 break;
485 }
486 case DBGFTRACEREVT_MMIO_UNMAP:
487 {
488 PCDBGFTRACEREVTMMIOUNMAP pEvtMmioUnmap = (PCDBGFTRACEREVTMMIOUNMAP)(pEvtHdr + 1);
489
490 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevMmioUnmapEvtDesc, 0 /*fFlags*/,
491 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtMmioUnmap->hMmioRegion);
492 break;
493 }
494 case DBGFTRACEREVT_MMIO_READ:
495 case DBGFTRACEREVT_MMIO_WRITE:
496 {
497 PCDBGFTRACEREVTMMIO pEvtMmioRw = (PCDBGFTRACEREVTMMIO)(pEvtHdr + 1);
498
499 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog,
500 pEvtHdr->enmEvt == DBGFTRACEREVT_MMIO_READ
501 ? &g_DevMmioReadEvtDesc
502 : &g_DevMmioWriteEvtDesc,
503 0 /*fFlags*/,
504 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtMmioRw->hMmioRegion, pEvtMmioRw->offMmio,
505 pEvtMmioRw->cbXfer, pEvtMmioRw->u64Val);
506 break;
507 }
508 case DBGFTRACEREVT_MMIO_FILL:
509 {
510 PCDBGFTRACEREVTMMIOFILL pEvtMmioFill = (PCDBGFTRACEREVTMMIOFILL)(pEvtHdr + 1);
511
512 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevMmioFillEvtDesc, 0 /*fFlags*/,
513 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtMmioFill->hMmioRegion, pEvtMmioFill->offMmio,
514 pEvtMmioFill->cbItem, pEvtMmioFill->cItems, pEvtMmioFill->u32Item);
515 break;
516 }
517 case DBGFTRACEREVT_IOPORT_MAP:
518 {
519 PCDBGFTRACEREVTIOPORTMAP pEvtIoPortMap = (PCDBGFTRACEREVTIOPORTMAP)(pEvtHdr + 1);
520
521 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevIoPortMapEvtDesc, 0 /*fFlags*/,
522 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtIoPortMap->hIoPorts, pEvtIoPortMap->IoPortBase);
523 break;
524 }
525 case DBGFTRACEREVT_IOPORT_UNMAP:
526 {
527 PCDBGFTRACEREVTIOPORTUNMAP pEvtIoPortUnmap = (PCDBGFTRACEREVTIOPORTUNMAP)(pEvtHdr + 1);
528
529 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevIoPortUnmapEvtDesc, 0 /*fFlags*/,
530 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtIoPortUnmap->hIoPorts);
531 break;
532 }
533 case DBGFTRACEREVT_IOPORT_READ:
534 case DBGFTRACEREVT_IOPORT_WRITE:
535 {
536 PCDBGFTRACEREVTIOPORT pEvtIoPortRw = (PCDBGFTRACEREVTIOPORT)(pEvtHdr + 1);
537
538 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog,
539 pEvtHdr->enmEvt == DBGFTRACEREVT_IOPORT_READ
540 ? &g_DevIoPortReadEvtDesc
541 : &g_DevIoPortWriteEvtDesc,
542 0 /*fFlags*/,
543 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtIoPortRw->hIoPorts, pEvtIoPortRw->offPort,
544 pEvtIoPortRw->cbXfer, pEvtIoPortRw->u32Val);
545 break;
546 }
547 case DBGFTRACEREVT_IRQ:
548 {
549 PCDBGFTRACEREVTIRQ pEvtIrq = (PCDBGFTRACEREVTIRQ)(pEvtHdr + 1);
550
551 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevIrqEvtDesc, 0 /*fFlags*/,
552 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtIrq->iIrq, pEvtIrq->fIrqLvl);
553 break;
554 }
555 case DBGFTRACEREVT_IOAPIC_MSI:
556 {
557 PCDBGFTRACEREVTIOAPICMSI pEvtIoApicMsi = (PCDBGFTRACEREVTIOAPICMSI)(pEvtHdr + 1);
558
559 rc = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_DevIrqEvtDesc, 0 /*fFlags*/,
560 pEvtHdr->idEvt, pEvtHdr->hEvtSrc, pEvtIoApicMsi->GCPhys, pEvtIoApicMsi->u32Val);
561 break;
562 }
563 case DBGFTRACEREVT_GCPHYS_READ:
564 case DBGFTRACEREVT_GCPHYS_WRITE:
565 {
566 PCRTTRACELOGEVTDESC pEvtDesc = pEvtHdr->enmEvt == DBGFTRACEREVT_GCPHYS_WRITE
567 ? &g_DevGCPhysWriteEvtDesc
568 : &g_DevGCPhysReadEvtDesc;
569
570 /* If the previous event ID is invalid this starts a new read/write we have to aggregate all the data for. */
571 if (pEvtHdr->idEvtPrev == DBGF_TRACER_EVT_HDR_ID_INVALID)
572 {
573 PCDBGFTRACEREVTGCPHYS pEvtGCPhysRw = (PCDBGFTRACEREVTGCPHYS)(pEvtHdr + 1);
574 rc = dbgfTracerR3EvtGCPhysRwStart(pThis, pEvtHdr, pEvtGCPhysRw, pEvtDesc);
575 }
576 else
577 {
578 /* Continuation of a started read or write, look up the right tracking structure and process the new data. */
579 void *pvData = pEvtHdr + 1;
580 rc = dbgfTracerR3EvtGCPhysRwContinue(pThis, pEvtHdr, pvData);
581 }
582 break;
583 }
584 default:
585 AssertLogRelMsgFailed(("Invalid or unsupported event: %u!\n", pEvtHdr->enmEvt));
586 break;
587 }
588
589 return rc;
590}
591
592
593/**
594 * @callback_method_impl{FNRTTHREAD,
595 * DBGF Tracer flush thread}
596 */
597static DECLCALLBACK(int) dbgfR3TracerThreadFlush(RTTHREAD ThreadSelf, void *pvUser)
598{
599 PDBGFTRACERINSR3 pThis = (PDBGFTRACERINSR3)pvUser;
600 PDBGFTRACERSHARED pShared = pThis->pSharedR3;
601 PSUPDRVSESSION pSession = pThis->pVMR3->pSession;
602
603 /* Release the waiter. */
604 RTThreadUserSignal(ThreadSelf);
605
606 /*
607 * Process stuff until we're told to terminate.
608 */
609 for (;;)
610 {
611 ASMAtomicXchgBool(&pShared->fFlushThrdActive, false);
612 if (!ASMAtomicXchgBool(&pShared->fEvtsWaiting, false))
613 {
614 int rc = SUPSemEventWaitNoResume(pSession, pShared->hSupSemEvtFlush, RT_INDEFINITE_WAIT);
615 AssertRC(rc);
616
617 if (RT_UNLIKELY(ASMAtomicReadBool(&pThis->fShutdown)))
618 break;
619 }
620
621 ASMAtomicXchgBool(&pShared->fFlushThrdActive, true);
622
623 uint64_t idEvtNow = ASMAtomicReadU64(&pShared->idEvt);
624 uint64_t idEvt = pThis->idEvtLast;
625 size_t cRingBufEvts = pShared->cbRingBuf / DBGF_TRACER_EVT_SZ;
626 while (idEvt < idEvtNow)
627 {
628 uint64_t idxRingBuf = idEvt % cRingBufEvts; /* This gives the index in the ring buffer for the event. */
629 PDBGFTRACEREVTHDR pEvtHdr = (PDBGFTRACEREVTHDR)(pThis->CTX_SUFF(pbRingBuf) + idxRingBuf * DBGF_TRACER_EVT_SZ);
630
631 /*
632 * If the event header contains the invalid ID the producer was interrupted or didn't get that far yet, spin a bit
633 * and wait for the ID to become valid.
634 */
635 while (ASMAtomicReadU64(&pEvtHdr->idEvt) == DBGF_TRACER_EVT_HDR_ID_INVALID)
636 RTThreadYield();
637
638 int rc = dbgfR3TracerEvtProcess(pThis, pEvtHdr);
639 if (RT_FAILURE(rc))
640 LogRelMax(10, ("DBGF: Writing event failed with %Rrc, tracing log will be incomplete!\n", rc));
641
642 ASMAtomicWriteU64(&pEvtHdr->idEvt, DBGF_TRACER_EVT_HDR_ID_INVALID);
643 idEvt++;
644 }
645
646 pThis->idEvtLast = idEvt;
647 ASMAtomicXchgBool(&pShared->fEvtsWaiting, false);
648 }
649
650 return VINF_SUCCESS;
651}
652
653
654/**
655 * Registers a possible event descriptors with the created trace log for faster subsequent operations.
656 *
657 * @returns VBox status code.
658 * @param pThis The DBGF tracer instance.
659 */
660static int dbgfR3TracerTraceLogEvtDescRegister(PDBGFTRACERINSR3 pThis)
661{
662 int rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevMmioMapEvtDesc);
663 if (RT_SUCCESS(rc))
664 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevMmioUnmapEvtDesc);
665 if (RT_SUCCESS(rc))
666 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevMmioReadEvtDesc);
667 if (RT_SUCCESS(rc))
668 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevMmioWriteEvtDesc);
669 if (RT_SUCCESS(rc))
670 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIoPortMapEvtDesc);
671 if (RT_SUCCESS(rc))
672 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIoPortUnmapEvtDesc);
673 if (RT_SUCCESS(rc))
674 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIoPortReadEvtDesc);
675 if (RT_SUCCESS(rc))
676 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIoPortWriteEvtDesc);
677 if (RT_SUCCESS(rc))
678 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIrqEvtDesc);
679 if (RT_SUCCESS(rc))
680 rc = RTTraceLogWrAddEvtDesc(pThis->hTraceLog, &g_DevIoApicMsiEvtDesc);
681
682 return rc;
683}
684
685
686/**
687 * Initializes the R3 and shared tarcer instance data and spins up the flush thread.
688 *
689 * @returns VBox status code.
690 * @param pThis The DBGF tracer instance.
691 * @param pszTraceFilePath The path of the trace file to create.
692 */
693static int dbgfR3TracerInitR3(PDBGFTRACERINSR3 pThis, const char *pszTraceFilePath)
694{
695 PVM pVM = pThis->pVMR3;
696 PDBGFTRACERSHARED pShared = pThis->pSharedR3;
697
698 pThis->fShutdown = false;
699
700 for (uint32_t i = 0; i < RT_ELEMENTS(pThis->aGstMemRwData); i++)
701 pThis->aGstMemRwData[i].idEvtStart = DBGF_TRACER_EVT_HDR_ID_INVALID;
702
703 /* Try to create a file based trace log. */
704 int rc = RTTraceLogWrCreateFile(&pThis->hTraceLog, RTBldCfgVersion(), pszTraceFilePath);
705 AssertLogRelRCReturn(rc, rc);
706
707 rc = dbgfR3TracerTraceLogEvtDescRegister(pThis);
708 AssertLogRelRCReturn(rc, rc);
709
710 /*
711 * Go through the whole ring buffer and initialize the event IDs of all entries
712 * to invalid values.
713 */
714 uint64_t cEvtEntries = pShared->cbRingBuf / DBGF_TRACER_EVT_SZ;
715 PDBGFTRACEREVTHDR pEvtHdr = (PDBGFTRACEREVTHDR)pThis->pbRingBufR3;
716 for (uint32_t i = 0; i < cEvtEntries; i++)
717 {
718 pEvtHdr->idEvt = DBGF_TRACER_EVT_HDR_ID_INVALID;
719 pEvtHdr++;
720 }
721
722 rc = SUPSemEventCreate(pVM->pSession, &pShared->hSupSemEvtFlush);
723 if (RT_SUCCESS(rc))
724 {
725 rc = RTThreadCreate(&pThis->hThrdFlush, dbgfR3TracerThreadFlush, pThis, 0 /*cbStack*/, RTTHREADTYPE_IO,
726 RTTHREADFLAGS_WAITABLE, "DBGFTracer");
727 if (RT_SUCCESS(rc))
728 {
729 rc = RTThreadUserWait(pThis->hThrdFlush, 10 * 1000);
730 if (RT_SUCCESS(rc))
731 {
732 return VINF_SUCCESS;
733 }
734 }
735
736 SUPSemEventClose(pVM->pSession, pShared->hSupSemEvtFlush);
737 }
738
739 return rc;
740}
741
742
743/**
744 * Creates a DBGF tracer based on the given config and returns it.
745 *
746 * @returns VBox status code.
747 * @param pVM The cross context VM structure.
748 * @param fR0Enabled Flag whether the tracer should have R0 support enabled.
749 * @param pszTraceFilePath The path of the trace file to create.
750 * @param cbRingBuf Size of the ring buffer in bytes.
751 * @param ppDbgfTracerR3 Where to store the pointer to the tracer on success.
752 */
753DECLHIDDEN(int) dbgfR3TracerCreate(PVM pVM, bool fR0Enabled, const char *pszTraceFilePath,
754 size_t cbRingBuf, PDBGFTRACERINSR3 *ppDbgfTracerR3)
755{
756 PDBGFTRACERINSR3 pThis = NULL;
757
758 /*
759 * Allocate the tracer instance.
760 */
761 if (fR0Enabled /*|| fRCEnabled*/)
762 {
763 AssertLogRel(fR0Enabled /* not possible to just enabled raw-mode atm. */);
764
765 DBGFTRACERCREATEREQ Req;
766 Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
767 Req.Hdr.cbReq = sizeof(Req);
768 Req.pTracerInsR3 = NULL;
769 Req.cbRingBuf = cbRingBuf;
770 Req.fRCEnabled = false; /*fRCEnabled;*/
771 Req.afReserved[0] = false;
772 Req.afReserved[1] = false;
773 Req.afReserved[2] = false;
774 int rc = VMMR3CallR0Emt(pVM, pVM->apCpusR3[0], VMMR0_DO_DBGF_TRACER_CREATE, 0, &Req.Hdr);
775 AssertLogRelMsgRCReturn(rc, ("VMMR0_DO_DBGF_TRACER_CREATE failed: %Rrc\n", rc), rc);
776 pThis = Req.pTracerInsR3;
777 }
778 else
779 {
780 /* The code in this else branch works by the same rules as the DBGFR0Tracer.cpp
781 code, except there is only the ring-3 components of the tracer instance.
782 Changes here may need to be reflected in DBGFR0Tracer.cpp and vice versa! */
783 uint32_t cb = sizeof(DBGFTRACERINSR3);
784 cb = RT_ALIGN_32(cb, 64);
785 const uint32_t offShared = cb;
786 cb += sizeof(DBGFTRACERSHARED) + cbRingBuf;
787 AssertLogRelMsgReturn(cb <= DBGF_MAX_TRACER_INSTANCE_SIZE_R3,
788 ("Tracer total instance size is to big: %u, max %u\n",
789 cb, DBGF_MAX_TRACER_INSTANCE_SIZE_R3),
790 VERR_ALLOCATION_TOO_BIG);
791
792 int rc = MMR3HeapAllocZEx(pVM, MM_TAG_DBGF_TRACER, cb, (void **)&pThis);
793 AssertLogRelMsgRCReturn(rc, ("Failed to allocate %zu bytes of instance data for tracer. rc=%Rrc\n",
794 cb, rc), rc);
795
796 /* Initialize it: */
797 pThis->pNextR3 = NULL;
798 pThis->pVMR3 = pVM;
799 pThis->fR0Enabled = false;
800 pThis->pSharedR3 = (PDBGFTRACERSHARED)((uint8_t *)pThis + offShared);
801 pThis->pbRingBufR3 = (uint8_t *)(pThis->pSharedR3 + 1);
802
803 pThis->pSharedR3->idEvt = 0;
804 pThis->pSharedR3->cbRingBuf = cbRingBuf;
805 pThis->pSharedR3->fEvtsWaiting = false;
806 pThis->pSharedR3->fFlushThrdActive = false;
807 }
808
809 /* Initialize the rest of the R3 tracer instance and spin up the flush thread. */
810 int rc = dbgfR3TracerInitR3(pThis, pszTraceFilePath);
811 if (RT_SUCCESS(rc))
812 {
813 *ppDbgfTracerR3 = pThis;
814 return rc;
815 }
816
817 /** @todo Cleanup. */
818 LogFlow(("dbgfR3TracerCreate: returns %Rrc\n", rc));
819 return rc;
820}
821
822
823/**
824 * Initializes and configures the tracer if configured.
825 *
826 * @returns VBox status code.
827 * @param pVM The cross context VM pointer.
828 */
829DECLHIDDEN(int) dbgfR3TracerInit(PVM pVM)
830{
831 PUVM pUVM = pVM->pUVM;
832
833 pUVM->dbgf.s.pTracerR3 = NULL;
834
835 /*
836 * Check the config and enable tracing if requested.
837 */
838 PCFGMNODE pDbgfNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF");
839 bool fTracerEnabled;
840 int rc = CFGMR3QueryBoolDef(pDbgfNode, "TracerEnabled", &fTracerEnabled, false);
841 AssertRCReturn(rc, rc);
842 if (fTracerEnabled)
843 {
844 bool fR0Enabled;
845 uint64_t cbRingBuf = 0;
846 char *pszTraceFilePath = NULL;
847 rc = CFGMR3QueryBoolDef(pDbgfNode, "TracerR0Enabled", &fR0Enabled, false);
848 if (RT_SUCCESS(rc))
849 rc = CFGMR3QueryU64Def(pDbgfNode, "TracerRingBufSz", &cbRingBuf, _4M);
850 if (RT_SUCCESS(rc))
851 rc = CFGMR3QueryStringAlloc(pDbgfNode, "TracerFilePath", &pszTraceFilePath);
852 if (RT_SUCCESS(rc))
853 {
854 AssertLogRelMsgReturn(cbRingBuf && cbRingBuf == (size_t)cbRingBuf,
855 ("Tracing ringbuffer size %#RX64 is invalid\n", cbRingBuf),
856 VERR_INVALID_PARAMETER);
857
858 rc = dbgfR3TracerCreate(pVM, fR0Enabled, pszTraceFilePath, cbRingBuf, &pUVM->dbgf.s.pTracerR3);
859 }
860
861 if (pszTraceFilePath)
862 {
863 MMR3HeapFree(pszTraceFilePath);
864 pszTraceFilePath = NULL;
865 }
866 }
867
868 return rc;
869}
870
871
872/**
873 * Terminates any configured tracer for the given VM instance.
874 *
875 * @returns nothing.
876 * @param pVM The cross context VM structure.
877 */
878DECLHIDDEN(void) dbgfR3TracerTerm(PVM pVM)
879{
880 PUVM pUVM = pVM->pUVM;
881
882 if (pUVM->dbgf.s.pTracerR3)
883 {
884 PDBGFTRACERINSR3 pThis = pUVM->dbgf.s.pTracerR3;
885 PDBGFTRACERSHARED pSharedR3 = pThis->CTX_SUFF(pShared);
886
887 /* Tear down the flush thread. */
888 ASMAtomicXchgBool(&pThis->fShutdown, true);
889 SUPSemEventSignal(pVM->pSession, pSharedR3->hSupSemEvtFlush);
890
891 int rc = RTThreadWait(pThis->hThrdFlush, RT_MS_30SEC, NULL);
892 AssertLogRelMsgRC(rc, ("DBGF: Waiting for the tracer flush thread to terminate failed with %Rrc\n", rc));
893
894 /* Close the trace log. */
895 rc = RTTraceLogWrDestroy(pThis->hTraceLog);
896 AssertLogRelMsgRC(rc, ("DBGF: Closing the trace log file failed with %Rrc\n", rc));
897
898 SUPSemEventClose(pVM->pSession, pSharedR3->hSupSemEvtFlush);
899 /* The instance memory is freed by MM or when the R0 component terminates. */
900 pUVM->dbgf.s.pTracerR3 = NULL;
901 }
902}
903
904
905/**
906 * Registers a new event source with the given name and returns a tracer event source handle.
907 *
908 * @returns VBox status code.
909 * @param pVM The cross context VM structure.
910 * @param pszName The event source name.
911 * @param phEvtSrc Where to return the handle to the event source on success.
912 */
913VMMR3_INT_DECL(int) DBGFR3TracerRegisterEvtSrc(PVM pVM, const char *pszName, PDBGFTRACEREVTSRC phEvtSrc)
914{
915 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
916 AssertReturn(pszName && *pszName != '\0', VERR_INVALID_PARAMETER);
917 AssertPtrReturn(phEvtSrc, VERR_INVALID_POINTER);
918
919 PUVM pUVM = pVM->pUVM;
920 PDBGFTRACERINSR3 pThis = pUVM->dbgf.s.pTracerR3;
921
922 DBGFTRACEREVTSRC hEvtSrc = ASMAtomicIncU64((volatile uint64_t *)&pThis->hEvtSrcNext) - 1;
923
924 int rc = dbgfTracerR3EvtPostSingle(pVM, pThis, hEvtSrc, DBGFTRACEREVT_SRC_REGISTER,
925 NULL /*pvEvtDesc*/, 0 /*cbEvtDesc*/, NULL /*pidEvt*/);
926 if (RT_SUCCESS(rc))
927 *phEvtSrc = hEvtSrc;
928
929 return rc;
930}
931
932
933/**
934 * Deregisters the given event source handle.
935 *
936 * @returns VBox status code.
937 * @param pVM The cross context VM structure.
938 * @param hEvtSrc The event source handle to deregister.
939 */
940VMMR3_INT_DECL(int) DBGFR3TracerDeregisterEvtSrc(PVM pVM, DBGFTRACEREVTSRC hEvtSrc)
941{
942 VM_ASSERT_EMT_RETURN(pVM, VERR_VM_THREAD_NOT_EMT);
943 AssertReturn(hEvtSrc != NIL_DBGFTRACEREVTSRC, VERR_INVALID_HANDLE);
944
945 PUVM pUVM = pVM->pUVM;
946 PDBGFTRACERINSR3 pThis = pUVM->dbgf.s.pTracerR3;
947 return dbgfTracerR3EvtPostSingle(pVM, pThis, hEvtSrc, DBGFTRACEREVT_SRC_DEREGISTER,
948 NULL /*pvEvtDesc*/, 0 /*cbEvtDesc*/, NULL /*pidEvt*/);
949}
950
Note: See TracBrowser for help on using the repository browser.

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