diff options
Diffstat (limited to 'src/bldprogs/VBoxTpG.cpp')
-rw-r--r-- | src/bldprogs/VBoxTpG.cpp | 2665 |
1 files changed, 2665 insertions, 0 deletions
diff --git a/src/bldprogs/VBoxTpG.cpp b/src/bldprogs/VBoxTpG.cpp new file mode 100644 index 00000000..516b749e --- /dev/null +++ b/src/bldprogs/VBoxTpG.cpp @@ -0,0 +1,2665 @@ +/* $Id: VBoxTpG.cpp $ */ +/** @file + * VBox Build Tool - VBox Tracepoint Generator. + */ + +/* + * Copyright (C) 2012-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 <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <VBox/VBoxTpG.h> + +#include <iprt/alloca.h> +#include <iprt/assert.h> +#include <iprt/ctype.h> +#include <iprt/env.h> +#include <iprt/errcore.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/initterm.h> +#include <iprt/list.h> +#include <iprt/mem.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/process.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/uuid.h> + +#include "scmstream.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +typedef struct VTGPROBE *PVTGPROBE; + +typedef struct VTGATTRS +{ + kVTGStability enmCode; + kVTGStability enmData; + kVTGClass enmDataDep; +} VTGATTRS; +typedef VTGATTRS *PVTGATTRS; + + +typedef struct VTGARG +{ + RTLISTNODE ListEntry; + /** The argument name. (heap) */ + char *pszName; + /** The type presented to the tracer (in string table). */ + const char *pszTracerType; + /** The argument type used in the probe method in that context. (heap) */ + char *pszCtxType; + /** Argument passing format string. First and only argument is the name. + * (const string) */ + const char *pszArgPassingFmt; + /** The type flags. */ + uint32_t fType; + /** The argument number (0-based) for complaining/whatever. */ + uint16_t iArgNo; + /** The probe the argument belongs to (for complaining/whatever). */ + PVTGPROBE pProbe; + /** The absolute source position. */ + size_t offSrc; +} VTGARG; +typedef VTGARG *PVTGARG; + +typedef struct VTGPROBE +{ + RTLISTNODE ListEntry; + char *pszMangledName; + const char *pszUnmangledName; + RTLISTANCHOR ArgHead; + uint32_t cArgs; + bool fHaveLargeArgs; + uint32_t offArgList; + uint32_t iProbe; + size_t iLine; +} VTGPROBE; + +typedef struct VTGPROVIDER +{ + RTLISTNODE ListEntry; + const char *pszName; + + uint16_t iFirstProbe; + uint16_t cProbes; + + VTGATTRS AttrSelf; + VTGATTRS AttrModules; + VTGATTRS AttrFunctions; + VTGATTRS AttrName; + VTGATTRS AttrArguments; + + RTLISTANCHOR ProbeHead; +} VTGPROVIDER; +typedef VTGPROVIDER *PVTGPROVIDER; + +/** + * A string table string. + */ +typedef struct VTGSTRING +{ + /** The string space core. */ + RTSTRSPACECORE Core; + /** The string table offset. */ + uint32_t offStrTab; + /** The actual string. */ + char szString[1]; +} VTGSTRING; +typedef VTGSTRING *PVTGSTRING; + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +/** The string space organizing the string table strings. Each node is a VTGSTRING. */ +static RTSTRSPACE g_StrSpace = NULL; +/** Used by the string table enumerator to set VTGSTRING::offStrTab. */ +static uint32_t g_offStrTab; +/** List of providers created by the parser. */ +static RTLISTANCHOR g_ProviderHead; +/** The number of type errors. */ +static uint32_t g_cTypeErrors = 0; + + +/** @name Options + * @{ */ +static enum +{ + kVBoxTpGAction_Nothing, + kVBoxTpGAction_GenerateHeader, + kVBoxTpGAction_GenerateWrapperHeader, + kVBoxTpGAction_GenerateObject +} g_enmAction = kVBoxTpGAction_Nothing; +static uint32_t g_cBits = HC_ARCH_BITS; +static uint32_t g_cHostBits = HC_ARCH_BITS; +static uint32_t g_fTypeContext = VTG_TYPE_CTX_R0; +static const char *g_pszContextDefine = "IN_RING0"; +static const char *g_pszContextDefine2 = NULL; +static bool g_fApplyCpp = false; +static uint32_t g_cVerbosity = 0; +static const char *g_pszOutput = NULL; +static const char *g_pszScript = NULL; +static const char *g_pszTempAsm = NULL; +#ifdef RT_OS_DARWIN +static const char *g_pszAssembler = "yasm"; +static const char *g_pszAssemblerFmtOpt = "-f"; +static const char g_szAssemblerFmtVal32[] = "macho32"; +static const char g_szAssemblerFmtVal64[] = "macho64"; +static const char g_szAssemblerOsDef[] = "RT_OS_DARWIN"; +#elif defined(RT_OS_OS2) +static const char *g_pszAssembler = "nasm.exe"; +static const char *g_pszAssemblerFmtOpt = "-f"; +static const char g_szAssemblerFmtVal32[] = "obj"; +static const char g_szAssemblerFmtVal64[] = "elf64"; +static const char g_szAssemblerOsDef[] = "RT_OS_OS2"; +#elif defined(RT_OS_WINDOWS) +static const char *g_pszAssembler = "yasm.exe"; +static const char *g_pszAssemblerFmtOpt = "-f"; +static const char g_szAssemblerFmtVal32[] = "win32"; +static const char g_szAssemblerFmtVal64[] = "win64"; +static const char g_szAssemblerOsDef[] = "RT_OS_WINDOWS"; +#else +static const char *g_pszAssembler = "yasm"; +static const char *g_pszAssemblerFmtOpt = "-f"; +static const char g_szAssemblerFmtVal32[] = "elf32"; +static const char g_szAssemblerFmtVal64[] = "elf64"; +# ifdef RT_OS_FREEBSD +static const char g_szAssemblerOsDef[] = "RT_OS_FREEBSD"; +# elif defined(RT_OS_NETBSD) +static const char g_szAssemblerOsDef[] = "RT_OS_NETBSD"; +# elif defined(RT_OS_OPENBSD) +static const char g_szAssemblerOsDef[] = "RT_OS_OPENBSD"; +# elif defined(RT_OS_LINUX) +static const char g_szAssemblerOsDef[] = "RT_OS_LINUX"; +# elif defined(RT_OS_SOLARIS) +static const char g_szAssemblerOsDef[] = "RT_OS_SOLARIS"; +# else +# error "Port me!" +# endif +#endif +static const char *g_pszAssemblerFmtVal = RT_CONCAT(g_szAssemblerFmtVal, HC_ARCH_BITS); +static const char *g_pszAssemblerDefOpt = "-D"; +static const char *g_pszAssemblerIncOpt = "-I"; +static char g_szAssemblerIncVal[RTPATH_MAX]; +static const char *g_pszAssemblerIncVal = __FILE__ "/../../../include/"; +static const char *g_pszAssemblerOutputOpt = "-o"; +static unsigned g_cAssemblerOptions = 0; +static const char *g_apszAssemblerOptions[32]; +static const char *g_pszProbeFnName = "SUPR0TracerFireProbe"; +static bool g_fProbeFnImported = true; +static bool g_fPic = false; +/** @} */ + + + + +/** + * Inserts a string into the string table, reusing any matching existing string + * if possible. + * + * @returns Read only string. + * @param pch The string to insert (need not be terminated). + * @param cch The length of the string. + */ +static const char *strtabInsertN(const char *pch, size_t cch) +{ + PVTGSTRING pStr = (PVTGSTRING)RTStrSpaceGetN(&g_StrSpace, pch, cch); + if (pStr) + return pStr->szString; + + /* + * Create a new entry. + */ + pStr = (PVTGSTRING)RTMemAlloc(RT_UOFFSETOF_DYN(VTGSTRING, szString[cch + 1])); + if (!pStr) + return NULL; + + pStr->Core.pszString = pStr->szString; + memcpy(pStr->szString, pch, cch); + pStr->szString[cch] = '\0'; + pStr->offStrTab = UINT32_MAX; + + bool fRc = RTStrSpaceInsert(&g_StrSpace, &pStr->Core); + Assert(fRc); NOREF(fRc); + return pStr->szString; +} + + +/** + * Retrieves the string table offset of the given string table string. + * + * @returns String table offset. + * @param pszStrTabString The string table string. + */ +static uint32_t strtabGetOff(const char *pszStrTabString) +{ + PVTGSTRING pStr = RT_FROM_MEMBER(pszStrTabString, VTGSTRING, szString[0]); + Assert(pStr->Core.pszString == pszStrTabString); + return pStr->offStrTab; +} + + +/** + * Invokes the assembler. + * + * @returns Exit code. + * @param pszOutput The output file. + * @param pszTempAsm The source file. + */ +static RTEXITCODE generateInvokeAssembler(const char *pszOutput, const char *pszTempAsm) +{ + const char *apszArgs[64]; + unsigned iArg = 0; + + apszArgs[iArg++] = g_pszAssembler; + apszArgs[iArg++] = g_pszAssemblerFmtOpt; + apszArgs[iArg++] = g_pszAssemblerFmtVal; + apszArgs[iArg++] = g_pszAssemblerDefOpt; + if (!strcmp(g_pszAssemblerFmtVal, "macho32") || !strcmp(g_pszAssemblerFmtVal, "macho64")) + apszArgs[iArg++] = "ASM_FORMAT_MACHO"; + else if (!strcmp(g_pszAssemblerFmtVal, "obj") || !strcmp(g_pszAssemblerFmtVal, "omf")) + apszArgs[iArg++] = "ASM_FORMAT_OMF"; + else if ( !strcmp(g_pszAssemblerFmtVal, "win32") + || !strcmp(g_pszAssemblerFmtVal, "win64") + || !strcmp(g_pszAssemblerFmtVal, "pe32") + || !strcmp(g_pszAssemblerFmtVal, "pe64") + || !strcmp(g_pszAssemblerFmtVal, "pe") ) + apszArgs[iArg++] = "ASM_FORMAT_PE"; + else if ( !strcmp(g_pszAssemblerFmtVal, "elf32") + || !strcmp(g_pszAssemblerFmtVal, "elf64") + || !strcmp(g_pszAssemblerFmtVal, "elf")) + apszArgs[iArg++] = "ASM_FORMAT_ELF"; + else + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unknown assembler format '%s'", g_pszAssemblerFmtVal); + apszArgs[iArg++] = g_pszAssemblerDefOpt; + if (g_cBits == 32) + apszArgs[iArg++] = "ARCH_BITS=32"; + else + apszArgs[iArg++] = "ARCH_BITS=64"; + apszArgs[iArg++] = g_pszAssemblerDefOpt; + if (g_cHostBits == 32) + apszArgs[iArg++] = "HC_ARCH_BITS=32"; + else + apszArgs[iArg++] = "HC_ARCH_BITS=64"; + apszArgs[iArg++] = g_pszAssemblerDefOpt; + if (g_cBits == 32) + apszArgs[iArg++] = "RT_ARCH_X86"; + else + apszArgs[iArg++] = "RT_ARCH_AMD64"; + apszArgs[iArg++] = g_pszAssemblerDefOpt; + apszArgs[iArg++] = g_pszContextDefine; + if (g_pszContextDefine2) + { + apszArgs[iArg++] = g_pszAssemblerDefOpt; + apszArgs[iArg++] = g_pszContextDefine2; + } + if (g_szAssemblerOsDef[0]) + { + apszArgs[iArg++] = g_pszAssemblerDefOpt; + apszArgs[iArg++] = g_szAssemblerOsDef; + } + apszArgs[iArg++] = g_pszAssemblerIncOpt; + apszArgs[iArg++] = g_pszAssemblerIncVal; + apszArgs[iArg++] = g_pszAssemblerOutputOpt; + apszArgs[iArg++] = pszOutput; + for (unsigned i = 0; i < g_cAssemblerOptions; i++) + apszArgs[iArg++] = g_apszAssemblerOptions[i]; + apszArgs[iArg++] = pszTempAsm; + apszArgs[iArg] = NULL; + Assert(iArg <= RT_ELEMENTS(apszArgs)); + + if (g_cVerbosity > 1) + { + RTMsgInfo("Starting assmbler '%s' with arguments:\n", g_pszAssembler); + for (unsigned i = 0; i < iArg; i++) + RTMsgInfo(" #%02u: '%s'\n", i, apszArgs[i]); + } + + RTPROCESS hProc; + int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProc); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to start '%s' (assembler): %Rrc", apszArgs[0], rc); + + RTPROCSTATUS Status; + rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &Status); + if (RT_FAILURE(rc)) + { + RTProcTerminate(hProc); + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcWait failed: %Rrc", rc); + } + if (Status.enmReason == RTPROCEXITREASON_SIGNAL) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: signal %d", Status.iStatus); + if (Status.enmReason != RTPROCEXITREASON_NORMAL) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: abend"); + if (Status.iStatus != 0) + return RTMsgErrorExit((RTEXITCODE)Status.iStatus, "The assembler failed: exit code %d", Status.iStatus); + + return RTEXITCODE_SUCCESS; +} + + +/** + * Worker that does the boring bits when generating a file. + * + * @returns Exit code. + * @param pszOutput The name of the output file. + * @param pszWhat What kind of file it is. + * @param pfnGenerator The callback function that provides the contents + * of the file. + */ +static RTEXITCODE generateFile(const char *pszOutput, const char *pszWhat, + RTEXITCODE (*pfnGenerator)(PSCMSTREAM)) +{ + SCMSTREAM Strm; + int rc = ScmStreamInitForWriting(&Strm, NULL); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file", + rc, pszWhat); + + RTEXITCODE rcExit = pfnGenerator(&Strm); + if (RT_FAILURE(ScmStreamGetStatus(&Strm))) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Stream error %Rrc generating the %s file", + ScmStreamGetStatus(&Strm), pszWhat); + if (rcExit == RTEXITCODE_SUCCESS) + { + rc = ScmStreamWriteToFile(&Strm, "%s", pszOutput); + if (RT_FAILURE(rc)) + rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)", + rc, pszOutput, pszWhat); + if (rcExit == RTEXITCODE_SUCCESS) + { + if (g_cVerbosity > 0) + RTMsgInfo("Successfully generated '%s'.", pszOutput); + if (g_cVerbosity > 1) + { + RTMsgInfo("================ %s - start ================", pszWhat); + ScmStreamRewindForReading(&Strm); + const char *pszLine; + size_t cchLine; + SCMEOL enmEol; + while ((pszLine = ScmStreamGetLine(&Strm, &cchLine, &enmEol)) != NULL) + RTPrintf("%.*s\n", cchLine, pszLine); + RTMsgInfo("================ %s - end ================", pszWhat); + } + } + } + ScmStreamDelete(&Strm); + return rcExit; +} + + +/** + * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes the string table strings.} + */ +static DECLCALLBACK(int) generateAssemblyStrTabCallback(PRTSTRSPACECORE pStr, void *pvUser) +{ + PVTGSTRING pVtgStr = (PVTGSTRING)pStr; + PSCMSTREAM pStrm = (PSCMSTREAM)pvUser; + + pVtgStr->offStrTab = g_offStrTab; + g_offStrTab += (uint32_t)pVtgStr->Core.cchString + 1; + + ScmStreamPrintf(pStrm, + " db '%s', 0 ; off=%u len=%zu\n", + pVtgStr->szString, pVtgStr->offStrTab, pVtgStr->Core.cchString); + return VINF_SUCCESS; +} + + +/** + * Generate assembly source that can be turned into an object file. + * + * (This is a generateFile callback.) + * + * @returns Exit code. + * @param pStrm The output stream. + */ +static RTEXITCODE generateAssembly(PSCMSTREAM pStrm) +{ + PVTGPROVIDER pProvider; + PVTGPROBE pProbe; + PVTGARG pArg; + + + if (g_cVerbosity > 0) + RTMsgInfo("Generating assembly code..."); + + /* + * Write the file header. + */ + ScmStreamPrintf(pStrm, + "; $Id: VBoxTpG.cpp $ \n" + ";; @file\n" + "; Automatically generated from %s. Do NOT edit!\n" + ";\n" + "\n" + "%%include \"iprt/asmdefs.mac\"\n" + "\n" + "\n" + ";" + "; We put all the data in a dedicated section / segment.\n" + ";\n" + "; In order to find the probe location specifiers, we do the necessary\n" + "; trickery here, ASSUMING that this object comes in first in the link\n" + "; editing process.\n" + ";\n" + "%%ifdef ASM_FORMAT_OMF\n" + " %%macro VTG_GLOBAL 2\n" + " global NAME(%%1)\n" + " NAME(%%1):\n" + " %%endmacro\n" + " segment VTG.Obj public CLASS=VTG align=4096 use32\n" + "\n" + "%%elifdef ASM_FORMAT_MACHO\n" + " %%macro VTG_GLOBAL 2\n" + " global NAME(%%1)\n" + " NAME(%%1):\n" + " %%endmacro\n" + " %%ifdef IN_RING3\n" + " %%define VTG_NEW_MACHO_LINKER\n" + " %%elif ARCH_BITS == 64\n" + " %%define VTG_NEW_MACHO_LINKER\n" + " %%elifdef IN_RING0_AGNOSTIC\n" + " %%define VTG_NEW_MACHO_LINKER\n" + " %%endif\n" + " %%ifdef VTG_NEW_MACHO_LINKER\n" + " ; Section order hack!\n" + " ; With the ld64-97.17 linker there was a problem with it determining the section\n" + " ; order based on symbol references. The references to the start and end of the\n" + " ; __VTGPrLc section forced it in front of __VTGObj, we want __VTGObj first.\n" + " extern section$start$__VTG$__VTGObj\n" + " extern section$end$__VTG$__VTGObj\n" + " %%else\n" + " ; Creating 32-bit kext of the type MH_OBJECT. No fancy section end/start symbols handy.\n" + " [section __VTG __VTGObj align=16]\n" + "VTG_GLOBAL g_aVTGObj_LinkerPleaseNoticeMe, data\n" + " [section __VTG __VTGPrLc.Begin align=16]\n" + " dq 0, 0 ; Paranoia, related to the fudge below.\n" + "VTG_GLOBAL g_aVTGPrLc, data\n" + " [section __VTG __VTGPrLc align=16]\n" + "VTG_GLOBAL g_aVTGPrLc_LinkerPleaseNoticeMe, data\n" + " [section __VTG __VTGPrLc.End align=16]\n" + "VTG_GLOBAL g_aVTGPrLc_End, data\n" + " dq 0, 0 ; Fudge to work around unidentified linker where it would otherwise generate\n" + " ; a fix up of the first dword in __VTGPrLc.Begin despite the fact that it were\n" + " ; an empty section with nothing whatsoever to fix up.\n" + " %%endif\n" + " [section __VTG __VTGObj]\n" + "\n" + "%%elifdef ASM_FORMAT_PE\n" + " %%macro VTG_GLOBAL 2\n" + " global NAME(%%1)\n" + " NAME(%%1):\n" + " %%endmacro\n" + " [section VTGPrLc.Begin data align=64]\n" + /*" times 16 db 0xcc\n"*/ + "VTG_GLOBAL g_aVTGPrLc, data\n" + " [section VTGPrLc.Data data align=4]\n" + " [section VTGPrLc.End data align=4]\n" + "VTG_GLOBAL g_aVTGPrLc_End, data\n" + /*" times 16 db 0xcc\n"*/ + " [section VTGObj data align=32]\n" + "\n" + "%%elifdef ASM_FORMAT_ELF\n" + " %%macro VTG_GLOBAL 2\n" + " global NAME(%%1):%%2 hidden\n" + " NAME(%%1):\n" + " %%endmacro\n" + " [section .VTGData progbits alloc noexec write align=4096]\n" + " [section .VTGPrLc.Begin progbits alloc noexec write align=32]\n" + " dd 0,0,0,0, 0,0,0,0\n" + "VTG_GLOBAL g_aVTGPrLc, data\n" + " [section .VTGPrLc progbits alloc noexec write align=1]\n" + " [section .VTGPrLc.End progbits alloc noexec write align=1]\n" + "VTG_GLOBAL g_aVTGPrLc_End, data\n" + " dd 0,0,0,0, 0,0,0,0\n" + " [section .VTGData]\n" + "\n" + "%%else\n" + " %%error \"ASM_FORMAT_XXX is not defined\"\n" + "%%endif\n" + "\n" + "\n" + "VTG_GLOBAL g_VTGObjHeader, data\n" + " ;0 1 2 3\n" + " ;012345678901234567890123456789012\n" + " db 'VTG Object Header v1.7', 0, 0\n" + " dd %u\n" + " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_achVTGStringTable) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_achVTGStringTable_End) - NAME(g_achVTGStringTable)\n" + " dd NAME(g_aVTGArgLists) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_aVTGArgLists_End) - NAME(g_aVTGArgLists)\n" + " dd NAME(g_aVTGProbes) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_aVTGProbes_End) - NAME(g_aVTGProbes)\n" + " dd NAME(g_aVTGProviders) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_aVTGProviders_End) - NAME(g_aVTGProviders)\n" + " dd NAME(g_acVTGProbeEnabled) - NAME(g_VTGObjHeader)\n" + " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_acVTGProbeEnabled)\n" + " dd 0\n" + " dd 0\n" + "%%ifdef VTG_NEW_MACHO_LINKER\n" + " extern section$start$__VTG$__VTGPrLc\n" + " RTCCPTR_DEF section$start$__VTG$__VTGPrLc\n" + " %%if ARCH_BITS == 32\n" + " dd 0\n" + " %%endif\n" + " extern section$end$__VTG$__VTGPrLc\n" + " RTCCPTR_DEF section$end$__VTG$__VTGPrLc\n" + " %%if ARCH_BITS == 32\n" + " dd 0\n" + " %%endif\n" + "%%else\n" + " RTCCPTR_DEF NAME(g_aVTGPrLc)\n" + " %%if ARCH_BITS == 32\n" + " dd 0\n" + " %%endif\n" + " RTCCPTR_DEF NAME(g_aVTGPrLc_End)\n" + " %%if ARCH_BITS == 32\n" + " dd 0\n" + " %%endif\n" + "%%endif\n" + , + g_pszScript, g_cBits); + RTUUID Uuid; + int rc = RTUuidCreate(&Uuid); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc); + ScmStreamPrintf(pStrm, + " dd 0%08xh, 0%08xh, 0%08xh, 0%08xh\n" + "%%ifdef VTG_NEW_MACHO_LINKER\n" + " RTCCPTR_DEF section$start$__VTG$__VTGObj\n" + " %%if ARCH_BITS == 32\n" + " dd 0\n" + " %%endif\n" + "%%else\n" + " dd 0, 0\n" + "%%endif\n" + " dd 0, 0\n" + , Uuid.au32[0], Uuid.au32[1], Uuid.au32[2], Uuid.au32[3]); + + /* + * Dump the string table before we start using the strings. + */ + ScmStreamPrintf(pStrm, + "\n" + ";\n" + "; The string table.\n" + ";\n" + "VTG_GLOBAL g_achVTGStringTable, data\n"); + g_offStrTab = 0; + RTStrSpaceEnumerate(&g_StrSpace, generateAssemblyStrTabCallback, pStrm); + ScmStreamPrintf(pStrm, + "VTG_GLOBAL g_achVTGStringTable_End, data\n"); + + /* + * Write out the argument lists before we use them. + */ + ScmStreamPrintf(pStrm, + "\n" + ";\n" + "; The argument lists.\n" + ";\n" + "ALIGNDATA(16)\n" + "VTG_GLOBAL g_aVTGArgLists, data\n"); + uint32_t off = 0; + RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry) + { + RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + if (pProbe->offArgList != UINT32_MAX) + continue; + + /* Write it. */ + pProbe->offArgList = off; + ScmStreamPrintf(pStrm, + " ; off=%u\n" + " db %2u ; Argument count\n" + " db %u ; fHaveLargeArgs\n" + " db 0, 0 ; Reserved\n" + , off, pProbe->cArgs, (int)pProbe->fHaveLargeArgs); + off += 4; + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + ScmStreamPrintf(pStrm, + " dd %8u ; type '%s' (name '%s')\n" + " dd 0%08xh ; type flags\n", + strtabGetOff(pArg->pszTracerType), pArg->pszTracerType, pArg->pszName, + pArg->fType); + off += 8; + } + + /* Look for matching argument lists (lazy bird walks the whole list). */ + PVTGPROVIDER pProv2; + RTListForEach(&g_ProviderHead, pProv2, VTGPROVIDER, ListEntry) + { + PVTGPROBE pProbe2; + RTListForEach(&pProvider->ProbeHead, pProbe2, VTGPROBE, ListEntry) + { + if (pProbe2->offArgList != UINT32_MAX) + continue; + if (pProbe2->cArgs != pProbe->cArgs) + continue; + + PVTGARG pArg2; + pArg = RTListNodeGetNext(&pProbe->ArgHead, VTGARG, ListEntry); + pArg2 = RTListNodeGetNext(&pProbe2->ArgHead, VTGARG, ListEntry); + int32_t cArgs = pProbe->cArgs; + while ( cArgs-- > 0 + && pArg2->pszTracerType == pArg->pszTracerType + && pArg2->fType == pArg->fType) + { + pArg = RTListNodeGetNext(&pArg->ListEntry, VTGARG, ListEntry); + pArg2 = RTListNodeGetNext(&pArg2->ListEntry, VTGARG, ListEntry); + } + if (cArgs >= 0) + continue; + pProbe2->offArgList = pProbe->offArgList; + } + } + } + } + ScmStreamPrintf(pStrm, + "VTG_GLOBAL g_aVTGArgLists_End, data\n"); + + + /* + * Probe definitions. + */ + ScmStreamPrintf(pStrm, + "\n" + ";\n" + "; Prob definitions.\n" + ";\n" + "ALIGNDATA(16)\n" + "VTG_GLOBAL g_aVTGProbes, data\n" + "\n"); + uint32_t iProvider = 0; + uint32_t iProbe = 0; + RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry) + { + pProvider->iFirstProbe = iProbe; + RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + ScmStreamPrintf(pStrm, + "VTG_GLOBAL g_VTGProbeData_%s_%s, data ; idx=#%4u\n" + " dd %6u ; offName\n" + " dd %6u ; offArgList\n" + " dw (NAME(g_cVTGProbeEnabled_%s_%s) - NAME(g_acVTGProbeEnabled)) / 4 ; idxEnabled\n" + " dw %6u ; idxProvider\n" + " dd NAME(g_VTGObjHeader) - NAME(g_VTGProbeData_%s_%s) ; offObjHdr\n" + , + pProvider->pszName, pProbe->pszMangledName, iProbe, + strtabGetOff(pProbe->pszUnmangledName), + pProbe->offArgList, + pProvider->pszName, pProbe->pszMangledName, + iProvider, + pProvider->pszName, pProbe->pszMangledName + ); + pProbe->iProbe = iProbe; + iProbe++; + } + pProvider->cProbes = iProbe - pProvider->iFirstProbe; + iProvider++; + } + ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProbes_End, data\n"); + + /* + * The provider data. + */ + ScmStreamPrintf(pStrm, + "\n" + ";\n" + "; Provider data.\n" + ";\n" + "ALIGNDATA(16)\n" + "VTG_GLOBAL g_aVTGProviders, data\n"); + iProvider = 0; + RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry) + { + ScmStreamPrintf(pStrm, + " ; idx=#%4u - %s\n" + " dd %6u ; name\n" + " dw %6u ; index of first probe\n" + " dw %6u ; count of probes\n" + " db %d, %d, %d ; AttrSelf\n" + " db %d, %d, %d ; AttrModules\n" + " db %d, %d, %d ; AttrFunctions\n" + " db %d, %d, %d ; AttrName\n" + " db %d, %d, %d ; AttrArguments\n" + " db 0 ; reserved\n" + "VTG_GLOBAL g_cVTGProviderProbesEnabled_%s, data\n" + " dd 0\n" + "VTG_GLOBAL g_cVTGProviderSettingsSeqNo_%s, data\n" + " dd 0\n" + , + iProvider, pProvider->pszName, + strtabGetOff(pProvider->pszName), + pProvider->iFirstProbe, + pProvider->cProbes, + pProvider->AttrSelf.enmCode, pProvider->AttrSelf.enmData, pProvider->AttrSelf.enmDataDep, + pProvider->AttrModules.enmCode, pProvider->AttrModules.enmData, pProvider->AttrModules.enmDataDep, + pProvider->AttrFunctions.enmCode, pProvider->AttrFunctions.enmData, pProvider->AttrFunctions.enmDataDep, + pProvider->AttrName.enmCode, pProvider->AttrName.enmData, pProvider->AttrName.enmDataDep, + pProvider->AttrArguments.enmCode, pProvider->AttrArguments.enmData, pProvider->AttrArguments.enmDataDep, + pProvider->pszName, + pProvider->pszName); + iProvider++; + } + ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProviders_End, data\n"); + + /* + * Declare the probe enable flags. + * + * These must be placed at the end so they'll end up adjacent to the probe + * locations. This is important for reducing the amount of memory we need + * to lock down for user mode modules. + */ + ScmStreamPrintf(pStrm, + ";\n" + "; Probe enabled flags.\n" + ";\n" + "ALIGNDATA(16)\n" + "VTG_GLOBAL g_acVTGProbeEnabled, data\n" + ); + uint32_t cProbes = 0; + RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry) + { + RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + ScmStreamPrintf(pStrm, + "VTG_GLOBAL g_cVTGProbeEnabled_%s_%s, data\n" + " dd 0\n", + pProvider->pszName, pProbe->pszMangledName); + cProbes++; + } + } + ScmStreamPrintf(pStrm, "VTG_GLOBAL g_acVTGProbeEnabled_End, data\n"); + if (cProbes >= _32K) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many probes: %u (max %u)", cProbes, _32K - 1); + + + /* + * Emit code for the stub functions. + */ + bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64")); + bool const fElf = !strcmp(g_pszAssemblerFmtVal, "elf32") || !strcmp(g_pszAssemblerFmtVal, "elf64"); + ScmStreamPrintf(pStrm, + "\n" + ";\n" + "; Prob stubs.\n" + ";\n" + "BEGINCODE\n" + ); + if (g_fProbeFnImported) + ScmStreamPrintf(pStrm, + "EXTERN_IMP2 %s\n" + "BEGINCODE ; EXTERN_IMP2 changes section\n", + g_pszProbeFnName); + else + ScmStreamPrintf(pStrm, "extern NAME(%s)\n", g_pszProbeFnName); + + RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry) + { + RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + ScmStreamPrintf(pStrm, + "\n" + "VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc", + pProvider->pszName, pProbe->pszMangledName); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + ScmStreamPrintf(pStrm, ", %s %s", pArg->pszTracerType, pArg->pszName); + } + ScmStreamPrintf(pStrm, + ");\n"); + + /* + * Check if the probe in question is enabled. + */ + if (g_cBits == 32) + ScmStreamPrintf(pStrm, + " mov eax, [esp + 4]\n" + " test byte [eax+3], 0x80 ; fEnabled == true?\n" + " jz .return ; jump on false\n"); + else if (fWin64) + ScmStreamPrintf(pStrm, + " test byte [rcx+3], 0x80 ; fEnabled == true?\n" + " jz .return ; jump on false\n"); + else + ScmStreamPrintf(pStrm, + " test byte [rdi+3], 0x80 ; fEnabled == true?\n" + " jz .return ; jump on false\n"); + + /* + * Jump to the fire-probe function. + */ + if (g_cBits == 32) + ScmStreamPrintf(pStrm, g_fPic && fElf ? + " jmp %s wrt ..plt\n" + : g_fProbeFnImported ? + " mov ecx, IMP2(%s)\n" + " jmp ecx\n" + : + " jmp NAME(%s)\n" + , g_pszProbeFnName); + else + ScmStreamPrintf(pStrm, g_fPic && fElf ? + " jmp [rel %s wrt ..got]\n" + : g_fProbeFnImported ? + " jmp IMP2(%s)\n" + : + " jmp NAME(%s)\n" + , g_pszProbeFnName); + + ScmStreamPrintf(pStrm, + ".return:\n" + " ret ; The probe was disabled, return\n" + "\n"); + } + } + + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm) +{ + if (!pszTempAsm) + { + size_t cch = strlen(pszOutput); + char *psz = (char *)alloca(cch + sizeof(".asm")); + memcpy(psz, pszOutput, cch); + memcpy(psz + cch, ".asm", sizeof(".asm")); + pszTempAsm = psz; + } + + RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly); + if (rcExit == RTEXITCODE_SUCCESS) + rcExit = generateInvokeAssembler(pszOutput, pszTempAsm); + RTFileDelete(pszTempAsm); + return rcExit; +} + + +static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe) +{ + size_t cbMax = strlen(pszProvider) + 1 + strlen(pszProbe) + 1; + if (cbMax > cbBuf || cbMax > 80) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider); + + while (*pszProvider) + *pszBuf++ = RT_C_TO_UPPER(*pszProvider++); + + *pszBuf++ = '_'; + + while (*pszProbe) + { + if (pszProbe[0] == '_' && pszProbe[1] == '_') + pszProbe++; + *pszBuf++ = RT_C_TO_UPPER(*pszProbe++); + } + + *pszBuf = '\0'; + return RTEXITCODE_SUCCESS; +} + + +static RTEXITCODE generateProviderDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider) +{ + size_t cbMax = strlen(pszProvider) + 1; + if (cbMax > cbBuf || cbMax > 80) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Provider '%s' ends up with a too long defined\n", pszProvider); + + while (*pszProvider) + *pszBuf++ = RT_C_TO_UPPER(*pszProvider++); + + *pszBuf = '\0'; + return RTEXITCODE_SUCCESS; +} + + +/** + * Called via generateFile to generate the header file. + * + * @returns Exit code status. + * @param pStrm The output stream. + */ +static RTEXITCODE generateHeader(PSCMSTREAM pStrm) +{ + /* + * Calc the double inclusion blocker define and then write the file header. + */ + char szTmp[4096]; + const char *pszName = RTPathFilename(g_pszScript); + size_t cchName = strlen(pszName); + if (cchName >= sizeof(szTmp) - 64) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName); + szTmp[0] = '_'; + szTmp[1] = '_'; + szTmp[2] = '_'; + memcpy(&szTmp[3], pszName, cchName); + szTmp[3 + cchName + 0] = '_'; + szTmp[3 + cchName + 1] = '_'; + szTmp[3 + cchName + 2] = '_'; + szTmp[3 + cchName + 3] = '\0'; + char *psz = &szTmp[3]; + while (*psz) + { + if (!RT_C_IS_ALNUM(*psz) && *psz != '_') + *psz = '_'; + psz++; + } + + ScmStreamPrintf(pStrm, + "/* $Id: VBoxTpG.cpp $ */\n" + "/** @file\n" + " * Automatically generated from %s. Do NOT edit!\n" + " */\n" + "\n" + "#ifndef %s\n" + "#define %s\n" + "#ifndef RT_WITHOUT_PRAGMA_ONCE\n" + "# pragma once\n" + "#endif\n" + "\n" + "#include <VBox/VBoxTpG.h>\n" + "\n" + "#ifndef %s\n" + "# error \"Expected '%s' to be defined\"\n" + "#endif\n" + "\n" + "RT_C_DECLS_BEGIN\n" + "\n" + "#ifdef VBOX_WITH_DTRACE\n" + "\n" + "# ifdef _MSC_VER\n" + "# pragma data_seg(VTG_LOC_SECT)\n" + "# pragma data_seg()\n" + "# endif\n" + "\n" + , + g_pszScript, + szTmp, + szTmp, + g_pszContextDefine, + g_pszContextDefine); + + /* + * Declare data, code and macros for each probe. + */ + PVTGPROVIDER pProv; + PVTGPROBE pProbe; + PVTGARG pArg; + RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry) + { + /* This macro is not available in ring-3 because we don't have + anything similar available for native dtrace. */ + ScmStreamPrintf(pStrm, "\n\n"); + if (g_fTypeContext != VTG_TYPE_CTX_R3) + { + generateProviderDefineName(szTmp, sizeof(szTmp), pProv->pszName); + ScmStreamPrintf(pStrm, + "extern uint32_t const volatile g_cVTGProviderProbesEnabled_%s;\n" + "# define %s_ANY_PROBES_ENABLED() \\\n" + " (RT_UNLIKELY(g_cVTGProviderProbesEnabled_%s != 0))\n" + "extern uint32_t const volatile g_cVTGProviderSettingsSeqNo_%s;\n" + "# define %s_GET_SETTINGS_SEQ_NO() (g_cVTGProviderSettingsSeqNo_%s)\n" + "\n", + pProv->pszName, + szTmp, pProv->pszName, + pProv->pszName, + szTmp, pProv->pszName); + } + + RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + ScmStreamPrintf(pStrm, + "extern uint32_t const volatile g_cVTGProbeEnabled_%s_%s;\n" + "extern VTGDESCPROBE g_VTGProbeData_%s_%s;\n" + "DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC", + pProv->pszName, pProbe->pszMangledName, + pProv->pszName, pProbe->pszMangledName, + pProv->pszName, pProbe->pszMangledName); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + ScmStreamPrintf(pStrm, ", %s", pArg->pszCtxType); + } + generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName); + ScmStreamPrintf(pStrm, + ");\n" + "# define %s_ENABLED() (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s != 0))\n" + "# define %s_ENABLED_RAW() (g_cVTGProbeEnabled_%s_%s)\n" + "# define %s(" + , + szTmp, pProv->pszName, pProbe->pszMangledName, + szTmp, pProv->pszName, pProbe->pszMangledName, + szTmp); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "%s", pArg->pszName); + else + ScmStreamPrintf(pStrm, ", %s", pArg->pszName); + } + ScmStreamPrintf(pStrm, + ") \\\n" + " do { \\\n" + " if (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \\\n" + " { \\\n" + " VTG_DECL_VTGPROBELOC(s_VTGProbeLoc) = \\\n" + " { __LINE__, 0, 0, __FUNCTION__, &g_VTGProbeData_%s_%s }; \\\n" + " VTGProbeStub_%s_%s(&s_VTGProbeLoc", + pProv->pszName, pProbe->pszMangledName, + pProv->pszName, pProbe->pszMangledName, + pProv->pszName, pProbe->pszMangledName); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName); + } + ScmStreamPrintf(pStrm, + "); \\\n" + " } \\\n" + " { \\\n" ); + uint32_t iArg = 0; + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + if ((pArg->fType & (VTG_TYPE_FIXED_SIZED | VTG_TYPE_AUTO_CONV_PTR)) == VTG_TYPE_FIXED_SIZED) + ScmStreamPrintf(pStrm, + " AssertCompile(sizeof(%s) == %u); \\\n" + " AssertCompile(sizeof(%s) <= %u); \\\n", + pArg->pszTracerType, pArg->fType & VTG_TYPE_SIZE_MASK, + pArg->pszName, pArg->fType & VTG_TYPE_SIZE_MASK); + else if (pArg->fType & (VTG_TYPE_POINTER | VTG_TYPE_HC_ARCH_SIZED)) + ScmStreamPrintf(pStrm, + " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n" + " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n", + pArg->pszName, + pArg->pszTracerType); + iArg++; + } + ScmStreamPrintf(pStrm, + " } \\\n" + " } while (0)\n" + "\n"); + } + } + + ScmStreamPrintf(pStrm, + "\n" + "#else\n" + "\n"); + RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry) + { + if (g_fTypeContext != VTG_TYPE_CTX_R3) + { + generateProviderDefineName(szTmp, sizeof(szTmp), pProv->pszName); + ScmStreamPrintf(pStrm, + "# define %s_ANY_PROBES_ENABLED() (false)\n" + "# define %s_GET_SETTINGS_SEQ_NO() UINT32_C(0)\n" + "\n", + szTmp, szTmp); + } + + RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName); + ScmStreamPrintf(pStrm, + "# define %s_ENABLED() (false)\n" + "# define %s_ENABLED_RAW() UINT32_C(0)\n" + "# define %s(" + , szTmp, szTmp, szTmp); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "%s", pArg->pszName); + else + ScmStreamPrintf(pStrm, ", %s", pArg->pszName); + } + ScmStreamPrintf(pStrm, + ") do { } while (0)\n"); + } + } + + ScmStreamWrite(pStrm, RT_STR_TUPLE("\n" + "#endif\n" + "\n" + "RT_C_DECLS_END\n" + "#endif\n")); + return RTEXITCODE_SUCCESS; +} + + +/** + * Called via generateFile to generate the wrapper header file. + * + * @returns Exit code status. + * @param pStrm The output stream. + */ +static RTEXITCODE generateWrapperHeader(PSCMSTREAM pStrm) +{ + /* + * Calc the double inclusion blocker define and then write the file header. + */ + char szTmp[4096]; + const char *pszName = RTPathFilename(g_pszScript); + size_t cchName = strlen(pszName); + if (cchName >= sizeof(szTmp) - 64) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName); + szTmp[0] = '_'; + szTmp[1] = '_'; + szTmp[2] = '_'; + memcpy(&szTmp[3], pszName, cchName); + strcpy(&szTmp[3 + cchName ], "___WRAPPER___"); + char *psz = &szTmp[3]; + while (*psz) + { + if (!RT_C_IS_ALNUM(*psz) && *psz != '_') + *psz = '_'; + psz++; + } + + ScmStreamPrintf(pStrm, + "/* $Id: VBoxTpG.cpp $ */\n" + "/** @file\n" + " * Automatically generated from %s. Do NOT edit!\n" + " */\n" + "\n" + "#ifndef %s\n" + "#define %s\n" + "\n" + "#include <VBox/VBoxTpG.h>\n" + "\n" + "#ifndef %s\n" + "# error \"Expected '%s' to be defined\"\n" + "#endif\n" + "\n" + "#ifdef VBOX_WITH_DTRACE\n" + "\n" + , + g_pszScript, + szTmp, + szTmp, + g_pszContextDefine, + g_pszContextDefine); + + /* + * Declare macros for each probe. + */ + PVTGPROVIDER pProv; + PVTGPROBE pProbe; + PVTGARG pArg; + RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry) + { + RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName); + ScmStreamPrintf(pStrm, + "# define %s(" + , szTmp); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "%s", pArg->pszName); + else + ScmStreamPrintf(pStrm, ", %s", pArg->pszName); + } + ScmStreamPrintf(pStrm, + ") \\\n" + " do { \\\n" + " if (RT_UNLIKELY(%s_ENABLED())) \\\n" + " { \\\n" + " %s_ORIGINAL(" + , szTmp, szTmp); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + const char *pszFmt = pArg->pszArgPassingFmt; + if (pArg->fType & VTG_TYPE_AUTO_CONV_PTR) + { + /* Casting is required. ASSUMES sizeof(RTR0PTR) == sizeof(RTR3PTR) - safe! */ + pszFmt += sizeof(", ") - 1; + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "(%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName); + else + ScmStreamPrintf(pStrm, ", (%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName); + } + else if (pArg->fType & VTG_TYPE_CONST_CHAR_PTR) + { + /* Casting from 'const char *' (probe) to 'char *' (dtrace) is required to shut up warnings. */ + pszFmt += sizeof(", ") - 1; + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "(char *)%M", pszFmt, pArg->pszName); + else + ScmStreamPrintf(pStrm, ", (char *)%M", pszFmt, pArg->pszName); + } + else + { + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt + sizeof(", ") - 1, pArg->pszName); + else + ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName); + } + } + ScmStreamPrintf(pStrm, + "); \\\n" + " } \\\n" + " } while (0)\n" + "\n"); + } + } + + ScmStreamPrintf(pStrm, + "\n" + "#else\n" + "\n"); + RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry) + { + RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry) + { + generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName); + ScmStreamPrintf(pStrm, + "# define %s(" + , szTmp); + RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry) + { + if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry)) + ScmStreamPrintf(pStrm, "%s", pArg->pszName); + else + ScmStreamPrintf(pStrm, ", %s", pArg->pszName); + } + ScmStreamPrintf(pStrm, + ") do { } while (0)\n"); + } + } + + ScmStreamWrite(pStrm, RT_STR_TUPLE("\n" + "#endif\n" + "\n" + "#endif\n")); + return RTEXITCODE_SUCCESS; +} + + +/** + * Parser error with line and position. + * + * @returns RTEXITCODE_FAILURE. + * @param pStrm The stream. + * @param fAbs Absolute or relative offset. + * @param offSeek When @a fAbs is @c false, the offset from the current + * position to the point of failure. When @a fAbs is @c + * true, it's the absolute position. + * @param pszFormat The message format string. + * @param va Format arguments. + */ +static RTEXITCODE parseErrorExV(PSCMSTREAM pStrm, bool fAbs, size_t offSeek, const char *pszFormat, va_list va) +{ + if (fAbs) + ScmStreamSeekAbsolute(pStrm, offSeek); + else if (offSeek != 0) + ScmStreamSeekRelative(pStrm, -(ssize_t)offSeek); + size_t const off = ScmStreamTell(pStrm); + size_t const iLine = ScmStreamTellLine(pStrm); + ScmStreamSeekByLine(pStrm, iLine); + size_t const offLine = ScmStreamTell(pStrm); + + va_list va2; + va_copy(va2, va); + RTPrintf("%s:%d:%zd: error: %N.\n", g_pszScript, iLine + 1, off - offLine + 1, pszFormat, &va2); + va_end(va2); + + size_t cchLine; + SCMEOL enmEof; + const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof); + if (pszLine) + RTPrintf(" %.*s\n" + " %*s^\n", + cchLine, pszLine, off - offLine, ""); + return RTEXITCODE_FAILURE; +} + + +/** + * Parser error with line and position. + * + * @returns RTEXITCODE_FAILURE. + * @param pStrm The stream. + * @param off The offset from the current position to the point of + * failure. + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + RTEXITCODE rcExit = parseErrorExV(pStrm, false, off, pszFormat, va); + va_end(va); + return rcExit; +} + + +/** + * Parser error with line and position, absolute version. + * + * @returns RTEXITCODE_FAILURE. + * @param pStrm The stream. + * @param off The offset from the current position to the point of + * failure. + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + RTEXITCODE rcExit = parseErrorExV(pStrm, true /*fAbs*/, off, pszFormat, va); + va_end(va); + return rcExit; +} + + +/** + * Parser warning with line and position. + * + * @param pStrm The stream. + * @param fAbs Absolute or relative offset. + * @param offSeek When @a fAbs is @c false, the offset from the current + * position to the point of failure. When @a fAbs is @c + * true, it's the absolute position. + * @param pszFormat The message format string. + * @param va Format arguments. + */ +static void parseWarnExV(PSCMSTREAM pStrm, bool fAbs, size_t offSeek, const char *pszFormat, va_list va) +{ + /* Save the stream position. */ + size_t const offOrg = ScmStreamTell(pStrm); + + if (fAbs) + ScmStreamSeekAbsolute(pStrm, offSeek); + else if (offSeek != 0) + ScmStreamSeekRelative(pStrm, -(ssize_t)offSeek); + size_t const off = ScmStreamTell(pStrm); + size_t const iLine = ScmStreamTellLine(pStrm); + ScmStreamSeekByLine(pStrm, iLine); + size_t const offLine = ScmStreamTell(pStrm); + + va_list va2; + va_copy(va2, va); + RTPrintf("%s:%d:%zd: warning: %N.\n", g_pszScript, iLine + 1, off - offLine + 1, pszFormat, &va2); + va_end(va2); + + size_t cchLine; + SCMEOL enmEof; + const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof); + if (pszLine) + RTPrintf(" %.*s\n" + " %*s^\n", + cchLine, pszLine, off - offLine, ""); + + /* restore the position. */ + int rc = ScmStreamSeekAbsolute(pStrm, offOrg); + AssertRC(rc); +} + +#if 0 /* unused */ +/** + * Parser warning with line and position. + * + * @param pStrm The stream. + * @param off The offset from the current position to the point of + * failure. + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +static void parseWarn(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + parseWarnExV(pStrm, false, off, pszFormat, va); + va_end(va); +} +#endif /* unused */ + +/** + * Parser warning with line and position, absolute version. + * + * @param pStrm The stream. + * @param off The offset from the current position to the point of + * failure. + * @param pszFormat The message format string. + * @param ... Format arguments. + */ +static void parseWarnAbs(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + parseWarnExV(pStrm, true /*fAbs*/, off, pszFormat, va); + va_end(va); +} + + +/** + * Handles a C++ one line comment. + * + * @returns Exit code. + * @param pStrm The stream. + */ +static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm) +{ + ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1); + return RTEXITCODE_SUCCESS; +} + + +/** + * Handles a multi-line C/C++ comment. + * + * @returns Exit code. + * @param pStrm The stream. + */ +static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm) +{ + unsigned ch; + while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0) + { + if (ch == '*') + { + do + ch = ScmStreamGetCh(pStrm); + while (ch == '*'); + if (ch == '/') + return RTEXITCODE_SUCCESS; + } + } + + parseError(pStrm, 1, "Expected end of comment, got end of file"); + return RTEXITCODE_FAILURE; +} + + +/** + * Skips spaces and comments. + * + * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE. + * @param pStrm The stream.. + */ +static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm) +{ + unsigned ch; + while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0) + { + if (!RT_C_IS_SPACE(ch) && ch != '/') + return RTEXITCODE_SUCCESS; + unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2); + if (ch == '/') + { + ch = ScmStreamGetCh(pStrm); + RTEXITCODE rcExit; + if (ch == '*') + rcExit = parseMultiLineComment(pStrm); + else if (ch == '/') + rcExit = parseOneLineComment(pStrm); + else + rcExit = parseError(pStrm, 2, "Unexpected character"); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + } + } + + return parseError(pStrm, 0, "Unexpected end of file"); +} + + +/** + * Skips spaces and comments, returning the next character. + * + * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or + * failure. + * @param pStrm The stream. + */ +static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm) +{ + unsigned ch; + while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0) + { + if (!RT_C_IS_SPACE(ch) && ch != '/') + return ch; + if (ch == '/') + { + ch = ScmStreamGetCh(pStrm); + RTEXITCODE rcExit; + if (ch == '*') + rcExit = parseMultiLineComment(pStrm); + else if (ch == '/') + rcExit = parseOneLineComment(pStrm); + else + rcExit = parseError(pStrm, 2, "Unexpected character"); + if (rcExit != RTEXITCODE_SUCCESS) + return ~(unsigned)0; + } + } + + parseError(pStrm, 0, "Unexpected end of file"); + return ~(unsigned)0; +} + + +/** + * Get the next non-space-non-comment character on a preprocessor line. + * + * @returns The next character. On error message and ~(unsigned)0. + * @param pStrm The stream. + */ +static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm) +{ + size_t off = ScmStreamTell(pStrm) - 1; + unsigned ch; + while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0) + { + if (RT_C_IS_SPACE(ch)) + { + if (ch == '\n' || ch == '\r') + { + parseErrorAbs(pStrm, off, "Invalid preprocessor statement"); + break; + } + } + else if (ch == '\\') + { + size_t off2 = ScmStreamTell(pStrm) - 1; + ch = ScmStreamGetCh(pStrm); + if (ch == '\r') + ch = ScmStreamGetCh(pStrm); + if (ch != '\n') + { + parseErrorAbs(pStrm, off2, "Expected new line"); + break; + } + } + else + return ch; + } + return ~(unsigned)0; +} + + + +/** + * Skips spaces and comments. + * + * @returns Same as ScmStreamCGetWord + * @param pStrm The stream.. + * @param pcchWord Where to return the length. + */ +static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord) +{ + if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS) + return NULL; + return ScmStreamCGetWord(pStrm, pcchWord); +} + + + +/** + * Parses interface stability. + * + * @returns Interface stability if parsed correctly, otherwise error message and + * kVTGStability_Invalid. + * @param pStrm The stream. + * @param ch The first character in the stability spec. + */ +static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch) +{ + switch (ch) + { + case 'E': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External"))) + return kVTGStability_External; + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving"))) + return kVTGStability_Evolving; + break; + case 'I': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal"))) + return kVTGStability_Internal; + break; + case 'O': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete"))) + return kVTGStability_Obsolete; + break; + case 'P': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private"))) + return kVTGStability_Private; + break; + case 'S': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable"))) + return kVTGStability_Stable; + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard"))) + return kVTGStability_Standard; + break; + case 'U': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable"))) + return kVTGStability_Unstable; + break; + } + parseError(pStrm, 1, "Unknown stability specifier"); + return kVTGStability_Invalid; +} + + +/** + * Parses data depndency class. + * + * @returns Data dependency class if parsed correctly, otherwise error message + * and kVTGClass_Invalid. + * @param pStrm The stream. + * @param ch The first character in the stability spec. + */ +static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch) +{ + switch (ch) + { + case 'C': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common"))) + return kVTGClass_Common; + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu"))) + return kVTGClass_Cpu; + break; + case 'G': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group"))) + return kVTGClass_Group; + break; + case 'I': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa"))) + return kVTGClass_Isa; + break; + case 'P': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform"))) + return kVTGClass_Platform; + break; + case 'U': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown"))) + return kVTGClass_Unknown; + break; + } + parseError(pStrm, 1, "Unknown data dependency class specifier"); + return kVTGClass_Invalid; +} + +/** + * Parses a pragma D attributes statement. + * + * @returns Suitable exit code, errors message already written on failure. + * @param pStrm The stream. + */ +static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm) +{ + /* + * "CodeStability/DataStability/DataDepClass" - no spaces allowed. + */ + unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm); + if (ch == ~(unsigned)0) + return RTEXITCODE_FAILURE; + + kVTGStability enmCode = parseStability(pStrm, ch); + if (enmCode == kVTGStability_Invalid) + return RTEXITCODE_FAILURE; + ch = ScmStreamGetCh(pStrm); + if (ch != '/') + return parseError(pStrm, 1, "Expected '/' following the code stability specifier"); + + kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm)); + if (enmData == kVTGStability_Invalid) + return RTEXITCODE_FAILURE; + ch = ScmStreamGetCh(pStrm); + if (ch != '/') + return parseError(pStrm, 1, "Expected '/' following the data stability specifier"); + + kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm)); + if (enmDataDep == kVTGClass_Invalid) + return RTEXITCODE_FAILURE; + + /* + * Expecting 'provider' followed by the name of an provider defined earlier. + */ + ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm); + if (ch == ~(unsigned)0) + return RTEXITCODE_FAILURE; + if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider"))) + return parseError(pStrm, 1, "Expected 'provider'"); + + size_t cchName; + const char *pszName = parseGetNextCWord(pStrm, &cchName); + if (!pszName) + return parseError(pStrm, 1, "Expected provider name"); + + PVTGPROVIDER pProv; + RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry) + { + if ( !strncmp(pProv->pszName, pszName, cchName) + && pProv->pszName[cchName] == '\0') + break; + } + if (RTListNodeIsDummy(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)) + return parseError(pStrm, cchName, "Provider not found"); + + /* + * Which aspect of the provider? + */ + size_t cchAspect; + const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect); + if (!pszAspect) + return parseError(pStrm, 1, "Expected provider aspect"); + + PVTGATTRS pAttrs; + if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8)) + pAttrs = &pProv->AttrSelf; + else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8)) + pAttrs = &pProv->AttrFunctions; + else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6)) + pAttrs = &pProv->AttrModules; + else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4)) + pAttrs = &pProv->AttrName; + else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4)) + pAttrs = &pProv->AttrArguments; + else + return parseError(pStrm, cchAspect, "Unknown aspect"); + + if (pAttrs->enmCode != kVTGStability_Invalid) + return parseError(pStrm, cchAspect, "You have already specified these attributes"); + + pAttrs->enmCode = enmCode; + pAttrs->enmData = enmData; + pAttrs->enmDataDep = enmDataDep; + return RTEXITCODE_SUCCESS; +} + +/** + * Parses a D pragma statement. + * + * @returns Suitable exit code, errors message already written on failure. + * @param pStrm The stream. + */ +static RTEXITCODE parsePragma(PSCMSTREAM pStrm) +{ + RTEXITCODE rcExit; + unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm); + if (ch == ~(unsigned)0) + rcExit = RTEXITCODE_FAILURE; + else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D"))) + { + ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm); + if (ch == ~(unsigned)0) + rcExit = RTEXITCODE_FAILURE; + else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes"))) + rcExit = parsePragmaDAttributes(pStrm); + else + rcExit = parseError(pStrm, 1, "Unknown pragma D"); + } + else + rcExit = parseError(pStrm, 1, "Unknown pragma"); + return rcExit; +} + + +/** + * Classifies the given type expression. + * + * @return Type flags. + * @param pszType The type expression. + * @param pStrm The input stream (for errors + warnings). + * @param offSrc The absolute source position of this expression (for + * warnings). + */ +static uint32_t parseTypeExpression(const char *pszType, PSCMSTREAM pStrm, size_t offSrc) +{ + size_t cchType = strlen(pszType); +#define MY_STRMATCH(a_sz) (cchType == sizeof(a_sz) - 1 && !memcmp(a_sz, pszType, sizeof(a_sz) - 1)) + + /* + * Try detect pointers. + */ + if (pszType[cchType - 1] == '*') + { + if (MY_STRMATCH("const char *")) return VTG_TYPE_POINTER | VTG_TYPE_CONST_CHAR_PTR; + return VTG_TYPE_POINTER; + } + if (pszType[cchType - 1] == '&') + { + parseWarnAbs(pStrm, offSrc, "Please avoid using references like '%s' for probe arguments!", pszType); + return VTG_TYPE_POINTER; + } + + /* + * Standard integer types and IPRT variants. + * It's important that we catch all types larger than 32-bit here or we'll + * screw up the probe argument handling. + */ + if (MY_STRMATCH("int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("uintptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("intptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_SIGNED; + + //if (MY_STRMATCH("uint128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint128_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("uint64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("uint32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("uint16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("uint8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint8_t) | VTG_TYPE_UNSIGNED; + + //if (MY_STRMATCH("int128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int128_t) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("int64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int64_t) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("int32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int32_t) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("int16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int16_t) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("int8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int8_t) | VTG_TYPE_SIGNED; + + if (MY_STRMATCH("RTUINT64U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTUINT32U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTUINT16U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED; + + if (MY_STRMATCH("RTMSINTERVAL")) return VTG_TYPE_FIXED_SIZED | sizeof(RTMSINTERVAL) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTTIMESPEC")) return VTG_TYPE_FIXED_SIZED | sizeof(RTTIMESPEC) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("RTPROCESS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTPROCESS) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTHCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTHCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS; + + if (MY_STRMATCH("RTR3PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3; + if (MY_STRMATCH("RTR0PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0; + if (MY_STRMATCH("RTRCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC; + if (MY_STRMATCH("RTHCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0; + + if (MY_STRMATCH("RTR3UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTR0UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTRCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTHCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED; + + if (MY_STRMATCH("RTR3INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_SIGNED; + if (MY_STRMATCH("RTR0INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED; + if (MY_STRMATCH("RTRCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED; + if (MY_STRMATCH("RTHCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED; + + if (MY_STRMATCH("RTUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED; + + if (MY_STRMATCH("RTHCUINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTR3UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("RTR0UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED; + + if (MY_STRMATCH("RTGCUINTREG")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTREG) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCINTPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTPTR) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPTR32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR32) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPTR64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR64) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPHYS32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS32) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST; + if (MY_STRMATCH("RTGCPHYS64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS64) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST; + + /* + * The special VBox types. + */ + if (MY_STRMATCH("PVM")) return VTG_TYPE_POINTER; + if (MY_STRMATCH("PVMCPU")) return VTG_TYPE_POINTER; + if (MY_STRMATCH("PCPUMCTX")) return VTG_TYPE_POINTER; + + /* + * Preaching time. + */ + if ( MY_STRMATCH("unsigned long") + || MY_STRMATCH("unsigned long long") + || MY_STRMATCH("signed long") + || MY_STRMATCH("signed long long") + || MY_STRMATCH("long") + || MY_STRMATCH("long long") + || MY_STRMATCH("char") + || MY_STRMATCH("signed char") + || MY_STRMATCH("unsigned char") + || MY_STRMATCH("double") + || MY_STRMATCH("long double") + || MY_STRMATCH("float") + ) + { + RTMsgError("Please do NOT use the type '%s' for probe arguments!", pszType); + g_cTypeErrors++; + return 0; + } + + if ( MY_STRMATCH("unsigned") + || MY_STRMATCH("signed") + || MY_STRMATCH("signed int") + || MY_STRMATCH("unsigned int") + || MY_STRMATCH("short") + || MY_STRMATCH("signed short") + || MY_STRMATCH("unsigned short") + ) + parseWarnAbs(pStrm, offSrc, "Please avoid using the type '%s' for probe arguments!", pszType); + if (MY_STRMATCH("unsigned")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("unsigned int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED; + if (MY_STRMATCH("signed")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("signed int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("signed short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED; + if (MY_STRMATCH("unsigned short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_UNSIGNED; + + /* + * What we haven't caught by now is either unknown to us or wrong. + */ + if (pszType[0] == 'P') + { + RTMsgError("Type '%s' looks like a pointer typedef, please do NOT use those " + "but rather the non-pointer typedef or struct with '*'", + pszType); + g_cTypeErrors++; + return VTG_TYPE_POINTER; + } + + RTMsgError("Don't know '%s' - please change or fix VBoxTpG", pszType); + g_cTypeErrors++; + +#undef MY_STRCMP + return 0; +} + + +/** + * Initializes the members of an argument. + * + * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg. + * @param pProbe The probe. + * @param pArg The argument. + * @param pStrm The input stream (for errors + warnings). + * @param pchType The type. + * @param cchType The type length. + * @param pchName The name. + * @param cchName The name length. + */ +static RTEXITCODE parseInitArgument(PVTGPROBE pProbe, PVTGARG pArg, PSCMSTREAM pStrm, + char *pchType, size_t cchType, char *pchName, size_t cchName) +{ + Assert(!pArg->pszName); Assert(!pArg->pszTracerType); Assert(!pArg->pszCtxType); Assert(!pArg->fType); + + pArg->pszArgPassingFmt = ", %s"; + pArg->pszName = RTStrDupN(pchName, cchName); + pArg->pszTracerType = strtabInsertN(pchType, cchType); + if (!pArg->pszTracerType || !pArg->pszName) + return parseError(pStrm, 1, "Out of memory"); + pArg->fType = parseTypeExpression(pArg->pszTracerType, pStrm, pArg->offSrc); + + if ( (pArg->fType & VTG_TYPE_POINTER) + && !(g_fTypeContext & VTG_TYPE_CTX_R0) ) + { + pArg->fType &= ~VTG_TYPE_POINTER; + if ( !strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM") + || !strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU") + || !strcmp(pArg->pszTracerType, "struct CPUMCTX *") || !strcmp(pArg->pszTracerType, "PCPUMCTX") + ) + { + pArg->fType |= VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 + | VTG_TYPE_FIXED_SIZED | (g_cHostBits / 8) + | VTG_TYPE_AUTO_CONV_PTR; + pArg->pszCtxType = RTStrDup("RTR0PTR"); + + if (!strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM")) + pArg->pszArgPassingFmt = ", VTG_VM_TO_R0(%s)"; + else if (!strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU")) + pArg->pszArgPassingFmt = ", VTG_VMCPU_TO_R0(%s)"; + else + { + PVTGARG pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry); + if ( !pFirstArg + || pFirstArg == pArg + || strcmp(pFirstArg->pszName, "a_pVCpu") + || ( strcmp(pFirstArg->pszTracerType, "struct VMCPU *") + && strcmp(pFirstArg->pszTracerType, "PVMCPU *")) ) + return parseError(pStrm, 1, "The automatic ring-0 pointer conversion requires 'a_pVCpu' with type 'struct VMCPU *' as the first argument"); + + if (!strcmp(pArg->pszTracerType, "struct CPUMCTX *")|| !strcmp(pArg->pszTracerType, "PCPUMCTX")) + pArg->pszArgPassingFmt = ", VTG_CPUMCTX_TO_R0(a_pVCpu, %s)"; + else + pArg->pszArgPassingFmt = ", VBoxTpG-Is-Buggy!!"; + } + } + else + { + pArg->fType |= VTG_TYPE_CTX_POINTER | g_fTypeContext | VTG_TYPE_FIXED_SIZED | (g_cBits / 8); + pArg->pszCtxType = RTStrDupN(pchType, cchType); + } + } + else + pArg->pszCtxType = RTStrDupN(pchType, cchType); + if (!pArg->pszCtxType) + return parseError(pStrm, 1, "Out of memory"); + + return RTEXITCODE_SUCCESS; +} + + +/** + * Unmangles the probe name. + * + * This involves translating double underscore to dash. + * + * @returns Pointer to the unmangled name in the string table. + * @param pszMangled The mangled name. + */ +static const char *parseUnmangleProbeName(const char *pszMangled) +{ + size_t cchMangled = strlen(pszMangled); + char *pszTmp = (char *)alloca(cchMangled + 2); + const char *pszSrc = pszMangled; + char *pszDst = pszTmp; + + while (*pszSrc) + { + if (pszSrc[0] == '_' && pszSrc[1] == '_' && pszSrc[2] != '_') + { + *pszDst++ = '-'; + pszSrc += 2; + } + else + *pszDst++ = *pszSrc++; + } + *pszDst = '\0'; + + return strtabInsertN(pszTmp, pszDst - pszTmp); +} + + +/** + * Parses a D probe statement. + * + * @returns Suitable exit code, errors message already written on failure. + * @param pStrm The stream. + * @param pProv The provider being parsed. + */ +static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv) +{ + size_t const iProbeLine = ScmStreamTellLine(pStrm); + + /* + * Next up is a name followed by an opening parenthesis. + */ + size_t cchProbe; + const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe); + if (!pszProbe) + return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character"); + unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm); + if (ch != '(') + return parseError(pStrm, 1, "Expected '(' after the probe name"); + + /* + * Create a probe instance. + */ + PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe)); + if (!pProbe) + return parseError(pStrm, 0, "Out of memory"); + RTListInit(&pProbe->ArgHead); + RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry); + pProbe->offArgList = UINT32_MAX; + pProbe->iLine = iProbeLine; + pProbe->pszMangledName = RTStrDupN(pszProbe, cchProbe); + if (!pProbe->pszMangledName) + return parseError(pStrm, 0, "Out of memory"); + pProbe->pszUnmangledName = parseUnmangleProbeName(pProbe->pszMangledName); + if (!pProbe->pszUnmangledName) + return parseError(pStrm, 0, "Out of memory"); + + /* + * Parse loop for the argument. + */ + PVTGARG pArg = NULL; + size_t cchName = 0; + size_t cchArg = 0; + char szArg[4096]; + for (;;) + { + ch = parseGetNextNonSpaceNonCommentCh(pStrm); + switch (ch) + { + case ')': + case ',': + { + /* commit the argument */ + if (pArg) + { + if (!cchName) + return parseError(pStrm, 1, "Argument has no name"); + if (cchArg - cchName - 1 >= 128) + return parseError(pStrm, 1, "Argument type too long"); + RTEXITCODE rcExit = parseInitArgument(pProbe, pArg, pStrm, + szArg, cchArg - cchName - 1, + &szArg[cchArg - cchName], cchName); + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + if (VTG_TYPE_IS_LARGE(pArg->fType)) + pProbe->fHaveLargeArgs = true; + pArg = NULL; + cchName = cchArg = 0; + } + if (ch == ')') + { + size_t off = ScmStreamTell(pStrm); + ch = parseGetNextNonSpaceNonCommentCh(pStrm); + if (ch != ';') + return parseErrorAbs(pStrm, off, "Expected ';'"); + return RTEXITCODE_SUCCESS; + } + break; + } + + default: + { + size_t cchWord; + const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord); + if (!pszWord) + return parseError(pStrm, 0, "Expected argument"); + if (!pArg) + { + pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg)); + if (!pArg) + return parseError(pStrm, 1, "Out of memory"); + RTListAppend(&pProbe->ArgHead, &pArg->ListEntry); + pArg->iArgNo = pProbe->cArgs++; + pArg->pProbe = pProbe; + pArg->offSrc = ScmStreamTell(pStrm) - cchWord; + + if (cchWord + 1 > sizeof(szArg)) + return parseError(pStrm, 1, "Too long parameter declaration"); + memcpy(szArg, pszWord, cchWord); + szArg[cchWord] = '\0'; + cchArg = cchWord; + cchName = 0; + } + else + { + if (cchArg + 1 + cchWord + 1 > sizeof(szArg)) + return parseError(pStrm, 1, "Too long parameter declaration"); + + szArg[cchArg++] = ' '; + memcpy(&szArg[cchArg], pszWord, cchWord); + cchArg += cchWord; + szArg[cchArg] = '\0'; + cchName = cchWord; + } + break; + } + + case '*': + { + if (!pArg) + return parseError(pStrm, 1, "A parameter type does not start with an asterix"); + if (cchArg + sizeof(" *") >= sizeof(szArg)) + return parseError(pStrm, 1, "Too long parameter declaration"); + szArg[cchArg++] = ' '; + szArg[cchArg++] = '*'; + szArg[cchArg ] = '\0'; + cchName = 0; + break; + } + + case ~(unsigned)0: + return parseError(pStrm, 0, "Missing closing ')' on probe"); + } + } +} + +/** + * Parses a D provider statement. + * + * @returns Suitable exit code, errors message already written on failure. + * @param pStrm The stream. + */ +static RTEXITCODE parseProvider(PSCMSTREAM pStrm) +{ + /* + * Next up is a name followed by a curly bracket. Ignore comments. + */ + RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm); + if (rcExit != RTEXITCODE_SUCCESS) + return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character"); + size_t cchName; + const char *pszName = ScmStreamCGetWord(pStrm, &cchName); + if (!pszName) + return parseError(pStrm, 0, "Bad provider name"); + if (RT_C_IS_DIGIT(pszName[cchName - 1])) + return parseError(pStrm, 1, "A provider name cannot end with digit"); + + unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm); + if (ch != '{') + return parseError(pStrm, 1, "Expected '{' after the provider name"); + + /* + * Create a provider instance. + */ + PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv)); + if (!pProv) + return parseError(pStrm, 0, "Out of memory"); + RTListInit(&pProv->ProbeHead); + RTListAppend(&g_ProviderHead, &pProv->ListEntry); + pProv->pszName = strtabInsertN(pszName, cchName); + if (!pProv->pszName) + return parseError(pStrm, 0, "Out of memory"); + + /* + * Parse loop. + */ + for (;;) + { + ch = parseGetNextNonSpaceNonCommentCh(pStrm); + switch (ch) + { + case 'p': + if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe"))) + rcExit = parseProbe(pStrm, pProv); + else + rcExit = parseError(pStrm, 1, "Unexpected character"); + break; + + case '}': + { + size_t off = ScmStreamTell(pStrm); + ch = parseGetNextNonSpaceNonCommentCh(pStrm); + if (ch == ';') + return RTEXITCODE_SUCCESS; + rcExit = parseErrorAbs(pStrm, off, "Expected ';'"); + break; + } + + case ~(unsigned)0: + rcExit = parseError(pStrm, 0, "Missing closing '}' on provider"); + break; + + default: + rcExit = parseError(pStrm, 1, "Unexpected character"); + break; + } + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + } +} + + +static RTEXITCODE parseScript(const char *pszScript) +{ + SCMSTREAM Strm; + int rc = ScmStreamInitForReading(&Strm, pszScript); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc); + if (g_cVerbosity > 0) + RTMsgInfo("Parsing '%s'...", pszScript); + + RTEXITCODE rcExit = RTEXITCODE_SUCCESS; + unsigned ch; + while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0) + { + if (RT_C_IS_SPACE(ch)) + continue; + switch (ch) + { + case '/': + ch = ScmStreamGetCh(&Strm); + if (ch == '*') + rcExit = parseMultiLineComment(&Strm); + else if (ch == '/') + rcExit = parseOneLineComment(&Strm); + else + rcExit = parseError(&Strm, 2, "Unexpected character"); + break; + + case 'p': + if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider"))) + rcExit = parseProvider(&Strm); + else + rcExit = parseError(&Strm, 1, "Unexpected character"); + break; + + case '#': + { + ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm); + if (ch == ~(unsigned)0) + rcExit = RTEXITCODE_FAILURE; + else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma"))) + rcExit = parsePragma(&Strm); + else + rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive"); + break; + } + + default: + rcExit = parseError(&Strm, 1, "Unexpected character"); + break; + } + if (rcExit != RTEXITCODE_SUCCESS) + return rcExit; + } + + ScmStreamDelete(&Strm); + if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS) + RTMsgInfo("Successfully parsed '%s'.", pszScript); + return rcExit; +} + + +/** + * Parses the arguments. + */ +static RTEXITCODE parseArguments(int argc, char **argv) +{ + /* + * Set / Adjust defaults. + */ + int rc = RTPathAbs(g_pszAssemblerIncVal, g_szAssemblerIncVal, sizeof(g_szAssemblerIncVal) - 1); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed: %Rrc", rc); + strcat(g_szAssemblerIncVal, "/"); + g_pszAssemblerIncVal = g_szAssemblerIncVal; + + /* + * Option config. + */ + enum + { + kVBoxTpGOpt_32Bit = 1000, + kVBoxTpGOpt_64Bit, + kVBoxTpGOpt_GenerateWrapperHeader, + kVBoxTpGOpt_Assembler, + kVBoxTpGOpt_AssemblerFmtOpt, + kVBoxTpGOpt_AssemblerFmtVal, + kVBoxTpGOpt_AssemblerOutputOpt, + kVBoxTpGOpt_AssemblerOption, + kVBoxTpGOpt_Pic, + kVBoxTpGOpt_ProbeFnName, + kVBoxTpGOpt_ProbeFnImported, + kVBoxTpGOpt_ProbeFnNotImported, + kVBoxTpGOpt_Host32Bit, + kVBoxTpGOpt_Host64Bit, + kVBoxTpGOpt_RawModeContext, + kVBoxTpGOpt_Ring0Context, + kVBoxTpGOpt_Ring0ContextAgnostic, + kVBoxTpGOpt_Ring3Context, + kVBoxTpGOpt_End + }; + + static RTGETOPTDEF const s_aOpts[] = + { + /* dtrace w/ long options */ + { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING }, + { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING }, + { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING }, + { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING }, + { "--generate-header", 'h', RTGETOPT_REQ_NOTHING }, + { "--output", 'o', RTGETOPT_REQ_STRING }, + { "--script", 's', RTGETOPT_REQ_STRING }, + { "--verbose", 'v', RTGETOPT_REQ_NOTHING }, + /* our stuff */ + { "--generate-wrapper-header", kVBoxTpGOpt_GenerateWrapperHeader, RTGETOPT_REQ_NOTHING }, + { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING }, + { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING }, + { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING }, + { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING }, + { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING }, + { "--pic", kVBoxTpGOpt_Pic, RTGETOPT_REQ_NOTHING }, + { "--probe-fn-name", kVBoxTpGOpt_ProbeFnName, RTGETOPT_REQ_STRING }, + { "--probe-fn-imported", kVBoxTpGOpt_ProbeFnImported, RTGETOPT_REQ_NOTHING }, + { "--probe-fn-not-imported", kVBoxTpGOpt_ProbeFnNotImported, RTGETOPT_REQ_NOTHING }, + { "--host-32-bit", kVBoxTpGOpt_Host32Bit, RTGETOPT_REQ_NOTHING }, + { "--host-64-bit", kVBoxTpGOpt_Host64Bit, RTGETOPT_REQ_NOTHING }, + { "--raw-mode-context", kVBoxTpGOpt_RawModeContext, RTGETOPT_REQ_NOTHING }, + { "--ring-0-context", kVBoxTpGOpt_Ring0Context, RTGETOPT_REQ_NOTHING }, + { "--ring-0-context-agnostic", kVBoxTpGOpt_Ring0ContextAgnostic, RTGETOPT_REQ_NOTHING }, + { "--ring-3-context", kVBoxTpGOpt_Ring3Context, RTGETOPT_REQ_NOTHING }, + /** @todo We're missing a bunch of assembler options! */ + }; + + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetOptState; + rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST); + AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE); + + /* + * Process the options. + */ + while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0) + { + switch (rc) + { + /* + * DTrace compatible options. + */ + case kVBoxTpGOpt_32Bit: + g_cHostBits = g_cBits = 32; + g_pszAssemblerFmtVal = g_szAssemblerFmtVal32; + break; + + case kVBoxTpGOpt_64Bit: + g_cHostBits = g_cBits = 64; + g_pszAssemblerFmtVal = g_szAssemblerFmtVal64; + break; + + case 'C': + g_fApplyCpp = true; + RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed"); + break; + + case 'G': + if ( g_enmAction != kVBoxTpGAction_Nothing + && g_enmAction != kVBoxTpGAction_GenerateObject) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G does not mix with -h or --generate-wrapper-header"); + g_enmAction = kVBoxTpGAction_GenerateObject; + break; + + case 'h': + if (!strcmp(GetOptState.pDef->pszLong, "--generate-header")) + { + if ( g_enmAction != kVBoxTpGAction_Nothing + && g_enmAction != kVBoxTpGAction_GenerateHeader) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h does not mix with -G or --generate-wrapper-header"); + g_enmAction = kVBoxTpGAction_GenerateHeader; + } + else + { + /* --help or similar */ + RTPrintf("VirtualBox Tracepoint Generator\n" + "\n" + "Usage: %s [options]\n" + "\n" + "Options:\n", RTProcShortName()); + for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++) + if ((unsigned)s_aOpts[i].iShort < 128) + RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong); + else + RTPrintf(" %s\n", s_aOpts[i].pszLong); + return RTEXITCODE_SUCCESS; + } + break; + + case 'o': + if (g_pszOutput) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput); + g_pszOutput = ValueUnion.psz; + break; + + case 's': + if (g_pszScript) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript); + g_pszScript = ValueUnion.psz; + break; + + case 'v': + g_cVerbosity++; + break; + + case 'V': + { + /* The following is assuming that svn does it's job here. */ + static const char s_szRev[] = "$Revision: 155244 $"; + const char *psz = RTStrStripL(strchr(s_szRev, ' ')); + RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz); + return RTEXITCODE_SUCCESS; + } + + case VINF_GETOPT_NOT_OPTION: + if (g_enmAction == kVBoxTpGAction_GenerateObject) + break; /* object files, ignore them. */ + return RTGetOptPrintError(rc, &ValueUnion); + + + /* + * Our options. + */ + case kVBoxTpGOpt_GenerateWrapperHeader: + if ( g_enmAction != kVBoxTpGAction_Nothing + && g_enmAction != kVBoxTpGAction_GenerateWrapperHeader) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-wrapper-header does not mix with -h or -G"); + g_enmAction = kVBoxTpGAction_GenerateWrapperHeader; + break; + + case kVBoxTpGOpt_Assembler: + g_pszAssembler = ValueUnion.psz; + break; + + case kVBoxTpGOpt_AssemblerFmtOpt: + g_pszAssemblerFmtOpt = ValueUnion.psz; + break; + + case kVBoxTpGOpt_AssemblerFmtVal: + g_pszAssemblerFmtVal = ValueUnion.psz; + break; + + case kVBoxTpGOpt_AssemblerOutputOpt: + g_pszAssemblerOutputOpt = ValueUnion.psz; + break; + + case kVBoxTpGOpt_AssemblerOption: + if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions)) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions)); + g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz; + g_cAssemblerOptions++; + break; + + case kVBoxTpGOpt_Pic: + g_fPic = true; + break; + + case kVBoxTpGOpt_ProbeFnName: + g_pszProbeFnName = ValueUnion.psz; + break; + + case kVBoxTpGOpt_ProbeFnImported: + g_fProbeFnImported = true; + break; + + case kVBoxTpGOpt_ProbeFnNotImported: + g_fProbeFnImported = false; + break; + + case kVBoxTpGOpt_Host32Bit: + g_cHostBits = 32; + break; + + case kVBoxTpGOpt_Host64Bit: + g_cHostBits = 64; + break; + + case kVBoxTpGOpt_RawModeContext: + g_fTypeContext = VTG_TYPE_CTX_RC; + g_pszContextDefine = "IN_RC"; + g_pszContextDefine2 = NULL; + break; + + case kVBoxTpGOpt_Ring0Context: + g_fTypeContext = VTG_TYPE_CTX_R0; + g_pszContextDefine = "IN_RING0"; + g_pszContextDefine2 = NULL; + break; + + case kVBoxTpGOpt_Ring0ContextAgnostic: + g_fTypeContext = VTG_TYPE_CTX_R0; + g_pszContextDefine = "IN_RING0_AGNOSTIC"; + g_pszContextDefine2 = "IN_RING0"; + break; + + case kVBoxTpGOpt_Ring3Context: + g_fTypeContext = VTG_TYPE_CTX_R3; + g_pszContextDefine = "IN_RING3"; + g_pszContextDefine2 = NULL; + break; + + + /* + * Errors and bugs. + */ + default: + return RTGetOptPrintError(rc, &ValueUnion); + } + } + + /* + * Check that we've got all we need. + */ + if (g_enmAction == kVBoxTpGAction_Nothing) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h, -G or --generate-wrapper-header)"); + if (!g_pszScript) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)"); + if (!g_pszOutput) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)"); + + return RTEXITCODE_SUCCESS; +} + + +int main(int argc, char **argv) +{ + int rc = RTR3InitExe(argc, &argv, 0); + if (RT_FAILURE(rc)) + return 1; + + RTEXITCODE rcExit = parseArguments(argc, argv); + if (rcExit == RTEXITCODE_SUCCESS) + { + /* + * Parse the script. + */ + RTListInit(&g_ProviderHead); + rcExit = parseScript(g_pszScript); + if (rcExit == RTEXITCODE_SUCCESS) + { + /* + * Take action. + */ + if (g_enmAction == kVBoxTpGAction_GenerateHeader) + rcExit = generateFile(g_pszOutput, "header", generateHeader); + else if (g_enmAction == kVBoxTpGAction_GenerateWrapperHeader) + rcExit = generateFile(g_pszOutput, "wrapper header", generateWrapperHeader); + else + rcExit = generateObject(g_pszOutput, g_pszTempAsm); + } + } + + if (rcExit == RTEXITCODE_SUCCESS && g_cTypeErrors > 0) + rcExit = RTEXITCODE_FAILURE; + return rcExit; +} + |