VirtualBox

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

Last change on this file since 59410 was 58997, checked in by vboxsync, 9 years ago

VBoxTpG: Add stub macros for the two new ones. Make the indicator variables 'const volatile' as they can be changed by dtrace at any time but should never be changed by the application.

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

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