VBoxTpG.cpp revision 2cf9be9fb8dd19b4e5695e3f70c864390c500eb0
/* $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
{
const char *pszName;
const char *pszType;
} VTGARG;
typedef struct VTGPROBE
{
char *pszMangledName;
const char *pszUnmangledName;
} 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;
/** @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 *pszAssembler = "nasm.exe";
static const char *pszAssemblerFmtOpt = "-f";
static const char g_szAssemblerFmtVal32[] = "obj";
static const char g_szAssemblerFmtVal64[] = "elf64";
#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";
#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";
#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;
/** @} */
/**
* 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_cBits == 32)
else
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;
}
/**
* Formats a string and writes it to the SCM stream.
*
* @returns The number of bytes written (>= 0). Negative value are IPRT error
* status codes.
* @param pStream The stream to write to.
* @param pszFormat The format string.
* @param va The arguments to format.
*/
{
char *psz;
if (cch)
{
if (RT_FAILURE(rc))
}
return cch;
}
/**
* Formats a string and writes it to the SCM stream.
*
* @returns The number of bytes written (>= 0). Negative value are IPRT error
* status codes.
* @param pStream The stream to write to.
* @param pszFormat The format string.
* @param ... The arguments to format.
*/
{
return cch;
}
/**
* @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=DATA 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 VTG Obj align=4096]\n"
" [section .data]\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 24 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 24 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 .VTGPrLc.Start progbits alloc noexec write align=1]\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"
" [section .VTGData progbits alloc noexec write align=4096]\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.1', 0, 0\n"
" dd %u\n"
" dd 0\n"
" RTCCPTR_DEF NAME(g_aVTGProviders)\n"
" RTCCPTR_DEF NAME(g_aVTGProviders_End) - NAME(g_aVTGProviders)\n"
" RTCCPTR_DEF NAME(g_aVTGProbes)\n"
" RTCCPTR_DEF NAME(g_aVTGProbes_End) - NAME(g_aVTGProbes)\n"
" RTCCPTR_DEF NAME(g_afVTGProbeEnabled)\n"
" RTCCPTR_DEF NAME(g_afVTGProbeEnabled_End) - NAME(g_afVTGProbeEnabled)\n"
" RTCCPTR_DEF NAME(g_achVTGStringTable)\n"
" RTCCPTR_DEF NAME(g_achVTGStringTable_End) - NAME(g_achVTGStringTable)\n"
" RTCCPTR_DEF NAME(g_aVTGArgLists)\n"
" RTCCPTR_DEF NAME(g_aVTGArgLists_End) - NAME(g_aVTGArgLists)\n"
" RTCCPTR_DEF NAME(g_aVTGPrLc)\n"
" RTCCPTR_DEF 0\n"
" RTCCPTR_DEF 0\n"
" RTCCPTR_DEF 0\n"
" RTCCPTR_DEF 0\n"
,
/*
* Declare the probe enable flags.
*/
";\n"
"; Probe enabled flags. Since these will be accessed all the time\n"
"; they are placed together and early in the section to get some more\n"
"; cache and TLB hits when the probes are disabled.\n"
";\n"
"VTG_GLOBAL g_afVTGProbeEnabled, data\n"
);
{
{
"VTG_GLOBAL g_fVTGProbeEnabled_%s_%s, data\n"
" db 0\n",
cProbes++;
}
}
/*
* 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"
"VTG_GLOBAL g_aVTGArgLists, data\n");
{
{
continue;
/* Write it. */
" ; off=%u\n"
" db %2u ; Argument count\n"
" db 0, 0, 0 ; Reserved\n"
off += 4;
{
" dd %6u ; type '%s'\n"
" dd %6u ; name '%s'\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"
"VTG_GLOBAL g_aVTGProbes, data\n"
"\n");
{
{
"VTG_GLOBAL g_VTGProbeData_%s_%s, data ; idx=#%4u\n"
" dd %6u ; name\n"
" dd %6u ; Argument list offset\n"
" dw g_fVTGProbeEnabled_%s_%s - g_afVTGProbeEnabled\n"
" dw %6u ; provider index\n"
" dd 0 ; for the application\n"
,
iProbe++;
}
iProvider++;
}
/*
* The providers data.
*/
"\n"
";\n"
"; Provider data.\n"
";\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++;
}
/*
* Emit code for the stub functions.
*/
"\n"
";\n"
"; Prob stubs.\n"
";\n"
"BEGINCODE\n"
"extern %sNAME(%s)\n",
{
{
"\n"
"VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc",
{
}
");\n");
bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64"));
/*
* 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");
#if 0
/*
* Shuffle the arguments around, replacing the location pointer with the probe ID.
*/
if (fMachO32)
{
/* Need to recreate the stack frame entirely here as the probe
function differs by taking all uint64_t arguments instead
of uintptr_t. Understandable, but real PITA. */
}
else if (g_cBits == 32)
/* Assumes the size of the arguments are no larger than a
pointer. This is asserted in the header. */
" mov edx, [eax + 4] ; idProbe\n"
" mov ecx, IMP2(%s)\n"
" mov [esp + 4], edx ; Replace pVTGProbeLoc with idProbe.\n"
" jmp ecx\n"
:
" mov edx, [eax + 4] ; idProbe\n"
" mov [esp + 4], edx ; Replace pVTGProbeLoc with idProbe.\n"
" jmp NAME(%s)\n"
, g_pszProbeFnName);
else if (fWin64)
" mov rax, IMP2(%s)\n"
" mov ecx, [rcx + 4] ; idProbe replaces pVTGProbeLoc.\n"
" jmp rax\n"
:
" mov ecx, [rcx + 4] ; idProbe replaces pVTGProbeLoc.\n"
" jmp NAME(%s)\n"
, g_pszProbeFnName);
else
" lea rax, [IMP2(%s)]\n" //??? macho64?
" mov edi, [rdi + 4] ; idProbe replaces pVTGProbeLoc.\n"
" jmp rax\n"
:
" mov edi, [rdi + 4] ; idProbe replaces pVTGProbeLoc.\n"
" jmp NAME(%s)\n"
, g_pszProbeFnName);
#else
/*
* Jump to the fire-probe function.
*/
if (g_cBits == 32)
" 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
" lea rax, [IMP2(%s)]\n" //??? macho64?
" jmp rax\n"
:
" jmp NAME(%s)\n"
, g_pszProbeFnName);
#endif
".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;
}
{
/*
* Calc the double inclusion blocker define and then write the file header.
*/
char szTmp[4096];
szTmp[0] = '_';
while (*psz)
{
*psz = '_';
psz++;
}
"/* $Id$ */\n"
"/** @file\n"
" * Automatically generated from %s. Do NOT edit!\n"
" */\n"
"\n"
"#ifndef %s\n"
"#define %s\n"
"\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"
,
szTmp);
/*
* Declare data, code and macros for each probe.
*/
{
{
"extern bool g_fVTGProbeEnabled_%s_%s;\n"
"extern uint8_t g_VTGProbeData_%s_%s;\n"
"DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC",
{
}
");\n"
"# define %s_ENABLED() \\\n"
" (RT_UNLIKELY(g_fVTGProbeEnabled_%s_%s)) \n"
"# define %s("
, szTmp,
szTmp);
{
else
}
") \\\n"
" do { \\\n"
" if (RT_UNLIKELY(g_fVTGProbeEnabled_%s_%s)) \\\n"
" { \\\n"
" VTG_DECL_VTGPROBELOC(s_VTGProbeLoc) = \\\n"
" { __LINE__, 0, UINT32_MAX, __FUNCTION__, &g_VTGProbeData_%s_%s }; \\\n"
" VTGProbeStub_%s_%s(&s_VTGProbeLoc",
{
}
"); \\\n"
" } \\\n"
" { \\\n" );
{
" AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
" AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
}
" } \\\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;
}
{
}
/**
* If the given C word is at off - 1, return @c true and skip beyond it,
* otherwise return @c false.
*
* @retval true if the given C-word is at the current position minus one char.
* The stream position changes.
* @retval false if not. The stream position is unchanged.
*
* @param pStream The stream.
* @param cchWord The length of the word.
* @param pszWord The word.
*/
{
/* Check stream state. */
/* Sufficient chars left on the line? */
size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
return false;
/* Do they match? */
return false;
/* Is it the end of a C word? */
{
return false;
}
/* Skip ahead. */
return true;
}
/**
* Get's the C word starting at the current position.
*
* @returns Pointer to the word on success and the stream position advanced to
* the end of it.
* NULL on failure, stream position normally unchanged.
* @param pStream The stream to get the C word from.
* @param pcchWord Where to return the word length.
*/
{
/* Check stream state. */
/* Get the number of chars left on the line and locate the current char. */
/* Is it a leading C character. */
return NULL;
/* Find the end of the word. */
char ch;
|| RT_C_IS_ALNUM(ch)))
off++;
return psz;
}
/**
* Get's the C word starting at the current position minus one.
*
* @returns Pointer to the word on success and the stream position advanced to
* the end of it.
* NULL on failure, stream position normally unchanged.
* @param pStream The stream to get the C word from.
* @param pcchWord Where to return the word length.
*/
{
/* Check stream state. */
/* Get the number of chars left on the line and locate the current char. */
size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
/* Is it a leading C character. */
return NULL;
/* Find the end of the word. */
char ch;
|| RT_C_IS_ALNUM(ch)))
off++;
return psz;
}
/**
* 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;
}
/**
* 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 (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 */
/* out stuff */
};
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:
g_cBits = 32;
break;
case kVBoxTpGOpt_64Bit:
g_cBits = 64;
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_ProbeFnName:
break;
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;
}