VirtualBox

source: vbox/trunk/src/bldprogs/VBoxTpG.cpp@ 41343

Last change on this file since 41343 was 41343, checked in by vboxsync, 13 years ago

Convert some ring-3 addresses to ring-0 to ease structure access (PVM, PVMCPU and PCPUMCTX).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 93.4 KB
Line 
1/* $Id: VBoxTpG.cpp 41343 2012-05-16 20:07:33Z vboxsync $ */
2/** @file
3 * VBox Build Tool - VBox Tracepoint Generator.
4 */
5
6/*
7 * Copyright (C) 2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*******************************************************************************
20* Header Files *
21*******************************************************************************/
22#include <VBox/VBoxTpG.h>
23
24#include <iprt/alloca.h>
25#include <iprt/assert.h>
26#include <iprt/ctype.h>
27#include <iprt/env.h>
28#include <iprt/err.h>
29#include <iprt/file.h>
30#include <iprt/getopt.h>
31#include <iprt/initterm.h>
32#include <iprt/list.h>
33#include <iprt/mem.h>
34#include <iprt/message.h>
35#include <iprt/path.h>
36#include <iprt/process.h>
37#include <iprt/stream.h>
38#include <iprt/string.h>
39#include <iprt/uuid.h>
40
41#include "scmstream.h"
42
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47
48typedef struct VTGATTRS
49{
50 kVTGStability enmCode;
51 kVTGStability enmData;
52 kVTGClass enmDataDep;
53} VTGATTRS;
54typedef VTGATTRS *PVTGATTRS;
55
56
57typedef struct VTGARG
58{
59 RTLISTNODE ListEntry;
60 /** The argument name. (heap) */
61 char *pszName;
62 /** The type presented to the tracer (in string table). */
63 const char *pszTracerType;
64 /** The argument type used in the probe method in that context. (heap) */
65 char *pszCtxType;
66 /** Argument passing format string. First and only argument is the name.
67 * (const string) */
68 const char *pszArgPassingFmt;
69 /** The type flags. */
70 uint32_t fType;
71} VTGARG;
72typedef VTGARG *PVTGARG;
73
74typedef struct VTGPROBE
75{
76 RTLISTNODE ListEntry;
77 char *pszMangledName;
78 const char *pszUnmangledName;
79 RTLISTANCHOR ArgHead;
80 uint32_t cArgs;
81 bool fHaveLargeArgs;
82 uint32_t offArgList;
83 uint32_t iProbe;
84} VTGPROBE;
85typedef VTGPROBE *PVTGPROBE;
86
87typedef struct VTGPROVIDER
88{
89 RTLISTNODE ListEntry;
90 const char *pszName;
91
92 uint16_t iFirstProbe;
93 uint16_t cProbes;
94
95 VTGATTRS AttrSelf;
96 VTGATTRS AttrModules;
97 VTGATTRS AttrFunctions;
98 VTGATTRS AttrName;
99 VTGATTRS AttrArguments;
100
101 RTLISTANCHOR ProbeHead;
102} VTGPROVIDER;
103typedef VTGPROVIDER *PVTGPROVIDER;
104
105/**
106 * A string table string.
107 */
108typedef struct VTGSTRING
109{
110 /** The string space core. */
111 RTSTRSPACECORE Core;
112 /** The string table offset. */
113 uint32_t offStrTab;
114 /** The actual string. */
115 char szString[1];
116} VTGSTRING;
117typedef VTGSTRING *PVTGSTRING;
118
119
120/*******************************************************************************
121* Global Variables *
122*******************************************************************************/
123/** The string space organizing the string table strings. Each node is a VTGSTRING. */
124static RTSTRSPACE g_StrSpace = NULL;
125/** Used by the string table enumerator to set VTGSTRING::offStrTab. */
126static uint32_t g_offStrTab;
127/** List of providers created by the parser. */
128static RTLISTANCHOR g_ProviderHead;
129/** The number of type errors. */
130static uint32_t g_cTypeErrors = 0;
131
132
133/** @name Options
134 * @{ */
135static enum
136{
137 kVBoxTpGAction_Nothing,
138 kVBoxTpGAction_GenerateHeader,
139 kVBoxTpGAction_GenerateWrapperHeader,
140 kVBoxTpGAction_GenerateObject
141} g_enmAction = kVBoxTpGAction_Nothing;
142static uint32_t g_cBits = HC_ARCH_BITS;
143static uint32_t g_cHostBits = HC_ARCH_BITS;
144static uint32_t g_fTypeContext = VTG_TYPE_CTX_R0;
145static const char *g_pszContextDefine = "IN_RING0";
146static bool g_fApplyCpp = false;
147static uint32_t g_cVerbosity = 0;
148static const char *g_pszOutput = NULL;
149static const char *g_pszScript = NULL;
150static const char *g_pszTempAsm = NULL;
151#ifdef RT_OS_DARWIN
152static const char *g_pszAssembler = "yasm";
153static const char *g_pszAssemblerFmtOpt = "-f";
154static const char g_szAssemblerFmtVal32[] = "macho32";
155static const char g_szAssemblerFmtVal64[] = "macho64";
156static const char g_szAssemblerOsDef[] = "RT_OS_DARWIN";
157#elif defined(RT_OS_OS2)
158static const char *pszAssembler = "nasm.exe";
159static const char *pszAssemblerFmtOpt = "-f";
160static const char g_szAssemblerFmtVal32[] = "obj";
161static const char g_szAssemblerFmtVal64[] = "elf64";
162static const char g_szAssemblerOsDef[] = "RT_OS_OS2";
163#elif defined(RT_OS_WINDOWS)
164static const char *g_pszAssembler = "yasm.exe";
165static const char *g_pszAssemblerFmtOpt = "-f";
166static const char g_szAssemblerFmtVal32[] = "win32";
167static const char g_szAssemblerFmtVal64[] = "win64";
168static const char g_szAssemblerOsDef[] = "RT_OS_WINDOWS";
169#else
170static const char *g_pszAssembler = "yasm";
171static const char *g_pszAssemblerFmtOpt = "-f";
172static const char g_szAssemblerFmtVal32[] = "elf32";
173static const char g_szAssemblerFmtVal64[] = "elf64";
174# ifdef RT_OS_FREEBSD
175static const char g_szAssemblerOsDef[] = "RT_OS_FREEBSD";
176# elif defined(RT_OS_NETBSD)
177static const char g_szAssemblerOsDef[] = "RT_OS_NETBSD";
178# elif defined(RT_OS_OPENBSD)
179static const char g_szAssemblerOsDef[] = "RT_OS_OPENBSD";
180# elif defined(RT_OS_LINUX)
181static const char g_szAssemblerOsDef[] = "RT_OS_LINUX";
182# elif defined(RT_OS_SOLARIS)
183static const char g_szAssemblerOsDef[] = "RT_OS_SOLARIS";
184# else
185# error "Port me!"
186# endif
187#endif
188static const char *g_pszAssemblerFmtVal = RT_CONCAT(g_szAssemblerFmtVal, HC_ARCH_BITS);
189static const char *g_pszAssemblerDefOpt = "-D";
190static const char *g_pszAssemblerIncOpt = "-I";
191static char g_szAssemblerIncVal[RTPATH_MAX];
192static const char *g_pszAssemblerIncVal = __FILE__ "/../../../include/";
193static const char *g_pszAssemblerOutputOpt = "-o";
194static unsigned g_cAssemblerOptions = 0;
195static const char *g_apszAssemblerOptions[32];
196static const char *g_pszProbeFnName = "SUPR0TracerFireProbe";
197static bool g_fProbeFnImported = true;
198static bool g_fPic = false;
199/** @} */
200
201
202
203
204/**
205 * Inserts a string into the string table, reusing any matching existing string
206 * if possible.
207 *
208 * @returns Read only string.
209 * @param pch The string to insert (need not be terminated).
210 * @param cch The length of the string.
211 */
212static const char *strtabInsertN(const char *pch, size_t cch)
213{
214 PVTGSTRING pStr = (PVTGSTRING)RTStrSpaceGetN(&g_StrSpace, pch, cch);
215 if (pStr)
216 return pStr->szString;
217
218 /*
219 * Create a new entry.
220 */
221 pStr = (PVTGSTRING)RTMemAlloc(RT_OFFSETOF(VTGSTRING, szString[cch + 1]));
222 if (!pStr)
223 return NULL;
224
225 pStr->Core.pszString = pStr->szString;
226 memcpy(pStr->szString, pch, cch);
227 pStr->szString[cch] = '\0';
228 pStr->offStrTab = UINT32_MAX;
229
230 bool fRc = RTStrSpaceInsert(&g_StrSpace, &pStr->Core);
231 Assert(fRc); NOREF(fRc);
232 return pStr->szString;
233}
234
235
236/**
237 * Retrieves the string table offset of the given string table string.
238 *
239 * @returns String table offset.
240 * @param pszStrTabString The string table string.
241 */
242static uint32_t strtabGetOff(const char *pszStrTabString)
243{
244 PVTGSTRING pStr = RT_FROM_MEMBER(pszStrTabString, VTGSTRING, szString[0]);
245 Assert(pStr->Core.pszString == pszStrTabString);
246 return pStr->offStrTab;
247}
248
249
250/**
251 * Invokes the assembler.
252 *
253 * @returns Exit code.
254 * @param pszOutput The output file.
255 * @param pszTempAsm The source file.
256 */
257static RTEXITCODE generateInvokeAssembler(const char *pszOutput, const char *pszTempAsm)
258{
259 const char *apszArgs[64];
260 unsigned iArg = 0;
261
262 apszArgs[iArg++] = g_pszAssembler;
263 apszArgs[iArg++] = g_pszAssemblerFmtOpt;
264 apszArgs[iArg++] = g_pszAssemblerFmtVal;
265 apszArgs[iArg++] = g_pszAssemblerDefOpt;
266 if (!strcmp(g_pszAssemblerFmtVal, "macho32") || !strcmp(g_pszAssemblerFmtVal, "macho64"))
267 apszArgs[iArg++] = "ASM_FORMAT_MACHO";
268 else if (!strcmp(g_pszAssemblerFmtVal, "obj") || !strcmp(g_pszAssemblerFmtVal, "omf"))
269 apszArgs[iArg++] = "ASM_FORMAT_OMF";
270 else if ( !strcmp(g_pszAssemblerFmtVal, "win32")
271 || !strcmp(g_pszAssemblerFmtVal, "win64")
272 || !strcmp(g_pszAssemblerFmtVal, "pe32")
273 || !strcmp(g_pszAssemblerFmtVal, "pe64")
274 || !strcmp(g_pszAssemblerFmtVal, "pe") )
275 apszArgs[iArg++] = "ASM_FORMAT_PE";
276 else if ( !strcmp(g_pszAssemblerFmtVal, "elf32")
277 || !strcmp(g_pszAssemblerFmtVal, "elf64")
278 || !strcmp(g_pszAssemblerFmtVal, "elf"))
279 apszArgs[iArg++] = "ASM_FORMAT_ELF";
280 else
281 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unknown assembler format '%s'", g_pszAssemblerFmtVal);
282 apszArgs[iArg++] = g_pszAssemblerDefOpt;
283 if (g_cBits == 32)
284 apszArgs[iArg++] = "ARCH_BITS=32";
285 else
286 apszArgs[iArg++] = "ARCH_BITS=64";
287 apszArgs[iArg++] = g_pszAssemblerDefOpt;
288 if (g_cHostBits == 32)
289 apszArgs[iArg++] = "HC_ARCH_BITS=32";
290 else
291 apszArgs[iArg++] = "HC_ARCH_BITS=64";
292 apszArgs[iArg++] = g_pszAssemblerDefOpt;
293 if (g_cBits == 32)
294 apszArgs[iArg++] = "RT_ARCH_X86";
295 else
296 apszArgs[iArg++] = "RT_ARCH_AMD64";
297 apszArgs[iArg++] = g_pszAssemblerDefOpt;
298 apszArgs[iArg++] = g_pszContextDefine;
299 if (g_szAssemblerOsDef[0])
300 {
301 apszArgs[iArg++] = g_pszAssemblerDefOpt;
302 apszArgs[iArg++] = g_szAssemblerOsDef;
303 }
304 apszArgs[iArg++] = g_pszAssemblerIncOpt;
305 apszArgs[iArg++] = g_pszAssemblerIncVal;
306 apszArgs[iArg++] = g_pszAssemblerOutputOpt;
307 apszArgs[iArg++] = pszOutput;
308 for (unsigned i = 0; i < g_cAssemblerOptions; i++)
309 apszArgs[iArg++] = g_apszAssemblerOptions[i];
310 apszArgs[iArg++] = pszTempAsm;
311 apszArgs[iArg] = NULL;
312
313 if (g_cVerbosity > 1)
314 {
315 RTMsgInfo("Starting assmbler '%s' with arguments:\n", g_pszAssembler);
316 for (unsigned i = 0; i < iArg; i++)
317 RTMsgInfo(" #%02u: '%s'\n", i, apszArgs[i]);
318 }
319
320 RTPROCESS hProc;
321 int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProc);
322 if (RT_FAILURE(rc))
323 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to start '%s' (assembler): %Rrc", apszArgs[0], rc);
324
325 RTPROCSTATUS Status;
326 rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &Status);
327 if (RT_FAILURE(rc))
328 {
329 RTProcTerminate(hProc);
330 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcWait failed: %Rrc", rc);
331 }
332 if (Status.enmReason == RTPROCEXITREASON_SIGNAL)
333 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: signal %d", Status.iStatus);
334 if (Status.enmReason != RTPROCEXITREASON_NORMAL)
335 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: abend");
336 if (Status.iStatus != 0)
337 return RTMsgErrorExit((RTEXITCODE)Status.iStatus, "The assembler failed: exit code %d", Status.iStatus);
338
339 return RTEXITCODE_SUCCESS;
340}
341
342
343/**
344 * Worker that does the boring bits when generating a file.
345 *
346 * @returns Exit code.
347 * @param pszOutput The name of the output file.
348 * @param pszWhat What kind of file it is.
349 * @param pfnGenerator The callback function that provides the contents
350 * of the file.
351 */
352static RTEXITCODE generateFile(const char *pszOutput, const char *pszWhat,
353 RTEXITCODE (*pfnGenerator)(PSCMSTREAM))
354{
355 SCMSTREAM Strm;
356 int rc = ScmStreamInitForWriting(&Strm, NULL);
357 if (RT_FAILURE(rc))
358 return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file",
359 rc, pszWhat);
360
361 RTEXITCODE rcExit = pfnGenerator(&Strm);
362 if (RT_FAILURE(ScmStreamGetStatus(&Strm)))
363 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Stream error %Rrc generating the %s file",
364 ScmStreamGetStatus(&Strm), pszWhat);
365 if (rcExit == RTEXITCODE_SUCCESS)
366 {
367 rc = ScmStreamWriteToFile(&Strm, "%s", pszOutput);
368 if (RT_FAILURE(rc))
369 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)",
370 rc, pszOutput, pszWhat);
371 if (rcExit == RTEXITCODE_SUCCESS)
372 {
373 if (g_cVerbosity > 0)
374 RTMsgInfo("Successfully generated '%s'.", pszOutput);
375 if (g_cVerbosity > 1)
376 {
377 RTMsgInfo("================ %s - start ================", pszWhat);
378 ScmStreamRewindForReading(&Strm);
379 const char *pszLine;
380 size_t cchLine;
381 SCMEOL enmEol;
382 while ((pszLine = ScmStreamGetLine(&Strm, &cchLine, &enmEol)) != NULL)
383 RTPrintf("%.*s\n", cchLine, pszLine);
384 RTMsgInfo("================ %s - end ================", pszWhat);
385 }
386 }
387 }
388 ScmStreamDelete(&Strm);
389 return rcExit;
390}
391
392
393/**
394 * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes the string table strings.}
395 */
396static DECLCALLBACK(int) generateAssemblyStrTabCallback(PRTSTRSPACECORE pStr, void *pvUser)
397{
398 PVTGSTRING pVtgStr = (PVTGSTRING)pStr;
399 PSCMSTREAM pStrm = (PSCMSTREAM)pvUser;
400
401 pVtgStr->offStrTab = g_offStrTab;
402 g_offStrTab += (uint32_t)pVtgStr->Core.cchString + 1;
403
404 ScmStreamPrintf(pStrm,
405 " db '%s', 0 ; off=%u len=%zu\n",
406 pVtgStr->szString, pVtgStr->offStrTab, pVtgStr->Core.cchString);
407 return VINF_SUCCESS;
408}
409
410
411/**
412 * Generate assembly source that can be turned into an object file.
413 *
414 * (This is a generateFile callback.)
415 *
416 * @returns Exit code.
417 * @param pStrm The output stream.
418 */
419static RTEXITCODE generateAssembly(PSCMSTREAM pStrm)
420{
421 PVTGPROVIDER pProvider;
422 PVTGPROBE pProbe;
423 PVTGARG pArg;
424
425
426 if (g_cVerbosity > 0)
427 RTMsgInfo("Generating assembly code...");
428
429 /*
430 * Write the file header.
431 */
432 ScmStreamPrintf(pStrm,
433 "; $Id: VBoxTpG.cpp 41343 2012-05-16 20:07:33Z vboxsync $ \n"
434 ";; @file\n"
435 "; Automatically generated from %s. Do NOT edit!\n"
436 ";\n"
437 "\n"
438 "%%include \"iprt/asmdefs.mac\"\n"
439 "\n"
440 "\n"
441 ";"
442 "; We put all the data in a dedicated section / segment.\n"
443 ";\n"
444 "; In order to find the probe location specifiers, we do the necessary\n"
445 "; trickery here, ASSUMING that this object comes in first in the link\n"
446 "; editing process.\n"
447 ";\n"
448 "%%ifdef ASM_FORMAT_OMF\n"
449 " %%macro VTG_GLOBAL 2\n"
450 " global NAME(%%1)\n"
451 " NAME(%%1):\n"
452 " %%endmacro\n"
453 " segment VTG.Obj public CLASS=VTG align=4096 use32\n"
454 "\n"
455 "%%elifdef ASM_FORMAT_MACHO\n"
456 " %%macro VTG_GLOBAL 2\n"
457 " global NAME(%%1)\n"
458 " NAME(%%1):\n"
459 " %%endmacro\n"
460 " ; Section order hack!\n"
461 " ; With the ld64-97.17 linker there was a problem with it determin the section\n"
462 " ; order based on symbol references. The references to the start and end of the\n"
463 " ; __VTGPrLc section forced it in front of __VTGObj.\n"
464 " extern section$start$__VTG$__VTGObj\n"
465 " extern section$end$__VTG$__VTGObj\n"
466 " [section __VTG __VTGObj align=1024]\n"
467 "\n"
468 "%%elifdef ASM_FORMAT_PE\n"
469 " %%macro VTG_GLOBAL 2\n"
470 " global NAME(%%1)\n"
471 " NAME(%%1):\n"
472 " %%endmacro\n"
473 " [section VTGPrLc.Begin data align=64]\n"
474 /*" times 16 db 0xcc\n"*/
475 "VTG_GLOBAL g_aVTGPrLc, data\n"
476 " [section VTGPrLc.Data data align=4]\n"
477 " [section VTGPrLc.End data align=4]\n"
478 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
479 /*" times 16 db 0xcc\n"*/
480 " [section VTGObj data align=32]\n"
481 "\n"
482 "%%elifdef ASM_FORMAT_ELF\n"
483 " %%macro VTG_GLOBAL 2\n"
484 " global NAME(%%1):%%2 hidden\n"
485 " NAME(%%1):\n"
486 " %%endmacro\n"
487 " [section .VTGData progbits alloc noexec write align=4096]\n"
488 " [section .VTGPrLc.Begin progbits alloc noexec write align=32]\n"
489 " dd 0,0,0,0, 0,0,0,0\n"
490 "VTG_GLOBAL g_aVTGPrLc, data\n"
491 " [section .VTGPrLc progbits alloc noexec write align=1]\n"
492 " [section .VTGPrLc.End progbits alloc noexec write align=1]\n"
493 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
494 " dd 0,0,0,0, 0,0,0,0\n"
495 " [section .VTGData]\n"
496 "\n"
497 "%%else\n"
498 " %%error \"ASM_FORMAT_XXX is not defined\"\n"
499 "%%endif\n"
500 "\n"
501 "\n"
502 "VTG_GLOBAL g_VTGObjHeader, data\n"
503 " ;0 1 2 3\n"
504 " ;012345678901234567890123456789012\n"
505 " db 'VTG Object Header v1.5', 0, 0\n"
506 " dd %u\n"
507 " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_VTGObjHeader)\n"
508 " dd NAME(g_achVTGStringTable) - NAME(g_VTGObjHeader)\n"
509 " dd NAME(g_achVTGStringTable_End) - NAME(g_achVTGStringTable)\n"
510 " dd NAME(g_aVTGArgLists) - NAME(g_VTGObjHeader)\n"
511 " dd NAME(g_aVTGArgLists_End) - NAME(g_aVTGArgLists)\n"
512 " dd NAME(g_aVTGProbes) - NAME(g_VTGObjHeader)\n"
513 " dd NAME(g_aVTGProbes_End) - NAME(g_aVTGProbes)\n"
514 " dd NAME(g_aVTGProviders) - NAME(g_VTGObjHeader)\n"
515 " dd NAME(g_aVTGProviders_End) - NAME(g_aVTGProviders)\n"
516 " dd NAME(g_acVTGProbeEnabled) - NAME(g_VTGObjHeader)\n"
517 " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_acVTGProbeEnabled)\n"
518 " dd 0\n"
519 " dd 0\n"
520 "%%ifdef ASM_FORMAT_MACHO ; Apple has a real decent linker!\n"
521 "extern section$start$__VTG$__VTGPrLc\n"
522 " RTCCPTR_DEF section$start$__VTG$__VTGPrLc\n"
523 " %%if ARCH_BITS == 32\n"
524 " dd 0\n"
525 " %%endif\n"
526 "extern section$end$__VTG$__VTGPrLc\n"
527 " RTCCPTR_DEF section$end$__VTG$__VTGPrLc\n"
528 " %%if ARCH_BITS == 32\n"
529 " dd 0\n"
530 " %%endif\n"
531 "%%else\n"
532 " RTCCPTR_DEF NAME(g_aVTGPrLc)\n"
533 " %%if ARCH_BITS == 32\n"
534 " dd 0\n"
535 " %%endif\n"
536 " RTCCPTR_DEF NAME(g_aVTGPrLc_End)\n"
537 " %%if ARCH_BITS == 32\n"
538 " dd 0\n"
539 " %%endif\n"
540 "%%endif\n"
541 ,
542 g_pszScript, g_cBits);
543 RTUUID Uuid;
544 int rc = RTUuidCreate(&Uuid);
545 if (RT_FAILURE(rc))
546 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc);
547 ScmStreamPrintf(pStrm,
548 " dd 0%08xh, 0%08xh, 0%08xh, 0%08xh\n"
549 "%%ifdef ASM_FORMAT_MACHO\n"
550 " RTCCPTR_DEF section$start$__VTG$__VTGObj\n"
551 " %%if ARCH_BITS == 32\n"
552 " dd 0\n"
553 " %%endif\n"
554 "%%else\n"
555 " dd 0, 0\n"
556 "%%endif\n"
557 " dd 0, 0\n"
558 , Uuid.au32[0], Uuid.au32[1], Uuid.au32[2], Uuid.au32[3]);
559
560 /*
561 * Dump the string table before we start using the strings.
562 */
563 ScmStreamPrintf(pStrm,
564 "\n"
565 ";\n"
566 "; The string table.\n"
567 ";\n"
568 "VTG_GLOBAL g_achVTGStringTable, data\n");
569 g_offStrTab = 0;
570 RTStrSpaceEnumerate(&g_StrSpace, generateAssemblyStrTabCallback, pStrm);
571 ScmStreamPrintf(pStrm,
572 "VTG_GLOBAL g_achVTGStringTable_End, data\n");
573
574 /*
575 * Write out the argument lists before we use them.
576 */
577 ScmStreamPrintf(pStrm,
578 "\n"
579 ";\n"
580 "; The argument lists.\n"
581 ";\n"
582 "ALIGNDATA(16)\n"
583 "VTG_GLOBAL g_aVTGArgLists, data\n");
584 uint32_t off = 0;
585 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
586 {
587 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
588 {
589 if (pProbe->offArgList != UINT32_MAX)
590 continue;
591
592 /* Write it. */
593 pProbe->offArgList = off;
594 ScmStreamPrintf(pStrm,
595 " ; off=%u\n"
596 " db %2u ; Argument count\n"
597 " db %u ; fHaveLargeArgs\n"
598 " db 0, 0 ; Reserved\n"
599 , off, pProbe->cArgs, (int)pProbe->fHaveLargeArgs);
600 off += 4;
601 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
602 {
603 ScmStreamPrintf(pStrm,
604 " dd %8u ; type '%s' (name '%s')\n"
605 " dd 0%08xh ; type flags\n",
606 strtabGetOff(pArg->pszTracerType), pArg->pszTracerType, pArg->pszName,
607 pArg->fType);
608 off += 8;
609 }
610
611 /* Look for matching argument lists (lazy bird walks the whole list). */
612 PVTGPROVIDER pProv2;
613 RTListForEach(&g_ProviderHead, pProv2, VTGPROVIDER, ListEntry)
614 {
615 PVTGPROBE pProbe2;
616 RTListForEach(&pProvider->ProbeHead, pProbe2, VTGPROBE, ListEntry)
617 {
618 if (pProbe2->offArgList != UINT32_MAX)
619 continue;
620 if (pProbe2->cArgs != pProbe->cArgs)
621 continue;
622
623 PVTGARG pArg2;
624 pArg = RTListNodeGetNext(&pProbe->ArgHead, VTGARG, ListEntry);
625 pArg2 = RTListNodeGetNext(&pProbe2->ArgHead, VTGARG, ListEntry);
626 int32_t cArgs = pProbe->cArgs;
627 while ( cArgs-- > 0
628 && pArg2->pszTracerType == pArg->pszTracerType
629 && pArg2->fType == pArg->fType)
630 {
631 pArg = RTListNodeGetNext(&pArg->ListEntry, VTGARG, ListEntry);
632 pArg2 = RTListNodeGetNext(&pArg2->ListEntry, VTGARG, ListEntry);
633 }
634 if (cArgs >= 0)
635 continue;
636 pProbe2->offArgList = pProbe->offArgList;
637 }
638 }
639 }
640 }
641 ScmStreamPrintf(pStrm,
642 "VTG_GLOBAL g_aVTGArgLists_End, data\n");
643
644
645 /*
646 * Probe definitions.
647 */
648 ScmStreamPrintf(pStrm,
649 "\n"
650 ";\n"
651 "; Prob definitions.\n"
652 ";\n"
653 "ALIGNDATA(16)\n"
654 "VTG_GLOBAL g_aVTGProbes, data\n"
655 "\n");
656 uint32_t iProvider = 0;
657 uint32_t iProbe = 0;
658 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
659 {
660 pProvider->iFirstProbe = iProbe;
661 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
662 {
663 ScmStreamPrintf(pStrm,
664 "VTG_GLOBAL g_VTGProbeData_%s_%s, data ; idx=#%4u\n"
665 " dd %6u ; offName\n"
666 " dd %6u ; offArgList\n"
667 " dw (NAME(g_cVTGProbeEnabled_%s_%s) - NAME(g_acVTGProbeEnabled)) / 4 ; idxEnabled\n"
668 " dw %6u ; idxProvider\n"
669 " dd NAME(g_VTGObjHeader) - NAME(g_VTGProbeData_%s_%s) ; offObjHdr\n"
670 ,
671 pProvider->pszName, pProbe->pszMangledName, iProbe,
672 strtabGetOff(pProbe->pszUnmangledName),
673 pProbe->offArgList,
674 pProvider->pszName, pProbe->pszMangledName,
675 iProvider,
676 pProvider->pszName, pProbe->pszMangledName
677 );
678 pProbe->iProbe = iProbe;
679 iProbe++;
680 }
681 pProvider->cProbes = iProbe - pProvider->iFirstProbe;
682 iProvider++;
683 }
684 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProbes_End, data\n");
685
686 /*
687 * The provider data.
688 */
689 ScmStreamPrintf(pStrm,
690 "\n"
691 ";\n"
692 "; Provider data.\n"
693 ";\n"
694 "ALIGNDATA(16)\n"
695 "VTG_GLOBAL g_aVTGProviders, data\n");
696 iProvider = 0;
697 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
698 {
699 ScmStreamPrintf(pStrm,
700 " ; idx=#%4u - %s\n"
701 " dd %6u ; name\n"
702 " dw %6u ; index of first probe\n"
703 " dw %6u ; count of probes\n"
704 " db %d, %d, %d ; AttrSelf\n"
705 " db %d, %d, %d ; AttrModules\n"
706 " db %d, %d, %d ; AttrFunctions\n"
707 " db %d, %d, %d ; AttrName\n"
708 " db %d, %d, %d ; AttrArguments\n"
709 " db 0 ; reserved\n"
710 ,
711 iProvider, pProvider->pszName,
712 strtabGetOff(pProvider->pszName),
713 pProvider->iFirstProbe,
714 pProvider->cProbes,
715 pProvider->AttrSelf.enmCode, pProvider->AttrSelf.enmData, pProvider->AttrSelf.enmDataDep,
716 pProvider->AttrModules.enmCode, pProvider->AttrModules.enmData, pProvider->AttrModules.enmDataDep,
717 pProvider->AttrFunctions.enmCode, pProvider->AttrFunctions.enmData, pProvider->AttrFunctions.enmDataDep,
718 pProvider->AttrName.enmCode, pProvider->AttrName.enmData, pProvider->AttrName.enmDataDep,
719 pProvider->AttrArguments.enmCode, pProvider->AttrArguments.enmData, pProvider->AttrArguments.enmDataDep);
720 iProvider++;
721 }
722 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProviders_End, data\n");
723
724 /*
725 * Declare the probe enable flags.
726 *
727 * These must be placed at the end so they'll end up adjacent to the probe
728 * locations. This is important for reducing the amount of memory we need
729 * to lock down for user mode modules.
730 */
731 ScmStreamPrintf(pStrm,
732 ";\n"
733 "; Probe enabled flags.\n"
734 ";\n"
735 "ALIGNDATA(16)\n"
736 "VTG_GLOBAL g_acVTGProbeEnabled, data\n"
737 );
738 uint32_t cProbes = 0;
739 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
740 {
741 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
742 {
743 ScmStreamPrintf(pStrm,
744 "VTG_GLOBAL g_cVTGProbeEnabled_%s_%s, data\n"
745 " dd 0\n",
746 pProvider->pszName, pProbe->pszMangledName);
747 cProbes++;
748 }
749 }
750 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_acVTGProbeEnabled_End, data\n");
751 if (cProbes >= _32K)
752 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many probes: %u (max %u)", cProbes, _32K - 1);
753
754
755 /*
756 * Emit code for the stub functions.
757 */
758 bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64"));
759 bool const fMachO64 = g_cBits == 64 && !strcmp(g_pszAssemblerFmtVal, "macho64");
760 bool const fMachO32 = g_cBits == 32 && !strcmp(g_pszAssemblerFmtVal, "macho32");
761 ScmStreamPrintf(pStrm,
762 "\n"
763 ";\n"
764 "; Prob stubs.\n"
765 ";\n"
766 "BEGINCODE\n"
767 "extern %sNAME(%s)\n",
768 g_fProbeFnImported ? "IMP" : "",
769 g_pszProbeFnName);
770 if (fMachO64 && g_fProbeFnImported && !g_fPic)
771 ScmStreamPrintf(pStrm,
772 "g_pfnVtgProbeFn:\n"
773 " dq NAME(%s)\n",
774 g_pszProbeFnName);
775
776 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
777 {
778 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
779 {
780 ScmStreamPrintf(pStrm,
781 "\n"
782 "VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc",
783 pProvider->pszName, pProbe->pszMangledName);
784 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
785 {
786 ScmStreamPrintf(pStrm, ", %s %s", pArg->pszTracerType, pArg->pszName);
787 }
788 ScmStreamPrintf(pStrm,
789 ");\n");
790
791 /*
792 * Check if the probe in question is enabled.
793 */
794 if (g_cBits == 32)
795 ScmStreamPrintf(pStrm,
796 " mov eax, [esp + 4]\n"
797 " test byte [eax+3], 0x80 ; fEnabled == true?\n"
798 " jz .return ; jump on false\n");
799 else if (fWin64)
800 ScmStreamPrintf(pStrm,
801 " test byte [rcx+3], 0x80 ; fEnabled == true?\n"
802 " jz .return ; jump on false\n");
803 else
804 ScmStreamPrintf(pStrm,
805 " test byte [rdi+3], 0x80 ; fEnabled == true?\n"
806 " jz .return ; jump on false\n");
807
808 /*
809 * Jump to the fire-probe function.
810 */
811 if (g_cBits == 32)
812 ScmStreamPrintf(pStrm, g_fPic ?
813 " call .mov_ecx_eip_plus_5\n"
814 ".got_eip:\n"
815 " add ecx, _GLOBAL_OFFSET_TABLE + ($$ - .got_eip) wrt ..gotpc\n"
816 " mov ecx, [%s@GOT + ecx]\n"
817 " jmp ecx\n"
818 ".mov_ecx_eip_plus_5:\n"
819 " pop ecx\n"
820 " jmp ecx\n"
821 : g_fProbeFnImported ?
822 " mov ecx, IMP2(%s)\n"
823 " jmp ecx\n"
824 :
825 " jmp NAME(%s)\n"
826 , g_pszProbeFnName);
827 else if (fWin64)
828 ScmStreamPrintf(pStrm, g_fProbeFnImported ?
829 " mov rax, IMP2(%s)\n"
830 " jmp rax\n"
831 :
832 " jmp NAME(%s)\n"
833 , g_pszProbeFnName);
834 else if (fMachO64 && g_fProbeFnImported)
835 ScmStreamPrintf(pStrm,
836 " jmp [g_pfnVtgProbeFn wrt rip]\n");
837 else
838 ScmStreamPrintf(pStrm, g_fPic ?
839 " jmp [rel %s wrt ..got]\n"
840 : g_fProbeFnImported ?
841 " lea rax, [IMP2(%s)]\n"
842 " jmp rax\n"
843 :
844 " jmp NAME(%s)\n"
845 , g_pszProbeFnName);
846
847 ScmStreamPrintf(pStrm,
848 ".return:\n"
849 " ret ; The probe was disabled, return\n"
850 "\n");
851 }
852 }
853
854 return RTEXITCODE_SUCCESS;
855}
856
857
858static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm)
859{
860 if (!pszTempAsm)
861 {
862 size_t cch = strlen(pszOutput);
863 char *psz = (char *)alloca(cch + sizeof(".asm"));
864 memcpy(psz, pszOutput, cch);
865 memcpy(psz + cch, ".asm", sizeof(".asm"));
866 pszTempAsm = psz;
867 }
868
869 RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly);
870 if (rcExit == RTEXITCODE_SUCCESS)
871 rcExit = generateInvokeAssembler(pszOutput, pszTempAsm);
872 RTFileDelete(pszTempAsm);
873 return rcExit;
874}
875
876
877static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe)
878{
879 size_t cbMax = strlen(pszProvider) + 1 + strlen(pszProbe) + 1;
880 if (cbMax > cbBuf || cbMax > 80)
881 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider);
882
883 while (*pszProvider)
884 *pszBuf++ = RT_C_TO_UPPER(*pszProvider++);
885
886 *pszBuf++ = '_';
887
888 while (*pszProbe)
889 {
890 if (pszProbe[0] == '_' && pszProbe[1] == '_')
891 pszProbe++;
892 *pszBuf++ = RT_C_TO_UPPER(*pszProbe++);
893 }
894
895 *pszBuf = '\0';
896 return RTEXITCODE_SUCCESS;
897}
898
899
900/**
901 * Called via generateFile to generate the header file.
902 *
903 * @returns Exit code status.
904 * @param pStrm The output stream.
905 */
906static RTEXITCODE generateHeader(PSCMSTREAM pStrm)
907{
908 /*
909 * Calc the double inclusion blocker define and then write the file header.
910 */
911 char szTmp[4096];
912 const char *pszName = RTPathFilename(g_pszScript);
913 size_t cchName = strlen(pszName);
914 if (cchName >= sizeof(szTmp) - 64)
915 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
916 szTmp[0] = '_';
917 szTmp[1] = '_';
918 szTmp[2] = '_';
919 memcpy(&szTmp[3], pszName, cchName);
920 szTmp[3 + cchName + 0] = '_';
921 szTmp[3 + cchName + 1] = '_';
922 szTmp[3 + cchName + 2] = '_';
923 szTmp[3 + cchName + 3] = '\0';
924 char *psz = &szTmp[3];
925 while (*psz)
926 {
927 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
928 *psz = '_';
929 psz++;
930 }
931
932 ScmStreamPrintf(pStrm,
933 "/* $Id: VBoxTpG.cpp 41343 2012-05-16 20:07:33Z vboxsync $ */\n"
934 "/** @file\n"
935 " * Automatically generated from %s. Do NOT edit!\n"
936 " */\n"
937 "\n"
938 "#ifndef %s\n"
939 "#define %s\n"
940 "\n"
941 "#include <VBox/VBoxTpG.h>\n"
942 "\n"
943 "#ifndef %s\n"
944 "# error \"Expected '%s' to be defined\"\n"
945 "#endif\n"
946 "\n"
947 "RT_C_DECLS_BEGIN\n"
948 "\n"
949 "#ifdef VBOX_WITH_DTRACE\n"
950 "\n"
951 "# ifdef _MSC_VER\n"
952 "# pragma data_seg(VTG_LOC_SECT)\n"
953 "# pragma data_seg()\n"
954 "# endif\n"
955 "\n"
956 ,
957 g_pszScript,
958 szTmp,
959 szTmp,
960 g_pszContextDefine,
961 g_pszContextDefine);
962
963 /*
964 * Declare data, code and macros for each probe.
965 */
966 PVTGPROVIDER pProv;
967 PVTGPROBE pProbe;
968 PVTGARG pArg;
969 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
970 {
971 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
972 {
973 PVTGARG const pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
974
975 ScmStreamPrintf(pStrm,
976 "extern uint32_t g_cVTGProbeEnabled_%s_%s;\n"
977 "extern VTGDESCPROBE g_VTGProbeData_%s_%s;\n"
978 "DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC",
979 pProv->pszName, pProbe->pszMangledName,
980 pProv->pszName, pProbe->pszMangledName,
981 pProv->pszName, pProbe->pszMangledName);
982 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
983 {
984 ScmStreamPrintf(pStrm, ", %s", pArg->pszCtxType);
985 }
986 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
987 ScmStreamPrintf(pStrm,
988 ");\n"
989 "# define %s_ENABLED() \\\n"
990 " (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \n"
991 "# define %s("
992 , szTmp,
993 pProv->pszName, pProbe->pszMangledName,
994 szTmp);
995 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
996 {
997 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
998 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
999 else
1000 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1001 }
1002 ScmStreamPrintf(pStrm,
1003 ") \\\n"
1004 " do { \\\n"
1005 " if (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \\\n"
1006 " { \\\n"
1007 " VTG_DECL_VTGPROBELOC(s_VTGProbeLoc) = \\\n"
1008 " { __LINE__, 0, 0, __FUNCTION__, &g_VTGProbeData_%s_%s }; \\\n"
1009 " VTGProbeStub_%s_%s(&s_VTGProbeLoc",
1010 pProv->pszName, pProbe->pszMangledName,
1011 pProv->pszName, pProbe->pszMangledName,
1012 pProv->pszName, pProbe->pszMangledName);
1013 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1014 {
1015 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName);
1016 }
1017 ScmStreamPrintf(pStrm,
1018 "); \\\n"
1019 " } \\\n"
1020 " { \\\n" );
1021 uint32_t iArg = 0;
1022 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1023 {
1024 if ((pArg->fType & (VTG_TYPE_FIXED_SIZED | VTG_TYPE_AUTO_CONV_PTR)) == VTG_TYPE_FIXED_SIZED)
1025 ScmStreamPrintf(pStrm,
1026 " AssertCompile(sizeof(%s) == %u); \\\n"
1027 " AssertCompile(sizeof(%s) <= %u); \\\n",
1028 pArg->pszTracerType, pArg->fType & VTG_TYPE_SIZE_MASK,
1029 pArg->pszName, pArg->fType & VTG_TYPE_SIZE_MASK);
1030 else if (pArg->fType & (VTG_TYPE_POINTER | VTG_TYPE_HC_ARCH_SIZED))
1031 ScmStreamPrintf(pStrm,
1032 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
1033 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
1034 pArg->pszName,
1035 pArg->pszTracerType);
1036 iArg++;
1037 }
1038 ScmStreamPrintf(pStrm,
1039 " } \\\n"
1040 " } while (0)\n"
1041 "\n");
1042 }
1043 }
1044
1045 ScmStreamPrintf(pStrm,
1046 "\n"
1047 "#else\n"
1048 "\n");
1049 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1050 {
1051 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1052 {
1053 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1054 ScmStreamPrintf(pStrm,
1055 "# define %s_ENABLED() (false)\n"
1056 "# define %s("
1057 , szTmp, szTmp);
1058 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1059 {
1060 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1061 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1062 else
1063 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1064 }
1065 ScmStreamPrintf(pStrm,
1066 ") do { } while (0)\n");
1067 }
1068 }
1069
1070 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1071 "#endif\n"
1072 "\n"
1073 "RT_C_DECLS_END\n"
1074 "#endif\n"));
1075 return RTEXITCODE_SUCCESS;
1076}
1077
1078
1079/**
1080 * Called via generateFile to generate the wrapper header file.
1081 *
1082 * @returns Exit code status.
1083 * @param pStrm The output stream.
1084 */
1085static RTEXITCODE generateWrapperHeader(PSCMSTREAM pStrm)
1086{
1087 /*
1088 * Calc the double inclusion blocker define and then write the file header.
1089 */
1090 char szTmp[4096];
1091 const char *pszName = RTPathFilename(g_pszScript);
1092 size_t cchName = strlen(pszName);
1093 if (cchName >= sizeof(szTmp) - 64)
1094 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
1095 szTmp[0] = '_';
1096 szTmp[1] = '_';
1097 szTmp[2] = '_';
1098 memcpy(&szTmp[3], pszName, cchName);
1099 strcpy(&szTmp[3 + cchName ], "___WRAPPER___");
1100 char *psz = &szTmp[3];
1101 while (*psz)
1102 {
1103 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
1104 *psz = '_';
1105 psz++;
1106 }
1107
1108 ScmStreamPrintf(pStrm,
1109 "/* $Id: VBoxTpG.cpp 41343 2012-05-16 20:07:33Z vboxsync $ */\n"
1110 "/** @file\n"
1111 " * Automatically generated from %s. Do NOT edit!\n"
1112 " */\n"
1113 "\n"
1114 "#ifndef %s\n"
1115 "#define %s\n"
1116 "\n"
1117 "#include <VBox/VBoxTpG.h>\n"
1118 "\n"
1119 "#ifndef %s\n"
1120 "# error \"Expected '%s' to be defined\"\n"
1121 "#endif\n"
1122 "\n"
1123 "#ifdef VBOX_WITH_DTRACE\n"
1124 "\n"
1125 ,
1126 g_pszScript,
1127 szTmp,
1128 szTmp,
1129 g_pszContextDefine,
1130 g_pszContextDefine);
1131
1132 /*
1133 * Declare macros for each probe.
1134 */
1135 PVTGPROVIDER pProv;
1136 PVTGPROBE pProbe;
1137 PVTGARG pArg;
1138 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1139 {
1140 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1141 {
1142 PVTGARG const pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
1143
1144 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1145 ScmStreamPrintf(pStrm,
1146 "# define %s("
1147 , szTmp);
1148 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1149 {
1150 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1151 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1152 else
1153 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1154 }
1155 ScmStreamPrintf(pStrm,
1156 ") \\\n"
1157 " do { \\\n"
1158 " if (RT_UNLIKELY(%s_ENABLED())) \\\n"
1159 " { \\\n"
1160 " %s_ORIGINAL("
1161 , szTmp, szTmp);
1162 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1163 {
1164 const char *pszFmt = pArg->pszArgPassingFmt;
1165 if (pArg->fType & VTG_TYPE_AUTO_CONV_PTR)
1166 {
1167 /* Casting is required. ASSUMES sizeof(RTR0PTR) == sizeof(RTR3PTR) - safe! */
1168 pszFmt += sizeof(", ") - 1;
1169 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1170 ScmStreamPrintf(pStrm, "(%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1171 else
1172 ScmStreamPrintf(pStrm, ", (%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1173 }
1174 else
1175 {
1176 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1177 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt + sizeof(", ") - 1, pArg->pszName);
1178 else
1179 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName);
1180 }
1181 }
1182 ScmStreamPrintf(pStrm,
1183 "); \\\n"
1184 " } \\\n"
1185 " } while (0)\n"
1186 "\n");
1187 }
1188 }
1189
1190 ScmStreamPrintf(pStrm,
1191 "\n"
1192 "#else\n"
1193 "\n");
1194 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1195 {
1196 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1197 {
1198 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1199 ScmStreamPrintf(pStrm,
1200 "# define %s("
1201 , szTmp);
1202 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1203 {
1204 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1205 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1206 else
1207 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1208 }
1209 ScmStreamPrintf(pStrm,
1210 ") do { } while (0)\n");
1211 }
1212 }
1213
1214 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1215 "#endif\n"
1216 "\n"
1217 "#endif\n"));
1218 return RTEXITCODE_SUCCESS;
1219}
1220
1221
1222/**
1223 * Parser error with line and position.
1224 *
1225 * @returns RTEXITCODE_FAILURE.
1226 * @param pStrm The stream.
1227 * @param cb The offset from the current position to the
1228 * point of failure.
1229 * @param pszMsg The message to display.
1230 */
1231static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t cb, const char *pszMsg)
1232{
1233 if (cb)
1234 ScmStreamSeekRelative(pStrm, -(ssize_t)cb);
1235 size_t const off = ScmStreamTell(pStrm);
1236 size_t const iLine = ScmStreamTellLine(pStrm);
1237 ScmStreamSeekByLine(pStrm, iLine);
1238 size_t const offLine = ScmStreamTell(pStrm);
1239
1240 RTPrintf("%s:%d:%zd: error: %s.\n", g_pszScript, iLine + 1, off - offLine + 1, pszMsg);
1241
1242 size_t cchLine;
1243 SCMEOL enmEof;
1244 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
1245 if (pszLine)
1246 RTPrintf(" %.*s\n"
1247 " %*s^\n",
1248 cchLine, pszLine, off - offLine, "");
1249 return RTEXITCODE_FAILURE;
1250}
1251
1252
1253/**
1254 * Parser error with line and position.
1255 *
1256 * @returns RTEXITCODE_FAILURE.
1257 * @param pStrm The stream.
1258 * @param cb The offset from the current position to the
1259 * point of failure.
1260 * @param pszMsg The message to display.
1261 */
1262static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszMsg)
1263{
1264 ScmStreamSeekAbsolute(pStrm, off);
1265 return parseError(pStrm, 0, pszMsg);
1266}
1267
1268/**
1269 * Handles a C++ one line comment.
1270 *
1271 * @returns Exit code.
1272 * @param pStrm The stream.
1273 */
1274static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
1275{
1276 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
1277 return RTEXITCODE_SUCCESS;
1278}
1279
1280/**
1281 * Handles a multi-line C/C++ comment.
1282 *
1283 * @returns Exit code.
1284 * @param pStrm The stream.
1285 */
1286static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
1287{
1288 unsigned ch;
1289 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1290 {
1291 if (ch == '*')
1292 {
1293 do
1294 ch = ScmStreamGetCh(pStrm);
1295 while (ch == '*');
1296 if (ch == '/')
1297 return RTEXITCODE_SUCCESS;
1298 }
1299 }
1300
1301 parseError(pStrm, 1, "Expected end of comment, got end of file");
1302 return RTEXITCODE_FAILURE;
1303}
1304
1305
1306/**
1307 * Skips spaces and comments.
1308 *
1309 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
1310 * @param pStrm The stream..
1311 */
1312static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
1313{
1314 unsigned ch;
1315 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
1316 {
1317 if (!RT_C_IS_SPACE(ch) && ch != '/')
1318 return RTEXITCODE_SUCCESS;
1319 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
1320 if (ch == '/')
1321 {
1322 ch = ScmStreamGetCh(pStrm);
1323 RTEXITCODE rcExit;
1324 if (ch == '*')
1325 rcExit = parseMultiLineComment(pStrm);
1326 else if (ch == '/')
1327 rcExit = parseOneLineComment(pStrm);
1328 else
1329 rcExit = parseError(pStrm, 2, "Unexpected character");
1330 if (rcExit != RTEXITCODE_SUCCESS)
1331 return rcExit;
1332 }
1333 }
1334
1335 return parseError(pStrm, 0, "Unexpected end of file");
1336}
1337
1338
1339/**
1340 * Skips spaces and comments, returning the next character.
1341 *
1342 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
1343 * failure.
1344 * @param pStrm The stream.
1345 */
1346static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
1347{
1348 unsigned ch;
1349 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1350 {
1351 if (!RT_C_IS_SPACE(ch) && ch != '/')
1352 return ch;
1353 if (ch == '/')
1354 {
1355 ch = ScmStreamGetCh(pStrm);
1356 RTEXITCODE rcExit;
1357 if (ch == '*')
1358 rcExit = parseMultiLineComment(pStrm);
1359 else if (ch == '/')
1360 rcExit = parseOneLineComment(pStrm);
1361 else
1362 rcExit = parseError(pStrm, 2, "Unexpected character");
1363 if (rcExit != RTEXITCODE_SUCCESS)
1364 return ~(unsigned)0;
1365 }
1366 }
1367
1368 parseError(pStrm, 0, "Unexpected end of file");
1369 return ~(unsigned)0;
1370}
1371
1372
1373/**
1374 * Get the next non-space-non-comment character on a preprocessor line.
1375 *
1376 * @returns The next character. On error message and ~(unsigned)0.
1377 * @param pStrm The stream.
1378 */
1379static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
1380{
1381 size_t off = ScmStreamTell(pStrm) - 1;
1382 unsigned ch;
1383 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1384 {
1385 if (RT_C_IS_SPACE(ch))
1386 {
1387 if (ch == '\n' || ch == '\r')
1388 {
1389 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
1390 break;
1391 }
1392 }
1393 else if (ch == '\\')
1394 {
1395 size_t off2 = ScmStreamTell(pStrm) - 1;
1396 ch = ScmStreamGetCh(pStrm);
1397 if (ch == '\r')
1398 ch = ScmStreamGetCh(pStrm);
1399 if (ch != '\n')
1400 {
1401 parseErrorAbs(pStrm, off2, "Expected new line");
1402 break;
1403 }
1404 }
1405 else
1406 return ch;
1407 }
1408 return ~(unsigned)0;
1409}
1410
1411
1412
1413/**
1414 * Skips spaces and comments.
1415 *
1416 * @returns Same as ScmStreamCGetWord
1417 * @param pStrm The stream..
1418 * @param pcchWord Where to return the length.
1419 */
1420static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
1421{
1422 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
1423 return NULL;
1424 return ScmStreamCGetWord(pStrm, pcchWord);
1425}
1426
1427
1428
1429/**
1430 * Parses interface stability.
1431 *
1432 * @returns Interface stability if parsed correctly, otherwise error message and
1433 * kVTGStability_Invalid.
1434 * @param pStrm The stream.
1435 * @param ch The first character in the stability spec.
1436 */
1437static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
1438{
1439 switch (ch)
1440 {
1441 case 'E':
1442 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
1443 return kVTGStability_External;
1444 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
1445 return kVTGStability_Evolving;
1446 break;
1447 case 'I':
1448 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
1449 return kVTGStability_Internal;
1450 break;
1451 case 'O':
1452 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
1453 return kVTGStability_Obsolete;
1454 break;
1455 case 'P':
1456 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
1457 return kVTGStability_Private;
1458 break;
1459 case 'S':
1460 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
1461 return kVTGStability_Stable;
1462 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
1463 return kVTGStability_Standard;
1464 break;
1465 case 'U':
1466 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
1467 return kVTGStability_Unstable;
1468 break;
1469 }
1470 parseError(pStrm, 1, "Unknown stability specifier");
1471 return kVTGStability_Invalid;
1472}
1473
1474
1475/**
1476 * Parses data depndency class.
1477 *
1478 * @returns Data dependency class if parsed correctly, otherwise error message
1479 * and kVTGClass_Invalid.
1480 * @param pStrm The stream.
1481 * @param ch The first character in the stability spec.
1482 */
1483static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
1484{
1485 switch (ch)
1486 {
1487 case 'C':
1488 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
1489 return kVTGClass_Common;
1490 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
1491 return kVTGClass_Cpu;
1492 break;
1493 case 'G':
1494 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
1495 return kVTGClass_Group;
1496 break;
1497 case 'I':
1498 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
1499 return kVTGClass_Isa;
1500 break;
1501 case 'P':
1502 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
1503 return kVTGClass_Platform;
1504 break;
1505 case 'U':
1506 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
1507 return kVTGClass_Unknown;
1508 break;
1509 }
1510 parseError(pStrm, 1, "Unknown data dependency class specifier");
1511 return kVTGClass_Invalid;
1512}
1513
1514/**
1515 * Parses a pragma D attributes statement.
1516 *
1517 * @returns Suitable exit code, errors message already written on failure.
1518 * @param pStrm The stream.
1519 */
1520static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
1521{
1522 /*
1523 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
1524 */
1525 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1526 if (ch == ~(unsigned)0)
1527 return RTEXITCODE_FAILURE;
1528
1529 kVTGStability enmCode = parseStability(pStrm, ch);
1530 if (enmCode == kVTGStability_Invalid)
1531 return RTEXITCODE_FAILURE;
1532 ch = ScmStreamGetCh(pStrm);
1533 if (ch != '/')
1534 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
1535
1536 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
1537 if (enmData == kVTGStability_Invalid)
1538 return RTEXITCODE_FAILURE;
1539 ch = ScmStreamGetCh(pStrm);
1540 if (ch != '/')
1541 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
1542
1543 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
1544 if (enmDataDep == kVTGClass_Invalid)
1545 return RTEXITCODE_FAILURE;
1546
1547 /*
1548 * Expecting 'provider' followed by the name of an provider defined earlier.
1549 */
1550 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1551 if (ch == ~(unsigned)0)
1552 return RTEXITCODE_FAILURE;
1553 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
1554 return parseError(pStrm, 1, "Expected 'provider'");
1555
1556 size_t cchName;
1557 const char *pszName = parseGetNextCWord(pStrm, &cchName);
1558 if (!pszName)
1559 return parseError(pStrm, 1, "Expected provider name");
1560
1561 PVTGPROVIDER pProv;
1562 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1563 {
1564 if ( !strncmp(pProv->pszName, pszName, cchName)
1565 && pProv->pszName[cchName] == '\0')
1566 break;
1567 }
1568 if (RTListNodeIsDummy(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry))
1569 return parseError(pStrm, cchName, "Provider not found");
1570
1571 /*
1572 * Which aspect of the provider?
1573 */
1574 size_t cchAspect;
1575 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
1576 if (!pszAspect)
1577 return parseError(pStrm, 1, "Expected provider aspect");
1578
1579 PVTGATTRS pAttrs;
1580 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
1581 pAttrs = &pProv->AttrSelf;
1582 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
1583 pAttrs = &pProv->AttrFunctions;
1584 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
1585 pAttrs = &pProv->AttrModules;
1586 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
1587 pAttrs = &pProv->AttrName;
1588 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
1589 pAttrs = &pProv->AttrArguments;
1590 else
1591 return parseError(pStrm, cchAspect, "Unknown aspect");
1592
1593 if (pAttrs->enmCode != kVTGStability_Invalid)
1594 return parseError(pStrm, cchAspect, "You have already specified these attributes");
1595
1596 pAttrs->enmCode = enmCode;
1597 pAttrs->enmData = enmData;
1598 pAttrs->enmDataDep = enmDataDep;
1599 return RTEXITCODE_SUCCESS;
1600}
1601
1602/**
1603 * Parses a D pragma statement.
1604 *
1605 * @returns Suitable exit code, errors message already written on failure.
1606 * @param pStrm The stream.
1607 */
1608static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
1609{
1610 RTEXITCODE rcExit;
1611 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1612 if (ch == ~(unsigned)0)
1613 rcExit = RTEXITCODE_FAILURE;
1614 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
1615 {
1616 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1617 if (ch == ~(unsigned)0)
1618 rcExit = RTEXITCODE_FAILURE;
1619 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
1620 rcExit = parsePragmaDAttributes(pStrm);
1621 else
1622 rcExit = parseError(pStrm, 1, "Unknown pragma D");
1623 }
1624 else
1625 rcExit = parseError(pStrm, 1, "Unknown pragma");
1626 return rcExit;
1627}
1628
1629
1630/**
1631 * Classifies the given type expression.
1632 *
1633 * @return Type flags.
1634 * @param pszType The type expression.
1635 */
1636static uint32_t parseTypeExpression(const char *pszType)
1637{
1638 size_t cchType = strlen(pszType);
1639#define MY_STRMATCH(a_sz) (cchType == sizeof(a_sz) - 1 && !memcmp(a_sz, pszType, sizeof(a_sz) - 1))
1640
1641 /*
1642 * Try detect pointers.
1643 */
1644 if (pszType[cchType - 1] == '*') return VTG_TYPE_POINTER;
1645 if (pszType[cchType - 1] == '&')
1646 {
1647 RTMsgWarning("Please avoid using references like '%s' for probe arguments!", pszType);
1648 return VTG_TYPE_POINTER;
1649 }
1650
1651 /*
1652 * Standard integer types and IPRT variants.
1653 * It's important that we catch all types larger than 32-bit here or we'll
1654 * screw up the probe argument handling.
1655 */
1656 if (MY_STRMATCH("int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1657 if (MY_STRMATCH("uintptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_UNSIGNED;
1658 if (MY_STRMATCH("intptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_SIGNED;
1659
1660 //if (MY_STRMATCH("uint128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint128_t) | VTG_TYPE_UNSIGNED;
1661 if (MY_STRMATCH("uint64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1662 if (MY_STRMATCH("uint32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1663 if (MY_STRMATCH("uint16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1664 if (MY_STRMATCH("uint8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint8_t) | VTG_TYPE_UNSIGNED;
1665
1666 //if (MY_STRMATCH("int128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int128_t) | VTG_TYPE_SIGNED;
1667 if (MY_STRMATCH("int64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int64_t) | VTG_TYPE_SIGNED;
1668 if (MY_STRMATCH("int32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int32_t) | VTG_TYPE_SIGNED;
1669 if (MY_STRMATCH("int16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int16_t) | VTG_TYPE_SIGNED;
1670 if (MY_STRMATCH("int8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int8_t) | VTG_TYPE_SIGNED;
1671
1672 if (MY_STRMATCH("RTUINT64U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1673 if (MY_STRMATCH("RTUINT32U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1674 if (MY_STRMATCH("RTUINT16U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1675
1676 if (MY_STRMATCH("RTMSINTERVAL")) return VTG_TYPE_FIXED_SIZED | sizeof(RTMSINTERVAL) | VTG_TYPE_UNSIGNED;
1677 if (MY_STRMATCH("RTTIMESPEC")) return VTG_TYPE_FIXED_SIZED | sizeof(RTTIMESPEC) | VTG_TYPE_SIGNED;
1678 if (MY_STRMATCH("RTPROCESS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTPROCESS) | VTG_TYPE_UNSIGNED;
1679 if (MY_STRMATCH("RTHCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTHCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS;
1680
1681 if (MY_STRMATCH("RTR3PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3;
1682 if (MY_STRMATCH("RTR0PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0;
1683 if (MY_STRMATCH("RTRCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC;
1684 if (MY_STRMATCH("RTHCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0;
1685
1686 if (MY_STRMATCH("RTR3UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1687 if (MY_STRMATCH("RTR0UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1688 if (MY_STRMATCH("RTRCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1689 if (MY_STRMATCH("RTHCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1690
1691 if (MY_STRMATCH("RTR3INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_SIGNED;
1692 if (MY_STRMATCH("RTR0INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1693 if (MY_STRMATCH("RTRCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1694 if (MY_STRMATCH("RTHCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1695
1696 if (MY_STRMATCH("RTUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1697 if (MY_STRMATCH("RTINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1698
1699 if (MY_STRMATCH("RTHCUINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1700 if (MY_STRMATCH("RTR3UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1701 if (MY_STRMATCH("RTR0UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1702
1703 if (MY_STRMATCH("RTGCUINTREG")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTREG) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1704 if (MY_STRMATCH("RTGCPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1705 if (MY_STRMATCH("RTGCINTPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTPTR) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1706 if (MY_STRMATCH("RTGCPTR32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR32) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1707 if (MY_STRMATCH("RTGCPTR64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR64) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1708 if (MY_STRMATCH("RTGCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1709 if (MY_STRMATCH("RTGCPHYS32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS32) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1710 if (MY_STRMATCH("RTGCPHYS64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS64) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1711
1712 /*
1713 * The special VBox types.
1714 */
1715 if (MY_STRMATCH("PVM")) return VTG_TYPE_POINTER;
1716 if (MY_STRMATCH("PVMCPU")) return VTG_TYPE_POINTER;
1717 if (MY_STRMATCH("PCPUMCTX")) return VTG_TYPE_POINTER;
1718
1719 /*
1720 * Preaching time.
1721 */
1722 if ( MY_STRMATCH("unsigned long")
1723 || MY_STRMATCH("unsigned long long")
1724 || MY_STRMATCH("signed long")
1725 || MY_STRMATCH("signed long long")
1726 || MY_STRMATCH("long")
1727 || MY_STRMATCH("long long")
1728 || MY_STRMATCH("char")
1729 || MY_STRMATCH("signed char")
1730 || MY_STRMATCH("unsigned char")
1731 || MY_STRMATCH("double")
1732 || MY_STRMATCH("long double")
1733 || MY_STRMATCH("float")
1734 )
1735 {
1736 RTMsgError("Please do NOT use the type '%s' for probe arguments!", pszType);
1737 g_cTypeErrors++;
1738 return 0;
1739 }
1740
1741 if ( MY_STRMATCH("unsigned")
1742 || MY_STRMATCH("signed")
1743 || MY_STRMATCH("signed int")
1744 || MY_STRMATCH("unsigned int")
1745 || MY_STRMATCH("short")
1746 || MY_STRMATCH("signed short")
1747 || MY_STRMATCH("unsigned short")
1748 )
1749 RTMsgWarning("Please avoid using the type '%s' for probe arguments!", pszType);
1750 if (MY_STRMATCH("unsigned")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1751 if (MY_STRMATCH("unsigned int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1752 if (MY_STRMATCH("signed")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1753 if (MY_STRMATCH("signed int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1754 if (MY_STRMATCH("short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1755 if (MY_STRMATCH("signed short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1756 if (MY_STRMATCH("unsigned short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_UNSIGNED;
1757
1758 /*
1759 * What we haven't caught by now is either unknown to us or wrong.
1760 */
1761 if (pszType[0] == 'P')
1762 {
1763 RTMsgError("Type '%s' looks like a pointer typedef, please do NOT use those "
1764 "but rather the non-pointer typedef or struct with '*'",
1765 pszType);
1766 g_cTypeErrors++;
1767 return VTG_TYPE_POINTER;
1768 }
1769
1770 RTMsgError("Don't know '%s' - please change or fix VBoxTpG", pszType);
1771 g_cTypeErrors++;
1772
1773#undef MY_STRCMP
1774 return 0;
1775}
1776
1777
1778/**
1779 * Initializes the members of an argument.
1780 *
1781 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1782 * @param pProbe The probe.
1783 * @param pArg The argument.
1784 * @param pStrm The input stream (for errors).
1785 * @param pchType The type.
1786 * @param cchType The type length.
1787 * @param pchName The name.
1788 * @param cchName The name length.
1789 */
1790static RTEXITCODE parseInitArgument(PVTGPROBE pProbe, PVTGARG pArg, PSCMSTREAM pStrm,
1791 char *pchType, size_t cchType, char *pchName, size_t cchName)
1792{
1793 Assert(!pArg->pszName); Assert(!pArg->pszTracerType); Assert(!pArg->pszCtxType); Assert(!pArg->fType);
1794
1795 pArg->pszArgPassingFmt = ", %s";
1796 pArg->pszName = RTStrDupN(pchName, cchName);
1797 pArg->pszTracerType = strtabInsertN(pchType, cchType);
1798 if (!pArg->pszTracerType || !pArg->pszName)
1799 return parseError(pStrm, 1, "Out of memory");
1800 pArg->fType = parseTypeExpression(pArg->pszTracerType);
1801
1802 if ( (pArg->fType & VTG_TYPE_POINTER)
1803 && !(g_fTypeContext & VTG_TYPE_CTX_R0) )
1804 {
1805 pArg->fType &= ~VTG_TYPE_POINTER;
1806 if ( !strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM")
1807 || !strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU")
1808 || !strcmp(pArg->pszTracerType, "struct CPUMCTX *") || !strcmp(pArg->pszTracerType, "PCPUMCTX")
1809 )
1810 {
1811 pArg->fType |= VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0
1812 | VTG_TYPE_FIXED_SIZED | (g_cHostBits / 8)
1813 | VTG_TYPE_AUTO_CONV_PTR;
1814 pArg->pszCtxType = RTStrDup("RTR0PTR");
1815
1816 if (!strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM"))
1817 pArg->pszArgPassingFmt = ", VTG_VM_TO_R0(%s)";
1818 else if (!strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU"))
1819 pArg->pszArgPassingFmt = ", VTG_VMCPU_TO_R0(%s)";
1820 else
1821 {
1822 PVTGARG pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
1823 if ( !pFirstArg
1824 || pFirstArg == pArg
1825 || strcmp(pFirstArg->pszName, "a_pVCpu")
1826 || ( strcmp(pFirstArg->pszTracerType, "struct VMCPU *")
1827 && strcmp(pFirstArg->pszTracerType, "PVMCPU *")) )
1828 return parseError(pStrm, 1, "The automatic ring-0 pointer conversion requires 'a_pVCpu' with type 'struct VMCPU *' as the first argument");
1829
1830 if (!strcmp(pArg->pszTracerType, "struct CPUMCTX *")|| !strcmp(pArg->pszTracerType, "PCPUMCTX"))
1831 pArg->pszArgPassingFmt = ", VTG_CPUMCTX_TO_R0(a_pVCpu, %s)";
1832 else
1833 pArg->pszArgPassingFmt = ", VBoxTpG-Is-Buggy!!";
1834 }
1835 }
1836 else
1837 {
1838 pArg->fType |= VTG_TYPE_CTX_POINTER | g_fTypeContext | VTG_TYPE_FIXED_SIZED | (g_cBits / 8);
1839 pArg->pszCtxType = RTStrDupN(pchType, cchType);
1840 }
1841 }
1842 else
1843 pArg->pszCtxType = RTStrDupN(pchType, cchType);
1844 if (!pArg->pszCtxType)
1845 return parseError(pStrm, 1, "Out of memory");
1846
1847 return RTEXITCODE_SUCCESS;
1848}
1849
1850
1851/**
1852 * Unmangles the probe name.
1853 *
1854 * This involves translating double underscore to dash.
1855 *
1856 * @returns Pointer to the unmangled name in the string table.
1857 * @param pszMangled The mangled name.
1858 */
1859static const char *parseUnmangleProbeName(const char *pszMangled)
1860{
1861 size_t cchMangled = strlen(pszMangled);
1862 char *pszTmp = (char *)alloca(cchMangled + 2);
1863 const char *pszSrc = pszMangled;
1864 char *pszDst = pszTmp;
1865
1866 while (*pszSrc)
1867 {
1868 if (pszSrc[0] == '_' && pszSrc[1] == '_' && pszSrc[2] != '_')
1869 {
1870 *pszDst++ = '-';
1871 pszSrc += 2;
1872 }
1873 else
1874 *pszDst++ = *pszSrc++;
1875 }
1876 *pszDst = '\0';
1877
1878 return strtabInsertN(pszTmp, pszDst - pszTmp);
1879}
1880
1881
1882/**
1883 * Parses a D probe statement.
1884 *
1885 * @returns Suitable exit code, errors message already written on failure.
1886 * @param pStrm The stream.
1887 * @param pProv The provider being parsed.
1888 */
1889static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
1890{
1891 /*
1892 * Next up is a name followed by an opening parenthesis.
1893 */
1894 size_t cchProbe;
1895 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
1896 if (!pszProbe)
1897 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
1898 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1899 if (ch != '(')
1900 return parseError(pStrm, 1, "Expected '(' after the probe name");
1901
1902 /*
1903 * Create a probe instance.
1904 */
1905 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
1906 if (!pProbe)
1907 return parseError(pStrm, 0, "Out of memory");
1908 RTListInit(&pProbe->ArgHead);
1909 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
1910 pProbe->offArgList = UINT32_MAX;
1911 pProbe->pszMangledName = RTStrDupN(pszProbe, cchProbe);
1912 if (!pProbe->pszMangledName)
1913 return parseError(pStrm, 0, "Out of memory");
1914 pProbe->pszUnmangledName = parseUnmangleProbeName(pProbe->pszMangledName);
1915 if (!pProbe->pszUnmangledName)
1916 return parseError(pStrm, 0, "Out of memory");
1917
1918 /*
1919 * Parse loop for the argument.
1920 */
1921 PVTGARG pArg = NULL;
1922 size_t cchName = 0;
1923 size_t cchArg = 0;
1924 char szArg[4096];
1925 for (;;)
1926 {
1927 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1928 switch (ch)
1929 {
1930 case ')':
1931 case ',':
1932 {
1933 /* commit the argument */
1934 if (pArg)
1935 {
1936 if (!cchName)
1937 return parseError(pStrm, 1, "Argument has no name");
1938 if (cchArg - cchName - 1 >= 128)
1939 return parseError(pStrm, 1, "Argument type too long");
1940 RTEXITCODE rcExit = parseInitArgument(pProbe, pArg, pStrm,
1941 szArg, cchArg - cchName - 1,
1942 &szArg[cchArg - cchName], cchName);
1943 if (rcExit != RTEXITCODE_SUCCESS)
1944 return rcExit;
1945 if (VTG_TYPE_IS_LARGE(pArg->fType))
1946 pProbe->fHaveLargeArgs = true;
1947 pArg = NULL;
1948 cchName = cchArg = 0;
1949 }
1950 if (ch == ')')
1951 {
1952 size_t off = ScmStreamTell(pStrm);
1953 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1954 if (ch != ';')
1955 return parseErrorAbs(pStrm, off, "Expected ';'");
1956 return RTEXITCODE_SUCCESS;
1957 }
1958 break;
1959 }
1960
1961 default:
1962 {
1963 size_t cchWord;
1964 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
1965 if (!pszWord)
1966 return parseError(pStrm, 0, "Expected argument");
1967 if (!pArg)
1968 {
1969 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
1970 if (!pArg)
1971 return parseError(pStrm, 1, "Out of memory");
1972 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
1973 pProbe->cArgs++;
1974
1975 if (cchWord + 1 > sizeof(szArg))
1976 return parseError(pStrm, 1, "Too long parameter declaration");
1977 memcpy(szArg, pszWord, cchWord);
1978 szArg[cchWord] = '\0';
1979 cchArg = cchWord;
1980 cchName = 0;
1981 }
1982 else
1983 {
1984 if (cchArg + 1 + cchWord + 1 > sizeof(szArg))
1985 return parseError(pStrm, 1, "Too long parameter declaration");
1986
1987 szArg[cchArg++] = ' ';
1988 memcpy(&szArg[cchArg], pszWord, cchWord);
1989 cchArg += cchWord;
1990 szArg[cchArg] = '\0';
1991 cchName = cchWord;
1992 }
1993 break;
1994 }
1995
1996 case '*':
1997 {
1998 if (!pArg)
1999 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
2000 if (cchArg + sizeof(" *") >= sizeof(szArg))
2001 return parseError(pStrm, 1, "Too long parameter declaration");
2002 szArg[cchArg++] = ' ';
2003 szArg[cchArg++] = '*';
2004 szArg[cchArg ] = '\0';
2005 cchName = 0;
2006 break;
2007 }
2008
2009 case ~(unsigned)0:
2010 return parseError(pStrm, 0, "Missing closing ')' on probe");
2011 }
2012 }
2013}
2014
2015/**
2016 * Parses a D provider statement.
2017 *
2018 * @returns Suitable exit code, errors message already written on failure.
2019 * @param pStrm The stream.
2020 */
2021static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
2022{
2023 /*
2024 * Next up is a name followed by a curly bracket. Ignore comments.
2025 */
2026 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
2027 if (rcExit != RTEXITCODE_SUCCESS)
2028 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
2029 size_t cchName;
2030 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
2031 if (!pszName)
2032 return parseError(pStrm, 0, "Bad provider name");
2033 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
2034 return parseError(pStrm, 1, "A provider name cannot end with digit");
2035
2036 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2037 if (ch != '{')
2038 return parseError(pStrm, 1, "Expected '{' after the provider name");
2039
2040 /*
2041 * Create a provider instance.
2042 */
2043 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
2044 if (!pProv)
2045 return parseError(pStrm, 0, "Out of memory");
2046 RTListInit(&pProv->ProbeHead);
2047 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
2048 pProv->pszName = strtabInsertN(pszName, cchName);
2049 if (!pProv->pszName)
2050 return parseError(pStrm, 0, "Out of memory");
2051
2052 /*
2053 * Parse loop.
2054 */
2055 for (;;)
2056 {
2057 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2058 switch (ch)
2059 {
2060 case 'p':
2061 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
2062 rcExit = parseProbe(pStrm, pProv);
2063 else
2064 rcExit = parseError(pStrm, 1, "Unexpected character");
2065 break;
2066
2067 case '}':
2068 {
2069 size_t off = ScmStreamTell(pStrm);
2070 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2071 if (ch == ';')
2072 return RTEXITCODE_SUCCESS;
2073 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
2074 break;
2075 }
2076
2077 case ~(unsigned)0:
2078 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
2079 break;
2080
2081 default:
2082 rcExit = parseError(pStrm, 1, "Unexpected character");
2083 break;
2084 }
2085 if (rcExit != RTEXITCODE_SUCCESS)
2086 return rcExit;
2087 }
2088}
2089
2090
2091static RTEXITCODE parseScript(const char *pszScript)
2092{
2093 SCMSTREAM Strm;
2094 int rc = ScmStreamInitForReading(&Strm, pszScript);
2095 if (RT_FAILURE(rc))
2096 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
2097 if (g_cVerbosity > 0)
2098 RTMsgInfo("Parsing '%s'...", pszScript);
2099
2100 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2101 unsigned ch;
2102 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
2103 {
2104 if (RT_C_IS_SPACE(ch))
2105 continue;
2106 switch (ch)
2107 {
2108 case '/':
2109 ch = ScmStreamGetCh(&Strm);
2110 if (ch == '*')
2111 rcExit = parseMultiLineComment(&Strm);
2112 else if (ch == '/')
2113 rcExit = parseOneLineComment(&Strm);
2114 else
2115 rcExit = parseError(&Strm, 2, "Unexpected character");
2116 break;
2117
2118 case 'p':
2119 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
2120 rcExit = parseProvider(&Strm);
2121 else
2122 rcExit = parseError(&Strm, 1, "Unexpected character");
2123 break;
2124
2125 case '#':
2126 {
2127 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
2128 if (ch == ~(unsigned)0)
2129 rcExit = RTEXITCODE_FAILURE;
2130 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
2131 rcExit = parsePragma(&Strm);
2132 else
2133 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
2134 break;
2135 }
2136
2137 default:
2138 rcExit = parseError(&Strm, 1, "Unexpected character");
2139 break;
2140 }
2141 if (rcExit != RTEXITCODE_SUCCESS)
2142 return rcExit;
2143 }
2144
2145 ScmStreamDelete(&Strm);
2146 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
2147 RTMsgInfo("Successfully parsed '%s'.", pszScript);
2148 return rcExit;
2149}
2150
2151
2152/**
2153 * Parses the arguments.
2154 */
2155static RTEXITCODE parseArguments(int argc, char **argv)
2156{
2157 /*
2158 * Set / Adjust defaults.
2159 */
2160 int rc = RTPathAbs(g_pszAssemblerIncVal, g_szAssemblerIncVal, sizeof(g_szAssemblerIncVal) - 1);
2161 if (RT_FAILURE(rc))
2162 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed: %Rrc", rc);
2163 strcat(g_szAssemblerIncVal, "/");
2164 g_pszAssemblerIncVal = g_szAssemblerIncVal;
2165
2166 /*
2167 * Option config.
2168 */
2169 enum
2170 {
2171 kVBoxTpGOpt_32Bit = 1000,
2172 kVBoxTpGOpt_64Bit,
2173 kVBoxTpGOpt_GenerateWrapperHeader,
2174 kVBoxTpGOpt_Assembler,
2175 kVBoxTpGOpt_AssemblerFmtOpt,
2176 kVBoxTpGOpt_AssemblerFmtVal,
2177 kVBoxTpGOpt_AssemblerOutputOpt,
2178 kVBoxTpGOpt_AssemblerOption,
2179 kVBoxTpGOpt_Pic,
2180 kVBoxTpGOpt_ProbeFnName,
2181 kVBoxTpGOpt_ProbeFnImported,
2182 kVBoxTpGOpt_ProbeFnNotImported,
2183 kVBoxTpGOpt_Host32Bit,
2184 kVBoxTpGOpt_Host64Bit,
2185 kVBoxTpGOpt_RawModeContext,
2186 kVBoxTpGOpt_Ring0Context,
2187 kVBoxTpGOpt_Ring3Context,
2188 kVBoxTpGOpt_End
2189 };
2190
2191 static RTGETOPTDEF const s_aOpts[] =
2192 {
2193 /* dtrace w/ long options */
2194 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
2195 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
2196 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
2197 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
2198 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
2199 { "--output", 'o', RTGETOPT_REQ_STRING },
2200 { "--script", 's', RTGETOPT_REQ_STRING },
2201 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2202 /* our stuff */
2203 { "--generate-wrapper-header", kVBoxTpGOpt_GenerateWrapperHeader, RTGETOPT_REQ_NOTHING },
2204 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
2205 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
2206 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
2207 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
2208 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
2209 { "--pic", kVBoxTpGOpt_Pic, RTGETOPT_REQ_NOTHING },
2210 { "--probe-fn-name", kVBoxTpGOpt_ProbeFnName, RTGETOPT_REQ_STRING },
2211 { "--probe-fn-imported", kVBoxTpGOpt_ProbeFnImported, RTGETOPT_REQ_NOTHING },
2212 { "--probe-fn-not-imported", kVBoxTpGOpt_ProbeFnNotImported, RTGETOPT_REQ_NOTHING },
2213 { "--host-32-bit", kVBoxTpGOpt_Host32Bit, RTGETOPT_REQ_NOTHING },
2214 { "--host-64-bit", kVBoxTpGOpt_Host64Bit, RTGETOPT_REQ_NOTHING },
2215 { "--raw-mode-context", kVBoxTpGOpt_RawModeContext, RTGETOPT_REQ_NOTHING },
2216 { "--ring-0-context", kVBoxTpGOpt_Ring0Context, RTGETOPT_REQ_NOTHING },
2217 { "--ring-3-context", kVBoxTpGOpt_Ring3Context, RTGETOPT_REQ_NOTHING },
2218 /** @todo We're missing a bunch of assembler options! */
2219 };
2220
2221 RTGETOPTUNION ValueUnion;
2222 RTGETOPTSTATE GetOptState;
2223 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2224 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2225
2226 /*
2227 * Process the options.
2228 */
2229 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2230 {
2231 switch (rc)
2232 {
2233 /*
2234 * DTrace compatible options.
2235 */
2236 case kVBoxTpGOpt_32Bit:
2237 g_cHostBits = g_cBits = 32;
2238 g_pszAssemblerFmtVal = g_szAssemblerFmtVal32;
2239 break;
2240
2241 case kVBoxTpGOpt_64Bit:
2242 g_cHostBits = g_cBits = 64;
2243 g_pszAssemblerFmtVal = g_szAssemblerFmtVal64;
2244 break;
2245
2246 case 'C':
2247 g_fApplyCpp = true;
2248 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
2249 break;
2250
2251 case 'G':
2252 if ( g_enmAction != kVBoxTpGAction_Nothing
2253 && g_enmAction != kVBoxTpGAction_GenerateObject)
2254 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G does not mix with -h or --generate-wrapper-header");
2255 g_enmAction = kVBoxTpGAction_GenerateObject;
2256 break;
2257
2258 case 'h':
2259 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
2260 {
2261 if ( g_enmAction != kVBoxTpGAction_Nothing
2262 && g_enmAction != kVBoxTpGAction_GenerateHeader)
2263 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h does not mix with -G or --generate-wrapper-header");
2264 g_enmAction = kVBoxTpGAction_GenerateHeader;
2265 }
2266 else
2267 {
2268 /* --help or similar */
2269 RTPrintf("VirtualBox Tracepoint Generator\n"
2270 "\n"
2271 "Usage: %s [options]\n"
2272 "\n"
2273 "Options:\n", RTProcShortName());
2274 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
2275 if ((unsigned)s_aOpts[i].iShort < 128)
2276 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
2277 else
2278 RTPrintf(" %s\n", s_aOpts[i].pszLong);
2279 return RTEXITCODE_SUCCESS;
2280 }
2281 break;
2282
2283 case 'o':
2284 if (g_pszOutput)
2285 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
2286 g_pszOutput = ValueUnion.psz;
2287 break;
2288
2289 case 's':
2290 if (g_pszScript)
2291 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
2292 g_pszScript = ValueUnion.psz;
2293 break;
2294
2295 case 'v':
2296 g_cVerbosity++;
2297 break;
2298
2299 case 'V':
2300 {
2301 /* The following is assuming that svn does it's job here. */
2302 static const char s_szRev[] = "$Revision: 41343 $";
2303 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2304 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2305 return RTEXITCODE_SUCCESS;
2306 }
2307
2308 case VINF_GETOPT_NOT_OPTION:
2309 if (g_enmAction == kVBoxTpGAction_GenerateObject)
2310 break; /* object files, ignore them. */
2311 return RTGetOptPrintError(rc, &ValueUnion);
2312
2313
2314 /*
2315 * Our options.
2316 */
2317 case kVBoxTpGOpt_GenerateWrapperHeader:
2318 if ( g_enmAction != kVBoxTpGAction_Nothing
2319 && g_enmAction != kVBoxTpGAction_GenerateWrapperHeader)
2320 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-wrapper-header does not mix with -h or -G");
2321 g_enmAction = kVBoxTpGAction_GenerateWrapperHeader;
2322 break;
2323
2324 case kVBoxTpGOpt_Assembler:
2325 g_pszAssembler = ValueUnion.psz;
2326 break;
2327
2328 case kVBoxTpGOpt_AssemblerFmtOpt:
2329 g_pszAssemblerFmtOpt = ValueUnion.psz;
2330 break;
2331
2332 case kVBoxTpGOpt_AssemblerFmtVal:
2333 g_pszAssemblerFmtVal = ValueUnion.psz;
2334 break;
2335
2336 case kVBoxTpGOpt_AssemblerOutputOpt:
2337 g_pszAssemblerOutputOpt = ValueUnion.psz;
2338 break;
2339
2340 case kVBoxTpGOpt_AssemblerOption:
2341 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
2342 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
2343 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
2344 g_cAssemblerOptions++;
2345 break;
2346
2347 case kVBoxTpGOpt_Pic:
2348 g_fPic = true;
2349 break;
2350
2351 case kVBoxTpGOpt_ProbeFnName:
2352 g_pszProbeFnName = ValueUnion.psz;
2353 break;
2354
2355 case kVBoxTpGOpt_ProbeFnImported:
2356 g_fProbeFnImported = true;
2357 break;
2358
2359 case kVBoxTpGOpt_ProbeFnNotImported:
2360 g_fProbeFnImported = false;
2361 break;
2362
2363 case kVBoxTpGOpt_Host32Bit:
2364 g_cHostBits = 32;
2365 break;
2366
2367 case kVBoxTpGOpt_Host64Bit:
2368 g_cHostBits = 64;
2369 break;
2370
2371 case kVBoxTpGOpt_RawModeContext:
2372 g_fTypeContext = VTG_TYPE_CTX_RC;
2373 g_pszContextDefine = "IN_RC";
2374 break;
2375
2376 case kVBoxTpGOpt_Ring0Context:
2377 g_fTypeContext = VTG_TYPE_CTX_R0;
2378 g_pszContextDefine = "IN_RING0";
2379 break;
2380
2381 case kVBoxTpGOpt_Ring3Context:
2382 g_fTypeContext = VTG_TYPE_CTX_R3;
2383 g_pszContextDefine = "IN_RING3";
2384 break;
2385
2386
2387 /*
2388 * Errors and bugs.
2389 */
2390 default:
2391 return RTGetOptPrintError(rc, &ValueUnion);
2392 }
2393 }
2394
2395 /*
2396 * Check that we've got all we need.
2397 */
2398 if (g_enmAction == kVBoxTpGAction_Nothing)
2399 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h, -G or --generate-wrapper-header)");
2400 if (!g_pszScript)
2401 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
2402 if (!g_pszOutput)
2403 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
2404
2405 return RTEXITCODE_SUCCESS;
2406}
2407
2408
2409int main(int argc, char **argv)
2410{
2411 int rc = RTR3InitExe(argc, &argv, 0);
2412 if (RT_FAILURE(rc))
2413 return 1;
2414
2415 RTEXITCODE rcExit = parseArguments(argc, argv);
2416 if (rcExit == RTEXITCODE_SUCCESS)
2417 {
2418 /*
2419 * Parse the script.
2420 */
2421 RTListInit(&g_ProviderHead);
2422 rcExit = parseScript(g_pszScript);
2423 if (rcExit == RTEXITCODE_SUCCESS)
2424 {
2425 /*
2426 * Take action.
2427 */
2428 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
2429 rcExit = generateFile(g_pszOutput, "header", generateHeader);
2430 else if (g_enmAction == kVBoxTpGAction_GenerateWrapperHeader)
2431 rcExit = generateFile(g_pszOutput, "wrapper header", generateWrapperHeader);
2432 else
2433 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
2434 }
2435 }
2436
2437 if (rcExit == RTEXITCODE_SUCCESS && g_cTypeErrors > 0)
2438 rcExit = RTEXITCODE_FAILURE;
2439 return rcExit;
2440}
2441
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette