From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/VMM/VMMR3/DBGFR3Trace.cpp | 458 +++++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 src/VBox/VMM/VMMR3/DBGFR3Trace.cpp (limited to 'src/VBox/VMM/VMMR3/DBGFR3Trace.cpp') diff --git a/src/VBox/VMM/VMMR3/DBGFR3Trace.cpp b/src/VBox/VMM/VMMR3/DBGFR3Trace.cpp new file mode 100644 index 00000000..21a6d66e --- /dev/null +++ b/src/VBox/VMM/VMMR3/DBGFR3Trace.cpp @@ -0,0 +1,458 @@ +/* $Id: DBGFR3Trace.cpp $ */ +/** @file + * DBGF - Debugger Facility, Tracing. + */ + +/* + * Copyright (C) 2011-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#define LOG_GROUP LOG_GROUP_DBGF +#include +#include +#include +#include +#include "DBGFInternal.h" +#include +#include "VMMTracing.h" + +#include +#include +#include + +#include +#include +#include + + +/********************************************************************************************************************************* +* Internal Functions * +*********************************************************************************************************************************/ +static DECLCALLBACK(void) dbgfR3TraceInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs); + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** + * VMM trace point group translation table. + */ +static const struct +{ + /** The group name. */ + const char *pszName; + /** The name length. */ + uint32_t cchName; + /** The mask. */ + uint32_t fMask; +} g_aVmmTpGroups[] = +{ + { RT_STR_TUPLE("em"), VMMTPGROUP_EM }, + { RT_STR_TUPLE("hm"), VMMTPGROUP_HM }, + { RT_STR_TUPLE("tm"), VMMTPGROUP_TM }, +}; + + +/** + * Initializes the tracing. + * + * @returns VBox status code + * @param pVM The cross context VM structure. + * @param cbEntry The trace entry size. + * @param cEntries The number of entries. + */ +static int dbgfR3TraceEnable(PVM pVM, uint32_t cbEntry, uint32_t cEntries) +{ + /* + * Don't enable it twice. + */ + if (pVM->hTraceBufR3 != NIL_RTTRACEBUF) + return VERR_ALREADY_EXISTS; + + /* + * Resolve default parameter values. + */ + int rc; + if (!cbEntry) + { + rc = CFGMR3QueryU32Def(CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF"), "TraceBufEntrySize", &cbEntry, 128); + AssertRCReturn(rc, rc); + } + if (!cEntries) + { + rc = CFGMR3QueryU32Def(CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF"), "TraceBufEntries", &cEntries, 4096); + AssertRCReturn(rc, rc); + } + + /* + * Figure the required size. + */ + RTTRACEBUF hTraceBuf; + size_t cbBlock = 0; + rc = RTTraceBufCarve(&hTraceBuf, cEntries, cbEntry, 0 /*fFlags*/, NULL, &cbBlock); + if (rc != VERR_BUFFER_OVERFLOW) + { + AssertReturn(!RT_SUCCESS_NP(rc), VERR_IPE_UNEXPECTED_INFO_STATUS); + return rc; + } + + /* + * Allocate a hyper heap block and carve a trace buffer out of it. + * + * Note! We ASSUME that the returned trace buffer handle has the same value + * as the heap block. + */ + cbBlock = RT_ALIGN_Z(cbBlock, HOST_PAGE_SIZE); + RTR0PTR pvBlockR0 = NIL_RTR0PTR; + void *pvBlockR3 = NULL; + rc = SUPR3PageAllocEx(cbBlock >> HOST_PAGE_SHIFT, 0, &pvBlockR3, &pvBlockR0, NULL); + if (RT_FAILURE(rc)) + return rc; + + rc = RTTraceBufCarve(&hTraceBuf, cEntries, cbEntry, 0 /*fFlags*/, pvBlockR3, &cbBlock); + AssertRCReturn(rc, rc); + AssertReleaseReturn(hTraceBuf == (RTTRACEBUF)pvBlockR3, VERR_INTERNAL_ERROR_3); + AssertReleaseReturn((void *)hTraceBuf == pvBlockR3, VERR_INTERNAL_ERROR_3); + + pVM->hTraceBufR3 = hTraceBuf; + pVM->hTraceBufR0 = pvBlockR0; + return VINF_SUCCESS; +} + + +/** + * Initializes the tracing. + * + * @returns VBox status code + * @param pVM The cross context VM structure. + */ +int dbgfR3TraceInit(PVM pVM) +{ + /* + * Initialize the trace buffer handles. + */ + Assert(NIL_RTTRACEBUF == (RTTRACEBUF)NULL); + pVM->hTraceBufR3 = NIL_RTTRACEBUF; + pVM->hTraceBufR0 = NIL_RTR0PTR; + + /* + * Check the config and enable tracing if requested. + */ + PCFGMNODE pDbgfNode = CFGMR3GetChild(CFGMR3GetRoot(pVM), "DBGF"); +#if defined(DEBUG) || defined(RTTRACE_ENABLED) + bool const fDefault = false; + const char * const pszConfigDefault = ""; +#else + bool const fDefault = false; + const char * const pszConfigDefault = ""; +#endif + bool fTracingEnabled; + int rc = CFGMR3QueryBoolDef(pDbgfNode, "TracingEnabled", &fTracingEnabled, fDefault); + AssertRCReturn(rc, rc); + if (fTracingEnabled) + { + rc = dbgfR3TraceEnable(pVM, 0, 0); + if (RT_SUCCESS(rc)) + { + if (pDbgfNode) + { + char *pszTracingConfig; + rc = CFGMR3QueryStringAllocDef(pDbgfNode, "TracingConfig", &pszTracingConfig, pszConfigDefault); + if (RT_SUCCESS(rc)) + { + rc = DBGFR3TraceConfig(pVM, pszTracingConfig); + if (RT_FAILURE(rc)) + rc = VMSetError(pVM, rc, RT_SRC_POS, "TracingConfig=\"%s\" -> %Rrc", pszTracingConfig, rc); + MMR3HeapFree(pszTracingConfig); + } + } + else + { + rc = DBGFR3TraceConfig(pVM, pszConfigDefault); + if (RT_FAILURE(rc)) + rc = VMSetError(pVM, rc, RT_SRC_POS, "TracingConfig=\"%s\" (default) -> %Rrc", pszConfigDefault, rc); + } + } + } + + /* + * Register a debug info item that will dump the trace buffer content. + */ + if (RT_SUCCESS(rc)) + rc = DBGFR3InfoRegisterInternal(pVM, "tracebuf", "Display the trace buffer content. No arguments.", dbgfR3TraceInfo); + + return rc; +} + + +/** + * Terminates the tracing. + * + * @param pVM The cross context VM structure. + */ +void dbgfR3TraceTerm(PVM pVM) +{ + /* nothing to do */ + NOREF(pVM); +} + + +/** + * Relocates the trace buffer handle in RC. + * + * @param pVM The cross context VM structure. + */ +void dbgfR3TraceRelocate(PVM pVM) +{ + RT_NOREF(pVM); +} + + +/** + * Change the traceing configuration of the VM. + * + * @returns VBox status code. + * @retval VINF_SUCCESS + * @retval VERR_NOT_FOUND if any of the trace point groups mentioned in the + * config string cannot be found. (Or if the string cannot be made + * sense of.) No change made. + * @retval VERR_INVALID_VM_HANDLE + * @retval VERR_INVALID_POINTER + * + * @param pVM The cross context VM structure. + * @param pszConfig The configuration change specification. + * + * Trace point group names, optionally prefixed by a '-' to + * indicate that the group is being disabled. A special + * group 'all' can be used to enable or disable all trace + * points. + * + * Drivers, devices and USB devices each have their own + * trace point group which can be accessed by prefixing + * their official PDM name by 'drv', 'dev' or 'usb' + * respectively. + */ +VMMDECL(int) DBGFR3TraceConfig(PVM pVM, const char *pszConfig) +{ + VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); + AssertPtrReturn(pszConfig, VERR_INVALID_POINTER); + if (pVM->hTraceBufR3 == NIL_RTTRACEBUF) + return VERR_DBGF_NO_TRACE_BUFFER; + + /* + * We do this in two passes, the first pass just validates the input string + * and the second applies the changes. + */ + for (uint32_t uPass = 0; uPass < 1; uPass++) + { + char ch; + while ((ch = *pszConfig) != '\0') + { + if (RT_C_IS_SPACE(ch)) + continue; + + /* + * Operation prefix. + */ + bool fNo = false; + do + { + if (ch == 'n' && pszConfig[1] == 'o') + { + fNo = !fNo; + pszConfig++; + } + else if (ch == '+') + fNo = false; + else if (ch == '-' || ch == '!' || ch == '~') + fNo = !fNo; + else + break; + } while ((ch = *++pszConfig) != '\0'); + if (ch == '\0') + break; + + /* + * Extract the name. + */ + const char *pszName = pszConfig; + while ( ch != '\0' + && !RT_C_IS_SPACE(ch) + && !RT_C_IS_PUNCT(ch)) + ch = *++pszConfig; + size_t const cchName = pszConfig - pszName; + + /* + * 'all' - special group that enables or disables all trace points. + */ + if (cchName == 3 && !strncmp(pszName, "all", 3)) + { + if (uPass != 0) + { + uint32_t iCpu = pVM->cCpus; + if (!fNo) + while (iCpu-- > 0) + pVM->apCpusR3[iCpu]->fTraceGroups = UINT32_MAX; + else + while (iCpu-- > 0) + pVM->apCpusR3[iCpu]->fTraceGroups = 0; + PDMR3TracingConfig(pVM, NULL, 0, !fNo, uPass > 0); + } + } + else + { + /* + * A specific group, try the VMM first then PDM. + */ + uint32_t i = RT_ELEMENTS(g_aVmmTpGroups); + while (i-- > 0) + if ( g_aVmmTpGroups[i].cchName == cchName + && !strncmp(g_aVmmTpGroups[i].pszName, pszName, cchName)) + { + if (uPass != 0) + { + uint32_t iCpu = pVM->cCpus; + if (!fNo) + while (iCpu-- > 0) + pVM->apCpusR3[iCpu]->fTraceGroups |= g_aVmmTpGroups[i].fMask; + else + while (iCpu-- > 0) + pVM->apCpusR3[iCpu]->fTraceGroups &= ~g_aVmmTpGroups[i].fMask; + } + break; + } + + if (i == UINT32_MAX) + { + int rc = PDMR3TracingConfig(pVM, pszName, cchName, !fNo, uPass > 0); + if (RT_FAILURE(rc)) + return rc; + } + } + } + } + + return VINF_SUCCESS; +} + + +/** + * Query the trace configuration specification string. + * + * @returns VBox status code. + * @retval VINF_SUCCESS + * @retval VERR_INVALID_VM_HANDLE + * @retval VERR_INVALID_POINTER + * @retval VERR_BUFFER_OVERFLOW if the buffer is too small. Buffer will be + * empty. + + * @param pVM The cross context VM structure. + * @param pszConfig Pointer to the output buffer. + * @param cbConfig The size of the output buffer. + */ +VMMDECL(int) DBGFR3TraceQueryConfig(PVM pVM, char *pszConfig, size_t cbConfig) +{ + VM_ASSERT_VALID_EXT_RETURN(pVM, VERR_INVALID_VM_HANDLE); + AssertPtrReturn(pszConfig, VERR_INVALID_POINTER); + if (cbConfig < 1) + return VERR_BUFFER_OVERFLOW; + *pszConfig = '\0'; + + if (pVM->hTraceBufR3 == NIL_RTTRACEBUF) + return VERR_DBGF_NO_TRACE_BUFFER; + + int rc = VINF_SUCCESS; + uint32_t const fTraceGroups = pVM->apCpusR3[0]->fTraceGroups; + if ( fTraceGroups == UINT32_MAX + && PDMR3TracingAreAll(pVM, true /*fEnabled*/)) + rc = RTStrCopy(pszConfig, cbConfig, "all"); + else if ( fTraceGroups == 0 + && PDMR3TracingAreAll(pVM, false /*fEnabled*/)) + rc = RTStrCopy(pszConfig, cbConfig, "-all"); + else + { + char *pszDst = pszConfig; + size_t cbDst = cbConfig; + uint32_t i = RT_ELEMENTS(g_aVmmTpGroups); + while (i-- > 0) + if (g_aVmmTpGroups[i].fMask & fTraceGroups) + { + size_t cchThis = g_aVmmTpGroups[i].cchName + (pszDst != pszConfig); + if (cchThis >= cbDst) + { + rc = VERR_BUFFER_OVERFLOW; + break; + } + if (pszDst != pszConfig) + { + *pszDst = ' '; + memcpy(pszDst + 1, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1); + } + else + memcpy(pszDst, g_aVmmTpGroups[i].pszName, g_aVmmTpGroups[i].cchName + 1); + pszDst += cchThis; + cbDst -= cchThis; + } + + if (RT_SUCCESS(rc)) + rc = PDMR3TracingQueryConfig(pVM, pszDst, cbDst); + } + + if (RT_FAILURE(rc)) + *pszConfig = '\0'; + return rc; +} + + +/** + * @callback_method_impl{FNRTTRACEBUFCALLBACK} + */ +static DECLCALLBACK(int) +dbgfR3TraceInfoDumpEntry(RTTRACEBUF hTraceBuf, uint32_t iEntry, uint64_t NanoTS, RTCPUID idCpu, const char *pszMsg, void *pvUser) +{ + PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvUser; + pHlp->pfnPrintf(pHlp, "#%04u/%'llu/%02x: %s\n", iEntry, NanoTS, idCpu, pszMsg); + NOREF(hTraceBuf); + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{FNDBGFHANDLERINT, Info handler for displaying the trace buffer content.} + */ +static DECLCALLBACK(void) dbgfR3TraceInfo(PVM pVM, PCDBGFINFOHLP pHlp, const char *pszArgs) +{ + RTTRACEBUF hTraceBuf = pVM->hTraceBufR3; + if (hTraceBuf == NIL_RTTRACEBUF) + pHlp->pfnPrintf(pHlp, "Tracing is disabled\n"); + else + { + pHlp->pfnPrintf(pHlp, "Trace buffer %p - %u entries of %u bytes\n", + hTraceBuf, RTTraceBufGetEntryCount(hTraceBuf), RTTraceBufGetEntrySize(hTraceBuf)); + RTTraceBufEnumEntries(hTraceBuf, dbgfR3TraceInfoDumpEntry, (void *)pHlp); + } + NOREF(pszArgs); +} + -- cgit v1.2.3