VirtualBox

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

Last change on this file was 103275, checked in by vboxsync, 3 months ago

Backed out r161549 again (not wanted / bloat).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use