VirtualBox

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

Last change on this file since 91446 was 88956, checked in by vboxsync, 4 years ago

VBoxTpG,VBoxDD.d: Better warning diagnostic messages. Fixed warnings in VBoxDD.d

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