VirtualBox

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

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

VMM/DBGFTracer: Implement support for guest memory read/write events, bugref:9210

The data from guest memory transfers need to be aggregated from the indivudual event entries
before they can be written out.

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