VBoxTpG.cpp revision 9540ab73f6cd0c76f44f6bbfe73f89ac145390b8
/* $Id$ */
/** @file
* VBox Build Tool - VBox Tracepoint Generator.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/initterm.h>
#include "scmstream.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct VTGATTRS
{
} VTGATTRS;
typedef struct VTGARG
{
/** 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. */
} VTGARG;
typedef struct VTGPROBE
{
char *pszMangledName;
const char *pszUnmangledName;
bool fHaveLargeArgs;
} VTGPROBE;
typedef struct VTGPROVIDER
{
const char *pszName;
} VTGPROVIDER;
typedef VTGPROVIDER *PVTGPROVIDER;
/**
* A string table string.
*/
typedef struct VTGSTRING
{
/** The string space core. */
/** The string table offset. */
/** 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. */
/** 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
{
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";
static const char *pszAssembler = "nasm.exe";
static const char *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_pszAssemblerDefOpt = "-D";
static const char *g_pszAssemblerIncOpt = "-I";
static char g_szAssemblerIncVal[RTPATH_MAX];
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.
*/
{
if (pStr)
/*
* Create a new entry.
*/
if (!pStr)
return NULL;
}
/**
* Retrieves the string table offset of the given string table string.
*
* @returns String table offset.
* @param pszStrTabString The string table string.
*/
{
}
/**
* Invokes the assembler.
*
* @returns Exit code.
* @param pszOutput The output file.
* @param pszTempAsm The source file.
*/
{
const char *apszArgs[64];
unsigned iArg = 0;
else
if (g_cBits == 32)
else
if (g_cHostBits == 32)
else
if (g_cBits == 32)
else
if (g_fTypeContext == VTG_TYPE_CTX_R0)
else if (g_fTypeContext == VTG_TYPE_CTX_R3)
else if (g_fTypeContext == VTG_TYPE_CTX_RC)
else
if (g_szAssemblerOsDef[0])
{
}
for (unsigned i = 0; i < g_cAssemblerOptions; i++)
if (g_cVerbosity > 1)
{
for (unsigned i = 0; i < iArg; i++)
}
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to start '%s' (assembler): %Rrc", apszArgs[0], rc);
if (RT_FAILURE(rc))
{
}
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.
*/
{
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file",
if (rcExit == RTEXITCODE_SUCCESS)
{
if (RT_FAILURE(rc))
rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)",
if (rcExit == RTEXITCODE_SUCCESS)
{
if (g_cVerbosity > 0)
if (g_cVerbosity > 1)
{
const char *pszLine;
}
}
}
return rcExit;
}
/**
* @callback_method_impl{FNRTSTRSPACECALLBACK, Writes the string table strings.}
*/
{
" db '%s', 0 ; off=%u len=%zu\n",
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.
*/
{
if (g_cVerbosity > 0)
RTMsgInfo("Generating assembly code...");
/*
* Write the file header.
*/
"; $Id$ \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"
" ; Section order hack!\n"
" ; With the ld64-97.17 linker there was a problem with it determin 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.\n"
" extern section$start$__VTG$__VTGObj\n"
" extern section$end$__VTG$__VTGObj\n"
" [section __VTG __VTGObj align=1024]\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.5', 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 ASM_FORMAT_MACHO ; Apple has a real decent 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"
,
if (RT_FAILURE(rc))
" dd 0%08xh, 0%08xh, 0%08xh, 0%08xh\n"
"%%ifdef ASM_FORMAT_MACHO\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"
/*
* Dump the string table before we start using the strings.
*/
"\n"
";\n"
"; The string table.\n"
";\n"
"VTG_GLOBAL g_achVTGStringTable, data\n");
g_offStrTab = 0;
"VTG_GLOBAL g_achVTGStringTable_End, data\n");
/*
* Write out the argument lists before we use them.
*/
"\n"
";\n"
"; The argument lists.\n"
";\n"
"ALIGNDATA(16)\n"
"VTG_GLOBAL g_aVTGArgLists, data\n");
{
{
continue;
/* Write it. */
" ; off=%u\n"
" db %2u ; Argument count\n"
" db %u ; fHaveLargeArgs\n"
" db 0, 0 ; Reserved\n"
off += 4;
{
" dd %8u ; type '%s' (name '%s')\n"
" dd 0%08xh ; type flags\n",
off += 8;
}
/* Look for matching argument lists (lazy bird walks the whole list). */
{
{
continue;
continue;
while ( cArgs-- > 0
{
}
if (cArgs >= 0)
continue;
}
}
}
}
"VTG_GLOBAL g_aVTGArgLists_End, data\n");
/*
* Probe definitions.
*/
"\n"
";\n"
"; Prob definitions.\n"
";\n"
"ALIGNDATA(16)\n"
"VTG_GLOBAL g_aVTGProbes, data\n"
"\n");
{
{
"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"
,
);
iProbe++;
}
iProvider++;
}
/*
* The provider data.
*/
"\n"
";\n"
"; Provider data.\n"
";\n"
"ALIGNDATA(16)\n"
"VTG_GLOBAL g_aVTGProviders, data\n");
iProvider = 0;
{
" ; 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"
,
pProvider->AttrFunctions.enmCode, pProvider->AttrFunctions.enmData, pProvider->AttrFunctions.enmDataDep,
pProvider->AttrArguments.enmCode, pProvider->AttrArguments.enmData, pProvider->AttrArguments.enmDataDep);
iProvider++;
}
/*
* 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.
*/
";\n"
"; Probe enabled flags.\n"
";\n"
"ALIGNDATA(16)\n"
"VTG_GLOBAL g_acVTGProbeEnabled, data\n"
);
{
{
"VTG_GLOBAL g_cVTGProbeEnabled_%s_%s, data\n"
" dd 0\n",
cProbes++;
}
}
/*
* Emit code for the stub functions.
*/
bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64"));
"\n"
";\n"
"; Prob stubs.\n"
";\n"
"BEGINCODE\n"
"extern %sNAME(%s)\n",
"g_pfnVtgProbeFn:\n"
" dq NAME(%s)\n",
{
{
"\n"
"VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc",
{
}
");\n");
/*
* Check if the probe in question is enabled.
*/
if (g_cBits == 32)
" mov eax, [esp + 4]\n"
" test byte [eax+3], 0x80 ; fEnabled == true?\n"
" jz .return ; jump on false\n");
else if (fWin64)
" test byte [rcx+3], 0x80 ; fEnabled == true?\n"
" jz .return ; jump on false\n");
else
" test byte [rdi+3], 0x80 ; fEnabled == true?\n"
" jz .return ; jump on false\n");
/*
* Jump to the fire-probe function.
*/
if (g_cBits == 32)
" call .mov_ecx_eip_plus_5\n"
".got_eip:\n"
" add ecx, _GLOBAL_OFFSET_TABLE + ($$ - .got_eip) wrt ..gotpc\n"
" mov ecx, [%s@GOT + ecx]\n"
" jmp ecx\n"
".mov_ecx_eip_plus_5:\n"
" pop ecx\n"
" jmp ecx\n"
" mov ecx, IMP2(%s)\n"
" jmp ecx\n"
:
" jmp NAME(%s)\n"
, g_pszProbeFnName);
else if (fWin64)
" mov rax, IMP2(%s)\n"
" jmp rax\n"
:
" jmp NAME(%s)\n"
, g_pszProbeFnName);
else if (fMachO64 && g_fProbeFnImported)
" jmp [g_pfnVtgProbeFn wrt rip]\n");
else
" jmp [rel %s wrt ..got]\n"
" lea rax, [IMP2(%s)]\n"
" jmp rax\n"
:
" jmp NAME(%s)\n"
, g_pszProbeFnName);
".return:\n"
" ret ; The probe was disabled, return\n"
"\n");
}
}
return RTEXITCODE_SUCCESS;
}
{
if (!pszTempAsm)
{
pszTempAsm = psz;
}
if (rcExit == RTEXITCODE_SUCCESS)
return rcExit;
}
static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe)
{
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider);
while (*pszProvider)
*pszBuf++ = '_';
while (*pszProbe)
{
pszProbe++;
}
*pszBuf = '\0';
return RTEXITCODE_SUCCESS;
}
/**
* Called via generateFile to generate the header file.
*
* @returns Exit code status.
* @param pStrm The output stream.
*/
{
/*
* Calc the double inclusion blocker define and then write the file header.
*/
char szTmp[4096];
szTmp[0] = '_';
while (*psz)
{
*psz = '_';
psz++;
}
const char *pszCtxDefine = "UNKNOWN_DEFINE";
if (g_fTypeContext == VTG_TYPE_CTX_R0)
pszCtxDefine = "IN_RING0";
else if (g_fTypeContext == VTG_TYPE_CTX_R3)
pszCtxDefine = "IN_RING3";
else if (g_fTypeContext == VTG_TYPE_CTX_RC)
pszCtxDefine = "IN_RC";
else
AssertFailed();
"/* $Id$ */\n"
"/** @file\n"
" * Automatically generated from %s. Do NOT edit!\n"
" */\n"
"\n"
"#ifndef %s\n"
"#define %s\n"
"\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"
,
/*
* Declare data, code and macros for each probe.
*/
{
{
"extern uint32_t g_cVTGProbeEnabled_%s_%s;\n"
"extern VTGDESCPROBE g_VTGProbeData_%s_%s;\n"
"DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC",
{
}
");\n"
"# define %s_ENABLED() \\\n"
" (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \n"
"# define %s("
, szTmp,
szTmp);
{
else
}
") \\\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",
{
}
"); \\\n"
" } \\\n"
" { \\\n" );
{
" AssertCompile(sizeof(%s) == %u); \\\n"
" AssertCompile(sizeof(%s) <= %u); \\\n",
" AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
" AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
iArg++;
}
" } \\\n"
" } while (0)\n"
"\n");
}
}
"\n"
"#else\n"
"\n");
{
{
"# define %s_ENABLED() (false)\n"
"# define %s("
{
else
}
") do { } while (0)\n");
}
}
"#endif\n"
"\n"
"RT_C_DECLS_END\n"
"#endif\n"));
return RTEXITCODE_SUCCESS;
}
{
}
/**
* Parser error with line and position.
*
* @returns RTEXITCODE_FAILURE.
* @param pStrm The stream.
* @param cb The offset from the current position to the
* point of failure.
* @param pszMsg The message to display.
*/
{
if (cb)
if (pszLine)
RTPrintf(" %.*s\n"
" %*s^\n",
return RTEXITCODE_FAILURE;
}
/**
* Parser error with line and position.
*
* @returns RTEXITCODE_FAILURE.
* @param pStrm The stream.
* @param cb The offset from the current position to the
* point of failure.
* @param pszMsg The message to display.
*/
{
}
/**
* Handles a C++ one line comment.
*
* @returns Exit code.
* @param pStrm The stream.
*/
{
return RTEXITCODE_SUCCESS;
}
/**
* Handles a multi-line C/C++ comment.
*
* @returns Exit code.
* @param pStrm The stream.
*/
{
unsigned ch;
{
if (ch == '*')
{
do
while (ch == '*');
if (ch == '/')
return RTEXITCODE_SUCCESS;
}
}
return RTEXITCODE_FAILURE;
}
/**
* Skips spaces and comments.
*
* @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
* @param pStrm The stream..
*/
{
unsigned ch;
{
return RTEXITCODE_SUCCESS;
if (ch == '/')
{
if (ch == '*')
else if (ch == '/')
else
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
}
}
/**
* 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.
*/
{
unsigned ch;
{
return ch;
if (ch == '/')
{
if (ch == '*')
else if (ch == '/')
else
if (rcExit != RTEXITCODE_SUCCESS)
return ~(unsigned)0;
}
}
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.
*/
{
unsigned ch;
{
if (RT_C_IS_SPACE(ch))
{
{
break;
}
}
else if (ch == '\\')
{
if (ch == '\r')
if (ch != '\n')
{
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.
*/
{
return NULL;
}
/**
* 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.
*/
{
switch (ch)
{
case 'E':
return kVTGStability_External;
return kVTGStability_Evolving;
break;
case 'I':
return kVTGStability_Internal;
break;
case 'O':
return kVTGStability_Obsolete;
break;
case 'P':
return kVTGStability_Private;
break;
case 'S':
return kVTGStability_Stable;
return kVTGStability_Standard;
break;
case 'U':
return kVTGStability_Unstable;
break;
}
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.
*/
{
switch (ch)
{
case 'C':
return kVTGClass_Common;
return kVTGClass_Cpu;
break;
case 'G':
return kVTGClass_Group;
break;
case 'I':
return kVTGClass_Isa;
break;
case 'P':
return kVTGClass_Platform;
break;
case 'U':
return kVTGClass_Unknown;
break;
}
return kVTGClass_Invalid;
}
/**
* Parses a pragma D attributes statement.
*
* @returns Suitable exit code, errors message already written on failure.
* @param pStrm The stream.
*/
{
/*
* "CodeStability/DataStability/DataDepClass" - no spaces allowed.
*/
if (ch == ~(unsigned)0)
return RTEXITCODE_FAILURE;
if (enmCode == kVTGStability_Invalid)
return RTEXITCODE_FAILURE;
if (ch != '/')
if (enmData == kVTGStability_Invalid)
return RTEXITCODE_FAILURE;
if (ch != '/')
if (enmDataDep == kVTGClass_Invalid)
return RTEXITCODE_FAILURE;
/*
* Expecting 'provider' followed by the name of an provider defined earlier.
*/
if (ch == ~(unsigned)0)
return RTEXITCODE_FAILURE;
if (!pszName)
{
break;
}
/*
* Which aspect of the provider?
*/
if (!pszAspect)
else
return RTEXITCODE_SUCCESS;
}
/**
* Parses a D pragma statement.
*
* @returns Suitable exit code, errors message already written on failure.
* @param pStrm The stream.
*/
{
if (ch == ~(unsigned)0)
{
if (ch == ~(unsigned)0)
else
}
else
return rcExit;
}
/**
* Classifies the given type expression.
*
* @return Type flags.
* @param pszType The type expression.
*/
{
/*
* Try detect pointers.
*/
{
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("uint128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint128_t) | VTG_TYPE_UNSIGNED;
//if (MY_STRMATCH("int128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int128_t) | VTG_TYPE_SIGNED;
if (MY_STRMATCH("RTMSINTERVAL")) return VTG_TYPE_FIXED_SIZED | sizeof(RTMSINTERVAL) | VTG_TYPE_UNSIGNED;
if (MY_STRMATCH("RTHCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTHCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS;
if (MY_STRMATCH("RTHCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
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.
*/
/*
* 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")
)
{
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")
)
/*
* 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);
return VTG_TYPE_POINTER;
}
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).
* @param pchType The type.
* @param cchType The type length.
* @param pchName The name.
* @param cchName The name length.
*/
{
Assert(!pArg->pszName); Assert(!pArg->pszTracerType); Assert(!pArg->pszCtxType); Assert(!pArg->fType);
&& !(g_fTypeContext & VTG_TYPE_CTX_R0) )
{
)
{
else
{
if ( !pFirstArg
return parseError(pStrm, 1, "The automatic ring-0 pointer conversion requires 'a_pVCpu' with type 'struct VMCPU *' as the first argument");
else
}
}
else
{
}
}
else
if (!pArg->pszCtxType)
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)
{
const char *pszSrc = pszMangled;
while (*pszSrc)
{
{
*pszDst++ = '-';
pszSrc += 2;
}
else
}
*pszDst = '\0';
}
/**
* 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.
*/
{
/*
* Next up is a name followed by an opening parenthesis.
*/
if (!pszProbe)
if (ch != '(')
/*
* Create a probe instance.
*/
if (!pProbe)
if (!pProbe->pszMangledName)
if (!pProbe->pszUnmangledName)
/*
* Parse loop for the argument.
*/
char szArg[4096];
for (;;)
{
switch (ch)
{
case ')':
case ',':
{
/* commit the argument */
if (pArg)
{
if (!cchName)
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
pProbe->fHaveLargeArgs = true;
}
if (ch == ')')
{
if (ch != ';')
return RTEXITCODE_SUCCESS;
}
break;
}
default:
{
if (!pszWord)
if (!pArg)
{
if (!pArg)
cchName = 0;
}
else
{
}
break;
}
case '*':
{
if (!pArg)
cchName = 0;
break;
}
case ~(unsigned)0:
}
}
}
/**
* Parses a D provider statement.
*
* @returns Suitable exit code, errors message already written on failure.
* @param pStrm The stream.
*/
{
/*
* Next up is a name followed by a curly bracket. Ignore comments.
*/
if (rcExit != RTEXITCODE_SUCCESS)
if (!pszName)
if (ch != '{')
/*
* Create a provider instance.
*/
if (!pProv)
/*
* Parse loop.
*/
for (;;)
{
switch (ch)
{
case 'p':
else
break;
case '}':
{
if (ch == ';')
return RTEXITCODE_SUCCESS;
break;
}
case ~(unsigned)0:
break;
default:
break;
}
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
}
{
if (RT_FAILURE(rc))
return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
if (g_cVerbosity > 0)
unsigned ch;
{
if (RT_C_IS_SPACE(ch))
continue;
switch (ch)
{
case '/':
if (ch == '*')
else if (ch == '/')
else
break;
case 'p':
else
break;
case '#':
{
if (ch == ~(unsigned)0)
else
break;
}
default:
break;
}
if (rcExit != RTEXITCODE_SUCCESS)
return rcExit;
}
return rcExit;
}
/**
* Parses the arguments.
*/
{
/*
* Set / Adjust defaults.
*/
if (RT_FAILURE(rc))
/*
* Option config.
*/
enum
{
kVBoxTpGOpt_32Bit = 1000,
};
static RTGETOPTDEF const s_aOpts[] =
{
/* dtrace w/ long options */
/* our stuff */
/** @todo We're missing a bunch of assembler options! */
};
rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
/*
* Process the options.
*/
{
switch (rc)
{
/*
* DTrace compatible options.
*/
case kVBoxTpGOpt_32Bit:
break;
case kVBoxTpGOpt_64Bit:
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
break;
case 'h':
{
if ( g_enmAction != kVBoxTpGAction_Nothing
}
else
{
/* --help or similar */
RTPrintf("VirtualBox Tracepoint Generator\n"
"\n"
"Usage: %s [options]\n"
"\n"
"Options:\n", RTProcShortName());
else
return RTEXITCODE_SUCCESS;
}
break;
case 'o':
if (g_pszOutput)
break;
case 's':
if (g_pszScript)
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$";
return RTEXITCODE_SUCCESS;
}
case VINF_GETOPT_NOT_OPTION:
break; /* object files, ignore them. */
/*
* Our options.
*/
case kVBoxTpGOpt_Assembler:
break;
break;
break;
break;
return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
break;
case kVBoxTpGOpt_Pic:
g_fPic = true;
break;
case kVBoxTpGOpt_ProbeFnName:
break;
g_fProbeFnImported = true;
break;
g_fProbeFnImported = false;
break;
case kVBoxTpGOpt_Host32Bit:
g_cHostBits = 32;
break;
case kVBoxTpGOpt_Host64Bit:
g_cHostBits = 64;
break;
break;
case kVBoxTpGOpt_Ring0Context:
break;
case kVBoxTpGOpt_Ring3Context:
break;
/*
* Errors and bugs.
*/
default:
}
}
/*
* Check that we've got all we need.
*/
if (g_enmAction == kVBoxTpGAction_Nothing)
if (!g_pszScript)
if (!g_pszOutput)
return RTEXITCODE_SUCCESS;
}
{
if (RT_FAILURE(rc))
return 1;
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Parse the script.
*/
if (rcExit == RTEXITCODE_SUCCESS)
{
/*
* Take action.
*/
else
}
}
return rcExit;
}