VirtualBox

source: vbox/trunk/src/VBox/VMM/VMMR3/DBGFR3Trace.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.8 KB
Line 
1/* $Id: DBGFR3Trace.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DBGF - Debugger Facility, Tracing.
4 */
5
6/*
7 * Copyright (C) 2011-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#define LOG_GROUP LOG_GROUP_DBGF
33#include <VBox/vmm/dbgftrace.h>
34#include <VBox/vmm/cfgm.h>
35#include <VBox/vmm/mm.h>
36#include <VBox/vmm/pdmapi.h>
37#include "DBGFInternal.h"
38#include <VBox/vmm/vm.h>
39#include "VMMTracing.h"
40
41#include <VBox/err.h>
42#include <VBox/log.h>
43#include <VBox/param.h>
44
45#include <iprt/assert.h>
46#include <iprt/ctype.h>
47#include <iprt/trace.h>
48
49
50/*********************************************************************************************************************************
51* Internal Functions *
52*********************************************************************************************************************************/
53static DECLCALLBACK(void) dbgfR3TraceInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs);
54
55
56/*********************************************************************************************************************************
57* Global Variables *
58*********************************************************************************************************************************/
59/**
60 * VMM trace point group translation table.
61 */
62static const struct
63{
64 /** The group name. */
65 const char *pszName;
66 /** The name length. */
67 uint32_t cchName;
68 /** The mask. */
69 uint32_t fMask;
70} g_aVmmTpGroups[] =
71{
72 { RT_STR_TUPLE("em"), VMMTPGROUP_EM },
73 { RT_STR_TUPLE("hm"), VMMTPGROUP_HM },
74 { RT_STR_TUPLE("tm"), VMMTPGROUP_TM },
75};
76
77
78/**
79 * Initializes the tracing.
80 *
81 * @returns VBox status code
82 * @param pVM The cross context VM structure.
83 * @param cbEntry The trace entry size.
84 * @param cEntries The number of entries.
85 */
86static int dbgfR3TraceEnable(PVM pVM, uint32_t cbEntry, uint32_t cEntries)
87{
88 /*
89 * Don't enable it twice.
90 */
91 if (pVM->hTraceBufR3 != NIL_RTTRACEBUF)
92 return VERR_ALREADY_EXISTS;
93
94 /*
95 * Resolve default parameter values.
96 */
97 int rc;
98 if (!cbEntry)
99 {
100 rc = CFGMR3QueryU32Def(CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF"), "TraceBufEntrySize", &cbEntry, 128);
101 AssertRCReturn(rc, rc);
102 }
103 if (!cEntries)
104 {
105 rc = CFGMR3QueryU32Def(CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF"), "TraceBufEntries", &cEntries, 4096);
106 AssertRCReturn(rc, rc);
107 }
108
109 /*
110 * Figure the required size.
111 */
112 RTTRACEBUF hTraceBuf;
113 size_t cbBlock = 0;
114 rc = RTTraceBufCarve(&hTraceBuf, cEntries, cbEntry, 0 /*fFlags*/, NULL, &cbBlock);
115 if (rc != VERR_BUFFER_OVERFLOW)
116 {
117 AssertReturn(!RT_SUCCESS_NP(rc), VERR_IPE_UNEXPECTED_INFO_STATUS);
118 return rc;
119 }
120
121 /*
122 * Allocate a hyper heap block and carve a trace buffer out of it.
123 *
124 * Note! We ASSUME that the returned trace buffer handle has the same value
125 * as the heap block.
126 */
127 cbBlock = RT_ALIGN_Z(cbBlock, HOST_PAGE_SIZE);
128 RTR0PTR pvBlockR0 = NIL_RTR0PTR;
129 void *pvBlockR3 = NULL;
130 rc = SUPR3PageAllocEx(cbBlock >> HOST_PAGE_SHIFT, 0, &pvBlockR3, &pvBlockR0, NULL);
131 if (RT_FAILURE(rc))
132 return rc;
133
134 rc = RTTraceBufCarve(&hTraceBuf, cEntries, cbEntry, 0 /*fFlags*/, pvBlockR3, &cbBlock);
135 AssertRCReturn(rc, rc);
136 AssertReleaseReturn(hTraceBuf == (RTTRACEBUF)pvBlockR3, VERR_INTERNAL_ERROR_3);
137 AssertReleaseReturn((void *)hTraceBuf == pvBlockR3, VERR_INTERNAL_ERROR_3);
138
139 pVM->hTraceBufR3 = hTraceBuf;
140 pVM->hTraceBufR0 = pvBlockR0;
141 return VINF_SUCCESS;
142}
143
144
145/**
146 * Initializes the tracing.
147 *
148 * @returns VBox status code
149 * @param pVM The cross context VM structure.
150 */
151int dbgfR3TraceInit(PVM pVM)
152{
153 /*
154 * Initialize the trace buffer handles.
155 */
156 Assert(NIL_RTTRACEBUF == (RTTRACEBUF)NULL);
157 pVM->hTraceBufR3 = NIL_RTTRACEBUF;
158 pVM->hTraceBufR0 = NIL_RTR0PTR;
159
160 /*
161 * Check the config and enable tracing if requested.
162 */
163 PCFGMNODE pDbgfNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF");
164#if defined(DEBUG) || defined(RTTRACE_ENABLED)
165 bool const fDefault = false;
166 const char * const pszConfigDefault = "";
167#else
168 bool const fDefault = false;
169 const char * const pszConfigDefault = "";
170#endif
171 bool fTracingEnabled;
172 int rc = CFGMR3QueryBoolDef(pDbgfNode, "TracingEnabled", &fTracingEnabled, fDefault);
173 AssertRCReturn(rc, rc);
174 if (fTracingEnabled)
175 {
176 rc = dbgfR3TraceEnable(pVM, 0, 0);
177 if (RT_SUCCESS(rc))
178 {
179 if (pDbgfNode)
180 {
181 char *pszTracingConfig;
182 rc = CFGMR3QueryStringAllocDef(pDbgfNode, "TracingConfig", &pszTracingConfig, pszConfigDefault);
183 if (RT_SUCCESS(rc))
184 {
185 rc = DBGFR3TraceConfig(pVM, pszTracingConfig);
186 if (RT_FAILURE(rc))
187 rc = VMSetError(pVM, rc, RT_SRC_POS, "TracingConfig=\"%s\" -> %Rrc", pszTracingConfig, rc);
188 MMR3HeapFree(pszTracingConfig);
189 }
190 }
191 else
192 {
193 rc = DBGFR3TraceConfig(pVM, pszConfigDefault);
194 if (RT_FAILURE(rc))
195 rc = VMSetError(pVM, rc, RT_SRC_POS, "TracingConfig=\"%s\" (default) -> %Rrc", pszConfigDefault, rc);
196 }
197 }
198 }
199
200 /*
201 * Register a debug info item that will dump the trace buffer content.
202 */
203 if (RT_SUCCESS(rc))
204 rc = DBGFR3InfoRegisterInternal(pVM, "tracebuf", "Display the trace buffer content. No arguments.", dbgfR3TraceInfo);
205
206 return rc;
207}
208
209
210/**
211 * Terminates the tracing.
212 *
213 * @param pVM The cross context VM structure.
214 */
215void dbgfR3TraceTerm(PVM pVM)
216{
217 /* nothing to do */
218 NOREF(pVM);
219}
220
221
222/**
223 * Relocates the trace buffer handle in RC.
224 *
225 * @param pVM The cross context VM structure.
226 */
227void dbgfR3TraceRelocate(PVM pVM)
228{
229 RT_NOREF(pVM);
230}
231
232
233/**
234 * Change the traceing configuration of the VM.
235 *
236 * @returns VBox status code.
237 * @retval VINF_SUCCESS
238 * @retval VERR_NOT_FOUND if any of the trace point groups mentioned in the
239 * config string cannot be found. (Or if the string cannot be made
240 * sense of.) No change made.
241 * @retval VERR_INVALID_VM_HANDLE
242 * @retval VERR_INVALID_POINTER
243 *
244 * @param pVM The cross context VM structure.
245 * @param pszConfig The configuration change specification.
246 *
247 * Trace point group names, optionally prefixed by a '-' to
248 * indicate that the group is being disabled. A special
249 * group 'all' can be used to enable or disable all trace
250 * points.
251 *
252 * Drivers, devices and USB devices each have their own
253 * trace point group which can be accessed by prefixing
254 * their official PDM name by 'drv', 'dev' or 'usb'
255 * respectively.
256 */
257VMMDECL(int) DBGFR3TraceConfig(PVM pVM, const char *pszConfig)
258{
259 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
260 AssertPtrReturn(pszConfig, VERR_INVALID_POINTER);
261 if (pVM->hTraceBufR3 == NIL_RTTRACEBUF)
262 return VERR_DBGF_NO_TRACE_BUFFER;
263
264 /*
265 * We do this in two passes, the first pass just validates the input string
266 * and the second applies the changes.
267 */
268 for (uint32_t uPass = 0; uPass < 1; uPass++)
269 {
270 char ch;
271 while ((ch = *pszConfig) != '\0')
272 {
273 if (RT_C_IS_SPACE(ch))
274 continue;
275
276 /*
277 * Operation prefix.
278 */
279 bool fNo = false;
280 do
281 {
282 if (ch == 'n' && pszConfig[1] == 'o')
283 {
284 fNo = !fNo;
285 pszConfig++;
286 }
287 else if (ch == '+')
288 fNo = false;
289 else if (ch == '-' || ch == '!' || ch == '~')
290 fNo = !fNo;
291 else
292 break;
293 } while ((ch = *++pszConfig) != '\0');
294 if (ch == '\0')
295 break;
296
297 /*
298 * Extract the name.
299 */
300 const char *pszName = pszConfig;
301 while ( ch != '\0'
302 && !RT_C_IS_SPACE(ch)
303 && !RT_C_IS_PUNCT(ch))
304 ch = *++pszConfig;
305 size_t const cchName = pszConfig - pszName;
306
307 /*
308 * 'all' - special group that enables or disables all trace points.
309 */
310 if (cchName == 3 && !strncmp(pszName, "all", 3))
311 {
312 if (uPass != 0)
313 {
314 uint32_t iCpu = pVM->cCpus;
315 if (!fNo)
316 while (iCpu-- > 0)
317 pVM->apCpusR3[iCpu]->fTraceGroups = UINT32_MAX;
318 else
319 while (iCpu-- > 0)
320 pVM->apCpusR3[iCpu]->fTraceGroups = 0;
321 PDMR3TracingConfig(pVM, NULL, 0, !fNo, uPass > 0);
322 }
323 }
324 else
325 {
326 /*
327 * A specific group, try the VMM first then PDM.
328 */
329 uint32_t i = RT_ELEMENTS(g_aVmmTpGroups);
330 while (i-- > 0)
331 if ( g_aVmmTpGroups[i].cchName == cchName
332 && !strncmp(g_aVmmTpGroups[i].pszName, pszName, cchName))
333 {
334 if (uPass != 0)
335 {
336 uint32_t iCpu = pVM->cCpus;
337 if (!fNo)
338 while (iCpu-- > 0)
339 pVM->apCpusR3[iCpu]->fTraceGroups |= g_aVmmTpGroups[i].fMask;
340 else
341 while (iCpu-- > 0)
342 pVM->apCpusR3[iCpu]->fTraceGroups &= ~g_aVmmTpGroups[i].fMask;
343 }
344 break;
345 }
346
347 if (i == UINT32_MAX)
348 {
349 int rc = PDMR3TracingConfig(pVM, pszName, cchName, !fNo, uPass > 0);
350 if (RT_FAILURE(rc))
351 return rc;
352 }
353 }
354 }
355 }
356
357 return VINF_SUCCESS;
358}
359
360
361/**
362 * Query the trace configuration specification string.
363 *
364 * @returns VBox status code.
365 * @retval VINF_SUCCESS
366 * @retval VERR_INVALID_VM_HANDLE
367 * @retval VERR_INVALID_POINTER
368 * @retval VERR_BUFFER_OVERFLOW if the buffer is too small. Buffer will be
369 * empty.
370
371 * @param pVM The cross context VM structure.
372 * @param pszConfig Pointer to the output buffer.
373 * @param cbConfig The size of the output buffer.
374 */
375VMMDECL(int) DBGFR3TraceQueryConfig(PVM pVM, char *pszConfig, size_t cbConfig)
376{
377 VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE);
378 AssertPtrReturn(pszConfig, VERR_INVALID_POINTER);
379 if (cbConfig < 1)
380 return VERR_BUFFER_OVERFLOW;
381 *pszConfig = '\0';
382
383 if (pVM->hTraceBufR3 == NIL_RTTRACEBUF)
384 return VERR_DBGF_NO_TRACE_BUFFER;
385
386 int rc = VINF_SUCCESS;
387 uint32_t const fTraceGroups = pVM->apCpusR3[0]->fTraceGroups;
388 if ( fTraceGroups == UINT32_MAX
389 && PDMR3TracingAreAll(pVM, true /*fEnabled*/))
390 rc = RTStrCopy(pszConfig, cbConfig, "all");
391 else if ( fTraceGroups == 0
392 && PDMR3TracingAreAll(pVM, false /*fEnabled*/))
393 rc = RTStrCopy(pszConfig, cbConfig, "-all");
394 else
395 {
396 char *pszDst = pszConfig;
397 size_t cbDst = cbConfig;
398 uint32_t i = RT_ELEMENTS(g_aVmmTpGroups);
399 while (i-- > 0)
400 if (g_aVmmTpGroups[i].fMask & fTraceGroups)
401 {
402 size_t cchThis = g_aVmmTpGroups[i].cchName + (pszDst != pszConfig);
403 if (cchThis >= cbDst)
404 {
405 rc = VERR_BUFFER_OVERFLOW;
406 break;
407 }
408 if (pszDst != pszConfig)
409 {
410 *pszDst = ' ';
411 memcpy(pszDst + 1, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1);
412 }
413 else
414 memcpy(pszDst, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1);
415 pszDst += cchThis;
416 cbDst -= cchThis;
417 }
418
419 if (RT_SUCCESS(rc))
420 rc = PDMR3TracingQueryConfig(pVM, pszDst, cbDst);
421 }
422
423 if (RT_FAILURE(rc))
424 *pszConfig = '\0';
425 return rc;
426}
427
428
429/**
430 * @callback_method_impl{FNRTTRACEBUFCALLBACK}
431 */
432static DECLCALLBACK(int)
433dbgfR3TraceInfoDumpEntry(RTTRACEBUF hTraceBuf, uint32_t iEntry, uint64_t NanoTS, RTCPUID idCpu, const char *pszMsg, void *pvUser)
434{
435 PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvUser;
436 pHlp->pfnPrintf(pHlp, "#%04u/%'llu/%02x: %s\n", iEntry, NanoTS, idCpu, pszMsg);
437 NOREF(hTraceBuf);
438 return VINF_SUCCESS;
439}
440
441
442/**
443 * @callback_method_impl{FNDBGFHANDLERINT, Info handler for displaying the trace buffer content.}
444 */
445static DECLCALLBACK(void) dbgfR3TraceInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs)
446{
447 RTTRACEBUF hTraceBuf = pVM->hTraceBufR3;
448 if (hTraceBuf == NIL_RTTRACEBUF)
449 pHlp->pfnPrintf(pHlp, "Tracing is disabled\n");
450 else
451 {
452 pHlp->pfnPrintf(pHlp, "Trace buffer %p - %u entries of %u bytes\n",
453 hTraceBuf, RTTraceBufGetEntryCount(hTraceBuf), RTTraceBufGetEntrySize(hTraceBuf));
454 RTTraceBufEnumEntries(hTraceBuf, dbgfR3TraceInfoDumpEntry, (void *)pHlp);
455 }
456 NOREF(pszArgs);
457}
458
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