/* $Id: DrvIfsTrace.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */ /** @file * VBox interface callback tracing driver. */ /* * Copyright (C) 2020-2024 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_MISC #include #include #include #include #include #include #include "VBoxDD.h" #include "DrvIfsTraceInternal.h" /* * * IBase Implementation. * */ static DECLCALLBACK(void *) drvIfTraceIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID) { PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); if (pThis->pISerialConBelow) PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALCONNECTOR, &pThis->ISerialConnector); if (pThis->pISerialPortAbove) PDMIBASE_RETURN_INTERFACE(pszIID, PDMISERIALPORT, &pThis->ISerialPort); if (pThis->pITpmConBelow) PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMCONNECTOR, &pThis->ITpmConnector); if (pThis->pITpmPortAbove) PDMIBASE_RETURN_INTERFACE(pszIID, PDMITPMPORT, &pThis->ITpmPort); return NULL; } /* * * PDMDRVREG Methods * */ static const RTTRACELOGEVTDESC g_IfTraceVmResumeEvtDesc = { "IfTrace.VmResume", "VM was resumed", RTTRACELOGEVTSEVERITY_DEBUG, 0, NULL }; /** * @callback_method_impl{FNPDMDRVRESUME} */ static DECLCALLBACK(void) drvIfTrace_Resume(PPDMDRVINS pDrvIns) { PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); int rcTraceLog = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_IfTraceVmResumeEvtDesc, 0, 0, 0); if (RT_FAILURE(rcTraceLog)) LogRelMax(10, ("DrvIfTrace#%d: Failed to add event to trace log %Rrc\n", pThis->pDrvIns->iInstance, rcTraceLog)); } static const RTTRACELOGEVTDESC g_IfTraceVmSuspendEvtDesc = { "IfTrace.VmSuspend", "VM was suspended", RTTRACELOGEVTSEVERITY_DEBUG, 0, NULL }; /** * @callback_method_impl{FNPDMDRVSUSPEND} */ static DECLCALLBACK(void) drvIfTrace_Suspend(PPDMDRVINS pDrvIns) { PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); int rcTraceLog = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_IfTraceVmSuspendEvtDesc, 0, 0, 0); if (RT_FAILURE(rcTraceLog)) LogRelMax(10, ("DrvIfTrace#%d: Failed to add event to trace log %Rrc\n", pThis->pDrvIns->iInstance, rcTraceLog)); } static const RTTRACELOGEVTDESC g_IfTraceVmResetEvtDesc = { "IfTrace.VmReset", "VM was reset", RTTRACELOGEVTSEVERITY_DEBUG, 0, NULL }; /** * @callback_method_impl{FNPDMDRVRESET} */ static DECLCALLBACK(void) drvIfTrace_Reset(PPDMDRVINS pDrvIns) { PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); int rcTraceLog = RTTraceLogWrEvtAddL(pThis->hTraceLog, &g_IfTraceVmResetEvtDesc, 0, 0, 0); if (RT_FAILURE(rcTraceLog)) LogRelMax(10, ("DrvIfTrace#%d: Failed to add event to trace log %Rrc\n", pThis->pDrvIns->iInstance, rcTraceLog)); } /** * Destroys a interface filter driver instance. * * @copydoc FNPDMDRVDESTRUCT */ static DECLCALLBACK(void) drvIfTrace_Destruct(PPDMDRVINS pDrvIns) { PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); LogFlow(("%s: iInstance=%d\n", __FUNCTION__, pDrvIns->iInstance)); if (pThis->hTraceLog != NIL_RTTRACELOGWR) { RTTraceLogWrDestroy(pThis->hTraceLog); pThis->hTraceLog = NIL_RTTRACELOGWR; } } /** * Construct a interface filter driver instance. * * @copydoc FNPDMDRVCONSTRUCT */ static DECLCALLBACK(int) drvIfTrace_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags) { PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns); PDRVIFTRACE pThis = PDMINS_2_DATA(pDrvIns, PDRVIFTRACE); PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3; /* * Initialize the instance data. */ pThis->pDrvIns = pDrvIns; pThis->hTraceLog = NIL_RTTRACELOGWR; pDrvIns->IBase.pfnQueryInterface = drvIfTraceIBase_QueryInterface; drvIfsTrace_SerialIfInit(pThis); drvIfsTrace_TpmIfInit(pThis); /* * Validate and read config. */ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "TraceFilePath|TraceLocation", ""); char *pszLocation = NULL; int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "TraceFilePath", &pszLocation); if (RT_SUCCESS(rc)) { /* Try to create a file based trace log. */ rc = RTTraceLogWrCreateFile(&pThis->hTraceLog, RTBldCfgVersion(), pszLocation); PDMDrvHlpMMHeapFree(pDrvIns, pszLocation); AssertLogRelRCReturn(rc, rc); } else if (rc == VERR_CFGM_VALUE_NOT_FOUND) { /* Try to connect to an external server. */ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "TraceLocation", &pszLocation); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("Configuration error: querying \"TraceLocation\" resulted in %Rrc"), rc); char *pszPort = strchr(pszLocation, ':'); if (!pszPort) return PDMDrvHlpVMSetError(pDrvIns, VERR_NOT_FOUND, RT_SRC_POS, N_("IfTrace#%d: The location misses the port to connect to"), pDrvIns->iInstance); *pszPort = '\0'; /* Overwrite temporarily to avoid copying the hostname into a temporary buffer. */ uint32_t uPort = 0; rc = RTStrToUInt32Ex(pszPort + 1, NULL, 10, &uPort); if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("IfTrace#%d: The port part of the location is not a numerical value"), pDrvIns->iInstance); rc = RTTraceLogWrCreateTcpClient(&pThis->hTraceLog, RTBldCfgVersion(), pszLocation, uPort); *pszPort = ':'; /* Restore delimiter before checking the status. */ if (RT_FAILURE(rc)) return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("IfTrace#%d: Failed to connect to socket %s"), pDrvIns->iInstance, pszLocation); PDMDrvHlpMMHeapFree(pDrvIns, pszLocation); } /* * Query interfaces from the driver/device above us. */ pThis->pISerialPortAbove = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMISERIALPORT); pThis->pITpmPortAbove = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMITPMPORT); /* * Attach driver below us. */ PPDMIBASE pIBaseBelow; rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pIBaseBelow); AssertLogRelRCReturn(rc, rc); pThis->pISerialConBelow = PDMIBASE_QUERY_INTERFACE(pIBaseBelow, PDMISERIALCONNECTOR); pThis->pITpmConBelow = PDMIBASE_QUERY_INTERFACE(pIBaseBelow, PDMITPMCONNECTOR); return VINF_SUCCESS; } /** * Storage filter driver registration record. */ const PDMDRVREG g_DrvIfTrace = { /* u32Version */ PDM_DRVREG_VERSION, /* szName */ "IfTrace", /* szRCMod */ "", /* szR0Mod */ "", /* pszDescription */ "Interface callback tracing driver", /* fFlags */ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, /* fClass. */ PDM_DRVREG_CLASS_STATUS, /* cMaxInstances */ ~0U, /* cbInstance */ sizeof(DRVIFTRACE), /* pfnConstruct */ drvIfTrace_Construct, /* pfnDestruct */ drvIfTrace_Destruct, /* pfnRelocate */ NULL, /* pfnIOCtl */ NULL, /* pfnPowerOn */ NULL, /* pfnReset */ drvIfTrace_Reset, /* pfnSuspend */ drvIfTrace_Suspend, /* pfnResume */ drvIfTrace_Resume, /* pfnAttach */ NULL, /* pfnDetach */ NULL, /* pfnPowerOff */ NULL, /* pfnSoftReset */ NULL, /* u32EndVersion */ PDM_DRVREG_VERSION };