VirtualBox

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

Last change on this file was 104620, checked in by vboxsync, 4 months ago

bldprogs/VBoxTpG.cpp: Remove unread variable, bugref:3409

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.3 KB
Line 
1/* $Id: VBoxTpG.cpp 104620 2024-05-14 06:56:35Z 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 104620 2024-05-14 06:56:35Z 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 104620 2024-05-14 06:56:35Z 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 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1091 {
1092 if ((pArg->fType & (VTG_TYPE_FIXED_SIZED | VTG_TYPE_AUTO_CONV_PTR)) == VTG_TYPE_FIXED_SIZED)
1093 ScmStreamPrintf(pStrm,
1094 " AssertCompile(sizeof(%s) == %u); \\\n"
1095 " AssertCompile(sizeof(%s) <= %u); \\\n",
1096 pArg->pszTracerType, pArg->fType & VTG_TYPE_SIZE_MASK,
1097 pArg->pszName, pArg->fType & VTG_TYPE_SIZE_MASK);
1098 else if (pArg->fType & (VTG_TYPE_POINTER | VTG_TYPE_HC_ARCH_SIZED))
1099 ScmStreamPrintf(pStrm,
1100 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
1101 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
1102 pArg->pszName,
1103 pArg->pszTracerType);
1104 }
1105 ScmStreamPrintf(pStrm,
1106 " } \\\n"
1107 " } while (0)\n"
1108 "\n");
1109 }
1110 }
1111
1112 ScmStreamPrintf(pStrm,
1113 "\n"
1114 "#else\n"
1115 "\n");
1116 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1117 {
1118 if (g_fTypeContext != VTG_TYPE_CTX_R3)
1119 {
1120 generateProviderDefineName(szTmp, sizeof(szTmp), pProv->pszName);
1121 ScmStreamPrintf(pStrm,
1122 "# define %s_ANY_PROBES_ENABLED() (false)\n"
1123 "# define %s_GET_SETTINGS_SEQ_NO() UINT32_C(0)\n"
1124 "\n",
1125 szTmp, szTmp);
1126 }
1127
1128 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1129 {
1130 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1131 ScmStreamPrintf(pStrm,
1132 "# define %s_ENABLED() (false)\n"
1133 "# define %s_ENABLED_RAW() UINT32_C(0)\n"
1134 "# define %s("
1135 , szTmp, szTmp, szTmp);
1136 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1137 {
1138 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1139 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1140 else
1141 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1142 }
1143 ScmStreamPrintf(pStrm,
1144 ") do { } while (0)\n");
1145 }
1146 }
1147
1148 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1149 "#endif\n"
1150 "\n"
1151 "RT_C_DECLS_END\n"
1152 "#endif\n"));
1153 return RTEXITCODE_SUCCESS;
1154}
1155
1156
1157/**
1158 * Called via generateFile to generate the wrapper header file.
1159 *
1160 * @returns Exit code status.
1161 * @param pStrm The output stream.
1162 */
1163static RTEXITCODE generateWrapperHeader(PSCMSTREAM pStrm)
1164{
1165 /*
1166 * Calc the double inclusion blocker define and then write the file header.
1167 */
1168 char szTmp[4096];
1169 const char *pszName = RTPathFilename(g_pszScript);
1170 size_t cchName = strlen(pszName);
1171 if (cchName >= sizeof(szTmp) - 64)
1172 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
1173 szTmp[0] = '_';
1174 szTmp[1] = '_';
1175 szTmp[2] = '_';
1176 memcpy(&szTmp[3], pszName, cchName);
1177 strcpy(&szTmp[3 + cchName ], "___WRAPPER___");
1178 char *psz = &szTmp[3];
1179 while (*psz)
1180 {
1181 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
1182 *psz = '_';
1183 psz++;
1184 }
1185
1186 ScmStreamPrintf(pStrm,
1187 "/* $Id: VBoxTpG.cpp 104620 2024-05-14 06:56:35Z vboxsync $ */\n"
1188 "/** @file\n"
1189 " * Automatically generated from %s. Do NOT edit!\n"
1190 " */\n"
1191 "\n"
1192 "#ifndef %s\n"
1193 "#define %s\n"
1194 "\n"
1195 "#include <VBox/VBoxTpG.h>\n"
1196 "\n"
1197 "#ifndef %s\n"
1198 "# error \"Expected '%s' to be defined\"\n"
1199 "#endif\n"
1200 "\n"
1201 "#ifdef VBOX_WITH_DTRACE\n"
1202 "\n"
1203 ,
1204 g_pszScript,
1205 szTmp,
1206 szTmp,
1207 g_pszContextDefine,
1208 g_pszContextDefine);
1209
1210 /*
1211 * Declare macros for each probe.
1212 */
1213 PVTGPROVIDER pProv;
1214 PVTGPROBE pProbe;
1215 PVTGARG pArg;
1216 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1217 {
1218 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1219 {
1220 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1221 ScmStreamPrintf(pStrm,
1222 "# define %s("
1223 , szTmp);
1224 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1225 {
1226 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1227 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1228 else
1229 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1230 }
1231 ScmStreamPrintf(pStrm,
1232 ") \\\n"
1233 " do { \\\n"
1234 " if (RT_UNLIKELY(%s_ENABLED())) \\\n"
1235 " { \\\n"
1236 " %s_ORIGINAL("
1237 , szTmp, szTmp);
1238 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1239 {
1240 const char *pszFmt = pArg->pszArgPassingFmt;
1241 if (pArg->fType & VTG_TYPE_AUTO_CONV_PTR)
1242 {
1243 /* Casting is required. ASSUMES sizeof(RTR0PTR) == sizeof(RTR3PTR) - safe! */
1244 pszFmt += sizeof(", ") - 1;
1245 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1246 ScmStreamPrintf(pStrm, "(%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1247 else
1248 ScmStreamPrintf(pStrm, ", (%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1249 }
1250 else if (pArg->fType & VTG_TYPE_CONST_CHAR_PTR)
1251 {
1252 /* Casting from 'const char *' (probe) to 'char *' (dtrace) is required to shut up warnings. */
1253 pszFmt += sizeof(", ") - 1;
1254 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1255 ScmStreamPrintf(pStrm, "(char *)%M", pszFmt, pArg->pszName);
1256 else
1257 ScmStreamPrintf(pStrm, ", (char *)%M", pszFmt, pArg->pszName);
1258 }
1259 else
1260 {
1261 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1262 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt + sizeof(", ") - 1, pArg->pszName);
1263 else
1264 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName);
1265 }
1266 }
1267 ScmStreamPrintf(pStrm,
1268 "); \\\n"
1269 " } \\\n"
1270 " } while (0)\n"
1271 "\n");
1272 }
1273 }
1274
1275 ScmStreamPrintf(pStrm,
1276 "\n"
1277 "#else\n"
1278 "\n");
1279 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1280 {
1281 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1282 {
1283 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1284 ScmStreamPrintf(pStrm,
1285 "# define %s("
1286 , szTmp);
1287 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1288 {
1289 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1290 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1291 else
1292 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1293 }
1294 ScmStreamPrintf(pStrm,
1295 ") do { } while (0)\n");
1296 }
1297 }
1298
1299 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1300 "#endif\n"
1301 "\n"
1302 "#endif\n"));
1303 return RTEXITCODE_SUCCESS;
1304}
1305
1306
1307/**
1308 * Parser error with line and position.
1309 *
1310 * @returns RTEXITCODE_FAILURE.
1311 * @param pStrm The stream.
1312 * @param fAbs Absolute or relative offset.
1313 * @param offSeek When @a fAbs is @c false, the offset from the current
1314 * position to the point of failure. When @a fAbs is @c
1315 * true, it's the absolute position.
1316 * @param pszFormat The message format string.
1317 * @param va Format arguments.
1318 */
1319static RTEXITCODE parseErrorExV(PSCMSTREAM pStrm, bool fAbs, size_t offSeek, const char *pszFormat, va_list va)
1320{
1321 if (fAbs)
1322 ScmStreamSeekAbsolute(pStrm, offSeek);
1323 else if (offSeek != 0)
1324 ScmStreamSeekRelative(pStrm, -(ssize_t)offSeek);
1325 size_t const off = ScmStreamTell(pStrm);
1326 size_t const iLine = ScmStreamTellLine(pStrm);
1327 ScmStreamSeekByLine(pStrm, iLine);
1328 size_t const offLine = ScmStreamTell(pStrm);
1329
1330 va_list va2;
1331 va_copy(va2, va);
1332 RTPrintf("%s:%d:%zd: error: %N.\n", g_pszScript, iLine + 1, off - offLine + 1, pszFormat, &va2);
1333 va_end(va2);
1334
1335 size_t cchLine;
1336 SCMEOL enmEof;
1337 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
1338 if (pszLine)
1339 RTPrintf(" %.*s\n"
1340 " %*s^\n",
1341 cchLine, pszLine, off - offLine, "");
1342 return RTEXITCODE_FAILURE;
1343}
1344
1345
1346/**
1347 * Parser error with line and position.
1348 *
1349 * @returns RTEXITCODE_FAILURE.
1350 * @param pStrm The stream.
1351 * @param off The offset from the current position to the point of
1352 * failure.
1353 * @param pszFormat The message format string.
1354 * @param ... Format arguments.
1355 */
1356static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...)
1357{
1358 va_list va;
1359 va_start(va, pszFormat);
1360 RTEXITCODE rcExit = parseErrorExV(pStrm, false, off, pszFormat, va);
1361 va_end(va);
1362 return rcExit;
1363}
1364
1365
1366/**
1367 * Parser error with line and position, absolute version.
1368 *
1369 * @returns RTEXITCODE_FAILURE.
1370 * @param pStrm The stream.
1371 * @param off The offset from the current position to the point of
1372 * failure.
1373 * @param pszFormat The message format string.
1374 * @param ... Format arguments.
1375 */
1376static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...)
1377{
1378 va_list va;
1379 va_start(va, pszFormat);
1380 RTEXITCODE rcExit = parseErrorExV(pStrm, true /*fAbs*/, off, pszFormat, va);
1381 va_end(va);
1382 return rcExit;
1383}
1384
1385
1386/**
1387 * Parser warning with line and position.
1388 *
1389 * @param pStrm The stream.
1390 * @param fAbs Absolute or relative offset.
1391 * @param offSeek When @a fAbs is @c false, the offset from the current
1392 * position to the point of failure. When @a fAbs is @c
1393 * true, it's the absolute position.
1394 * @param pszFormat The message format string.
1395 * @param va Format arguments.
1396 */
1397static void parseWarnExV(PSCMSTREAM pStrm, bool fAbs, size_t offSeek, const char *pszFormat, va_list va)
1398{
1399 /* Save the stream position. */
1400 size_t const offOrg = ScmStreamTell(pStrm);
1401
1402 if (fAbs)
1403 ScmStreamSeekAbsolute(pStrm, offSeek);
1404 else if (offSeek != 0)
1405 ScmStreamSeekRelative(pStrm, -(ssize_t)offSeek);
1406 size_t const off = ScmStreamTell(pStrm);
1407 size_t const iLine = ScmStreamTellLine(pStrm);
1408 ScmStreamSeekByLine(pStrm, iLine);
1409 size_t const offLine = ScmStreamTell(pStrm);
1410
1411 va_list va2;
1412 va_copy(va2, va);
1413 RTPrintf("%s:%d:%zd: warning: %N.\n", g_pszScript, iLine + 1, off - offLine + 1, pszFormat, &va2);
1414 va_end(va2);
1415
1416 size_t cchLine;
1417 SCMEOL enmEof;
1418 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
1419 if (pszLine)
1420 RTPrintf(" %.*s\n"
1421 " %*s^\n",
1422 cchLine, pszLine, off - offLine, "");
1423
1424 /* restore the position. */
1425 int rc = ScmStreamSeekAbsolute(pStrm, offOrg);
1426 AssertRC(rc);
1427}
1428
1429#if 0 /* unused */
1430/**
1431 * Parser warning with line and position.
1432 *
1433 * @param pStrm The stream.
1434 * @param off The offset from the current position to the point of
1435 * failure.
1436 * @param pszFormat The message format string.
1437 * @param ... Format arguments.
1438 */
1439static void parseWarn(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...)
1440{
1441 va_list va;
1442 va_start(va, pszFormat);
1443 parseWarnExV(pStrm, false, off, pszFormat, va);
1444 va_end(va);
1445}
1446#endif /* unused */
1447
1448/**
1449 * Parser warning with line and position, absolute version.
1450 *
1451 * @param pStrm The stream.
1452 * @param off The offset from the current position to the point of
1453 * failure.
1454 * @param pszFormat The message format string.
1455 * @param ... Format arguments.
1456 */
1457static void parseWarnAbs(PSCMSTREAM pStrm, size_t off, const char *pszFormat, ...)
1458{
1459 va_list va;
1460 va_start(va, pszFormat);
1461 parseWarnExV(pStrm, true /*fAbs*/, off, pszFormat, va);
1462 va_end(va);
1463}
1464
1465
1466/**
1467 * Handles a C++ one line comment.
1468 *
1469 * @returns Exit code.
1470 * @param pStrm The stream.
1471 */
1472static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
1473{
1474 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
1475 return RTEXITCODE_SUCCESS;
1476}
1477
1478
1479/**
1480 * Handles a multi-line C/C++ comment.
1481 *
1482 * @returns Exit code.
1483 * @param pStrm The stream.
1484 */
1485static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
1486{
1487 unsigned ch;
1488 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1489 {
1490 if (ch == '*')
1491 {
1492 do
1493 ch = ScmStreamGetCh(pStrm);
1494 while (ch == '*');
1495 if (ch == '/')
1496 return RTEXITCODE_SUCCESS;
1497 }
1498 }
1499
1500 parseError(pStrm, 1, "Expected end of comment, got end of file");
1501 return RTEXITCODE_FAILURE;
1502}
1503
1504
1505/**
1506 * Skips spaces and comments.
1507 *
1508 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
1509 * @param pStrm The stream..
1510 */
1511static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
1512{
1513 unsigned ch;
1514 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
1515 {
1516 if (!RT_C_IS_SPACE(ch) && ch != '/')
1517 return RTEXITCODE_SUCCESS;
1518 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
1519 if (ch == '/')
1520 {
1521 ch = ScmStreamGetCh(pStrm);
1522 RTEXITCODE rcExit;
1523 if (ch == '*')
1524 rcExit = parseMultiLineComment(pStrm);
1525 else if (ch == '/')
1526 rcExit = parseOneLineComment(pStrm);
1527 else
1528 rcExit = parseError(pStrm, 2, "Unexpected character");
1529 if (rcExit != RTEXITCODE_SUCCESS)
1530 return rcExit;
1531 }
1532 }
1533
1534 return parseError(pStrm, 0, "Unexpected end of file");
1535}
1536
1537
1538/**
1539 * Skips spaces and comments, returning the next character.
1540 *
1541 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
1542 * failure.
1543 * @param pStrm The stream.
1544 */
1545static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
1546{
1547 unsigned ch;
1548 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1549 {
1550 if (!RT_C_IS_SPACE(ch) && ch != '/')
1551 return ch;
1552 if (ch == '/')
1553 {
1554 ch = ScmStreamGetCh(pStrm);
1555 RTEXITCODE rcExit;
1556 if (ch == '*')
1557 rcExit = parseMultiLineComment(pStrm);
1558 else if (ch == '/')
1559 rcExit = parseOneLineComment(pStrm);
1560 else
1561 rcExit = parseError(pStrm, 2, "Unexpected character");
1562 if (rcExit != RTEXITCODE_SUCCESS)
1563 return ~(unsigned)0;
1564 }
1565 }
1566
1567 parseError(pStrm, 0, "Unexpected end of file");
1568 return ~(unsigned)0;
1569}
1570
1571
1572/**
1573 * Get the next non-space-non-comment character on a preprocessor line.
1574 *
1575 * @returns The next character. On error message and ~(unsigned)0.
1576 * @param pStrm The stream.
1577 */
1578static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
1579{
1580 size_t off = ScmStreamTell(pStrm) - 1;
1581 unsigned ch;
1582 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1583 {
1584 if (RT_C_IS_SPACE(ch))
1585 {
1586 if (ch == '\n' || ch == '\r')
1587 {
1588 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
1589 break;
1590 }
1591 }
1592 else if (ch == '\\')
1593 {
1594 size_t off2 = ScmStreamTell(pStrm) - 1;
1595 ch = ScmStreamGetCh(pStrm);
1596 if (ch == '\r')
1597 ch = ScmStreamGetCh(pStrm);
1598 if (ch != '\n')
1599 {
1600 parseErrorAbs(pStrm, off2, "Expected new line");
1601 break;
1602 }
1603 }
1604 else
1605 return ch;
1606 }
1607 return ~(unsigned)0;
1608}
1609
1610
1611
1612/**
1613 * Skips spaces and comments.
1614 *
1615 * @returns Same as ScmStreamCGetWord
1616 * @param pStrm The stream..
1617 * @param pcchWord Where to return the length.
1618 */
1619static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
1620{
1621 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
1622 return NULL;
1623 return ScmStreamCGetWord(pStrm, pcchWord);
1624}
1625
1626
1627
1628/**
1629 * Parses interface stability.
1630 *
1631 * @returns Interface stability if parsed correctly, otherwise error message and
1632 * kVTGStability_Invalid.
1633 * @param pStrm The stream.
1634 * @param ch The first character in the stability spec.
1635 */
1636static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
1637{
1638 switch (ch)
1639 {
1640 case 'E':
1641 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
1642 return kVTGStability_External;
1643 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
1644 return kVTGStability_Evolving;
1645 break;
1646 case 'I':
1647 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
1648 return kVTGStability_Internal;
1649 break;
1650 case 'O':
1651 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
1652 return kVTGStability_Obsolete;
1653 break;
1654 case 'P':
1655 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
1656 return kVTGStability_Private;
1657 break;
1658 case 'S':
1659 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
1660 return kVTGStability_Stable;
1661 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
1662 return kVTGStability_Standard;
1663 break;
1664 case 'U':
1665 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
1666 return kVTGStability_Unstable;
1667 break;
1668 }
1669 parseError(pStrm, 1, "Unknown stability specifier");
1670 return kVTGStability_Invalid;
1671}
1672
1673
1674/**
1675 * Parses data depndency class.
1676 *
1677 * @returns Data dependency class if parsed correctly, otherwise error message
1678 * and kVTGClass_Invalid.
1679 * @param pStrm The stream.
1680 * @param ch The first character in the stability spec.
1681 */
1682static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
1683{
1684 switch (ch)
1685 {
1686 case 'C':
1687 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
1688 return kVTGClass_Common;
1689 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
1690 return kVTGClass_Cpu;
1691 break;
1692 case 'G':
1693 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
1694 return kVTGClass_Group;
1695 break;
1696 case 'I':
1697 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
1698 return kVTGClass_Isa;
1699 break;
1700 case 'P':
1701 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
1702 return kVTGClass_Platform;
1703 break;
1704 case 'U':
1705 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
1706 return kVTGClass_Unknown;
1707 break;
1708 }
1709 parseError(pStrm, 1, "Unknown data dependency class specifier");
1710 return kVTGClass_Invalid;
1711}
1712
1713/**
1714 * Parses a pragma D attributes statement.
1715 *
1716 * @returns Suitable exit code, errors message already written on failure.
1717 * @param pStrm The stream.
1718 */
1719static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
1720{
1721 /*
1722 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
1723 */
1724 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1725 if (ch == ~(unsigned)0)
1726 return RTEXITCODE_FAILURE;
1727
1728 kVTGStability enmCode = parseStability(pStrm, ch);
1729 if (enmCode == kVTGStability_Invalid)
1730 return RTEXITCODE_FAILURE;
1731 ch = ScmStreamGetCh(pStrm);
1732 if (ch != '/')
1733 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
1734
1735 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
1736 if (enmData == kVTGStability_Invalid)
1737 return RTEXITCODE_FAILURE;
1738 ch = ScmStreamGetCh(pStrm);
1739 if (ch != '/')
1740 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
1741
1742 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
1743 if (enmDataDep == kVTGClass_Invalid)
1744 return RTEXITCODE_FAILURE;
1745
1746 /*
1747 * Expecting 'provider' followed by the name of an provider defined earlier.
1748 */
1749 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1750 if (ch == ~(unsigned)0)
1751 return RTEXITCODE_FAILURE;
1752 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
1753 return parseError(pStrm, 1, "Expected 'provider'");
1754
1755 size_t cchName;
1756 const char *pszName = parseGetNextCWord(pStrm, &cchName);
1757 if (!pszName)
1758 return parseError(pStrm, 1, "Expected provider name");
1759
1760 PVTGPROVIDER pProv;
1761 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1762 {
1763 if ( !strncmp(pProv->pszName, pszName, cchName)
1764 && pProv->pszName[cchName] == '\0')
1765 break;
1766 }
1767 if (RTListNodeIsDummy(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry))
1768 return parseError(pStrm, cchName, "Provider not found");
1769
1770 /*
1771 * Which aspect of the provider?
1772 */
1773 size_t cchAspect;
1774 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
1775 if (!pszAspect)
1776 return parseError(pStrm, 1, "Expected provider aspect");
1777
1778 PVTGATTRS pAttrs;
1779 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
1780 pAttrs = &pProv->AttrSelf;
1781 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
1782 pAttrs = &pProv->AttrFunctions;
1783 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
1784 pAttrs = &pProv->AttrModules;
1785 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
1786 pAttrs = &pProv->AttrName;
1787 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
1788 pAttrs = &pProv->AttrArguments;
1789 else
1790 return parseError(pStrm, cchAspect, "Unknown aspect");
1791
1792 if (pAttrs->enmCode != kVTGStability_Invalid)
1793 return parseError(pStrm, cchAspect, "You have already specified these attributes");
1794
1795 pAttrs->enmCode = enmCode;
1796 pAttrs->enmData = enmData;
1797 pAttrs->enmDataDep = enmDataDep;
1798 return RTEXITCODE_SUCCESS;
1799}
1800
1801/**
1802 * Parses a D pragma statement.
1803 *
1804 * @returns Suitable exit code, errors message already written on failure.
1805 * @param pStrm The stream.
1806 */
1807static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
1808{
1809 RTEXITCODE rcExit;
1810 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1811 if (ch == ~(unsigned)0)
1812 rcExit = RTEXITCODE_FAILURE;
1813 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
1814 {
1815 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1816 if (ch == ~(unsigned)0)
1817 rcExit = RTEXITCODE_FAILURE;
1818 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
1819 rcExit = parsePragmaDAttributes(pStrm);
1820 else
1821 rcExit = parseError(pStrm, 1, "Unknown pragma D");
1822 }
1823 else
1824 rcExit = parseError(pStrm, 1, "Unknown pragma");
1825 return rcExit;
1826}
1827
1828
1829/**
1830 * Classifies the given type expression.
1831 *
1832 * @return Type flags.
1833 * @param pszType The type expression.
1834 * @param pStrm The input stream (for errors + warnings).
1835 * @param offSrc The absolute source position of this expression (for
1836 * warnings).
1837 */
1838static uint32_t parseTypeExpression(const char *pszType, PSCMSTREAM pStrm, size_t offSrc)
1839{
1840 size_t cchType = strlen(pszType);
1841#define MY_STRMATCH(a_sz) (cchType == sizeof(a_sz) - 1 && !memcmp(a_sz, pszType, sizeof(a_sz) - 1))
1842
1843 /*
1844 * Try detect pointers.
1845 */
1846 if (pszType[cchType - 1] == '*')
1847 {
1848 if (MY_STRMATCH("const char *")) return VTG_TYPE_POINTER | VTG_TYPE_CONST_CHAR_PTR;
1849 return VTG_TYPE_POINTER;
1850 }
1851 if (pszType[cchType - 1] == '&')
1852 {
1853 parseWarnAbs(pStrm, offSrc, "Please avoid using references like '%s' for probe arguments!", pszType);
1854 return VTG_TYPE_POINTER;
1855 }
1856
1857 /*
1858 * Standard integer types and IPRT variants.
1859 * It's important that we catch all types larger than 32-bit here or we'll
1860 * screw up the probe argument handling.
1861 */
1862 if (MY_STRMATCH("int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1863 if (MY_STRMATCH("uintptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_UNSIGNED;
1864 if (MY_STRMATCH("intptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_SIGNED;
1865
1866 //if (MY_STRMATCH("uint128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint128_t) | VTG_TYPE_UNSIGNED;
1867 if (MY_STRMATCH("uint64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1868 if (MY_STRMATCH("uint32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1869 if (MY_STRMATCH("uint16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1870 if (MY_STRMATCH("uint8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint8_t) | VTG_TYPE_UNSIGNED;
1871
1872 //if (MY_STRMATCH("int128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int128_t) | VTG_TYPE_SIGNED;
1873 if (MY_STRMATCH("int64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int64_t) | VTG_TYPE_SIGNED;
1874 if (MY_STRMATCH("int32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int32_t) | VTG_TYPE_SIGNED;
1875 if (MY_STRMATCH("int16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int16_t) | VTG_TYPE_SIGNED;
1876 if (MY_STRMATCH("int8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int8_t) | VTG_TYPE_SIGNED;
1877
1878 if (MY_STRMATCH("RTUINT64U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1879 if (MY_STRMATCH("RTUINT32U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1880 if (MY_STRMATCH("RTUINT16U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1881
1882 if (MY_STRMATCH("RTMSINTERVAL")) return VTG_TYPE_FIXED_SIZED | sizeof(RTMSINTERVAL) | VTG_TYPE_UNSIGNED;
1883 if (MY_STRMATCH("RTTIMESPEC")) return VTG_TYPE_FIXED_SIZED | sizeof(RTTIMESPEC) | VTG_TYPE_SIGNED;
1884 if (MY_STRMATCH("RTPROCESS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTPROCESS) | VTG_TYPE_UNSIGNED;
1885 if (MY_STRMATCH("RTHCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTHCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS;
1886
1887 if (MY_STRMATCH("RTR3PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3;
1888 if (MY_STRMATCH("RTR0PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0;
1889 if (MY_STRMATCH("RTRCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC;
1890 if (MY_STRMATCH("RTHCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0;
1891
1892 if (MY_STRMATCH("RTR3UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1893 if (MY_STRMATCH("RTR0UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1894 if (MY_STRMATCH("RTRCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1895 if (MY_STRMATCH("RTHCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1896
1897 if (MY_STRMATCH("RTR3INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_SIGNED;
1898 if (MY_STRMATCH("RTR0INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1899 if (MY_STRMATCH("RTRCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1900 if (MY_STRMATCH("RTHCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1901
1902 if (MY_STRMATCH("RTUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1903 if (MY_STRMATCH("RTINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1904
1905 if (MY_STRMATCH("RTHCUINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1906 if (MY_STRMATCH("RTR3UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1907 if (MY_STRMATCH("RTR0UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1908
1909 if (MY_STRMATCH("RTGCUINTREG")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTREG) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1910 if (MY_STRMATCH("RTGCPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1911 if (MY_STRMATCH("RTGCINTPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTPTR) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1912 if (MY_STRMATCH("RTGCPTR32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR32) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1913 if (MY_STRMATCH("RTGCPTR64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR64) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1914 if (MY_STRMATCH("RTGCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1915 if (MY_STRMATCH("RTGCPHYS32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS32) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1916 if (MY_STRMATCH("RTGCPHYS64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS64) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1917
1918 /*
1919 * The special VBox types.
1920 */
1921 if (MY_STRMATCH("PVM")) return VTG_TYPE_POINTER;
1922 if (MY_STRMATCH("PVMCPU")) return VTG_TYPE_POINTER;
1923 if (MY_STRMATCH("PCPUMCTX")) return VTG_TYPE_POINTER;
1924
1925 /*
1926 * Preaching time.
1927 */
1928 if ( MY_STRMATCH("unsigned long")
1929 || MY_STRMATCH("unsigned long long")
1930 || MY_STRMATCH("signed long")
1931 || MY_STRMATCH("signed long long")
1932 || MY_STRMATCH("long")
1933 || MY_STRMATCH("long long")
1934 || MY_STRMATCH("char")
1935 || MY_STRMATCH("signed char")
1936 || MY_STRMATCH("unsigned char")
1937 || MY_STRMATCH("double")
1938 || MY_STRMATCH("long double")
1939 || MY_STRMATCH("float")
1940 )
1941 {
1942 RTMsgError("Please do NOT use the type '%s' for probe arguments!", pszType);
1943 g_cTypeErrors++;
1944 return 0;
1945 }
1946
1947 if ( MY_STRMATCH("unsigned")
1948 || MY_STRMATCH("signed")
1949 || MY_STRMATCH("signed int")
1950 || MY_STRMATCH("unsigned int")
1951 || MY_STRMATCH("short")
1952 || MY_STRMATCH("signed short")
1953 || MY_STRMATCH("unsigned short")
1954 )
1955 parseWarnAbs(pStrm, offSrc, "Please avoid using the type '%s' for probe arguments!", pszType);
1956 if (MY_STRMATCH("unsigned")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1957 if (MY_STRMATCH("unsigned int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1958 if (MY_STRMATCH("signed")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1959 if (MY_STRMATCH("signed int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1960 if (MY_STRMATCH("short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1961 if (MY_STRMATCH("signed short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1962 if (MY_STRMATCH("unsigned short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_UNSIGNED;
1963
1964 /*
1965 * What we haven't caught by now is either unknown to us or wrong.
1966 */
1967 if (pszType[0] == 'P')
1968 {
1969 RTMsgError("Type '%s' looks like a pointer typedef, please do NOT use those "
1970 "but rather the non-pointer typedef or struct with '*'",
1971 pszType);
1972 g_cTypeErrors++;
1973 return VTG_TYPE_POINTER;
1974 }
1975
1976 RTMsgError("Don't know '%s' - please change or fix VBoxTpG", pszType);
1977 g_cTypeErrors++;
1978
1979#undef MY_STRCMP
1980 return 0;
1981}
1982
1983
1984/**
1985 * Initializes the members of an argument.
1986 *
1987 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1988 * @param pProbe The probe.
1989 * @param pArg The argument.
1990 * @param pStrm The input stream (for errors + warnings).
1991 * @param pchType The type.
1992 * @param cchType The type length.
1993 * @param pchName The name.
1994 * @param cchName The name length.
1995 */
1996static RTEXITCODE parseInitArgument(PVTGPROBE pProbe, PVTGARG pArg, PSCMSTREAM pStrm,
1997 char *pchType, size_t cchType, char *pchName, size_t cchName)
1998{
1999 Assert(!pArg->pszName); Assert(!pArg->pszTracerType); Assert(!pArg->pszCtxType); Assert(!pArg->fType);
2000
2001 pArg->pszArgPassingFmt = ", %s";
2002 pArg->pszName = RTStrDupN(pchName, cchName);
2003 pArg->pszTracerType = strtabInsertN(pchType, cchType);
2004 if (!pArg->pszTracerType || !pArg->pszName)
2005 return parseError(pStrm, 1, "Out of memory");
2006 pArg->fType = parseTypeExpression(pArg->pszTracerType, pStrm, pArg->offSrc);
2007
2008 if ( (pArg->fType & VTG_TYPE_POINTER)
2009 && !(g_fTypeContext & VTG_TYPE_CTX_R0) )
2010 {
2011 pArg->fType &= ~VTG_TYPE_POINTER;
2012 if ( !strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM")
2013 || !strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU")
2014 || !strcmp(pArg->pszTracerType, "struct CPUMCTX *") || !strcmp(pArg->pszTracerType, "PCPUMCTX")
2015 )
2016 {
2017 pArg->fType |= VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0
2018 | VTG_TYPE_FIXED_SIZED | (g_cHostBits / 8)
2019 | VTG_TYPE_AUTO_CONV_PTR;
2020 pArg->pszCtxType = RTStrDup("RTR0PTR");
2021
2022 if (!strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM"))
2023 pArg->pszArgPassingFmt = ", VTG_VM_TO_R0(%s)";
2024 else if (!strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU"))
2025 pArg->pszArgPassingFmt = ", VTG_VMCPU_TO_R0(%s)";
2026 else
2027 {
2028 PVTGARG pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
2029 if ( !pFirstArg
2030 || pFirstArg == pArg
2031 || strcmp(pFirstArg->pszName, "a_pVCpu")
2032 || ( strcmp(pFirstArg->pszTracerType, "struct VMCPU *")
2033 && strcmp(pFirstArg->pszTracerType, "PVMCPU *")) )
2034 return parseError(pStrm, 1, "The automatic ring-0 pointer conversion requires 'a_pVCpu' with type 'struct VMCPU *' as the first argument");
2035
2036 if (!strcmp(pArg->pszTracerType, "struct CPUMCTX *")|| !strcmp(pArg->pszTracerType, "PCPUMCTX"))
2037 pArg->pszArgPassingFmt = ", VTG_CPUMCTX_TO_R0(a_pVCpu, %s)";
2038 else
2039 pArg->pszArgPassingFmt = ", VBoxTpG-Is-Buggy!!";
2040 }
2041 }
2042 else
2043 {
2044 pArg->fType |= VTG_TYPE_CTX_POINTER | g_fTypeContext | VTG_TYPE_FIXED_SIZED | (g_cBits / 8);
2045 pArg->pszCtxType = RTStrDupN(pchType, cchType);
2046 }
2047 }
2048 else
2049 pArg->pszCtxType = RTStrDupN(pchType, cchType);
2050 if (!pArg->pszCtxType)
2051 return parseError(pStrm, 1, "Out of memory");
2052
2053 return RTEXITCODE_SUCCESS;
2054}
2055
2056
2057/**
2058 * Unmangles the probe name.
2059 *
2060 * This involves translating double underscore to dash.
2061 *
2062 * @returns Pointer to the unmangled name in the string table.
2063 * @param pszMangled The mangled name.
2064 */
2065static const char *parseUnmangleProbeName(const char *pszMangled)
2066{
2067 size_t cchMangled = strlen(pszMangled);
2068 char *pszTmp = (char *)alloca(cchMangled + 2);
2069 const char *pszSrc = pszMangled;
2070 char *pszDst = pszTmp;
2071
2072 while (*pszSrc)
2073 {
2074 if (pszSrc[0] == '_' && pszSrc[1] == '_' && pszSrc[2] != '_')
2075 {
2076 *pszDst++ = '-';
2077 pszSrc += 2;
2078 }
2079 else
2080 *pszDst++ = *pszSrc++;
2081 }
2082 *pszDst = '\0';
2083
2084 return strtabInsertN(pszTmp, pszDst - pszTmp);
2085}
2086
2087
2088/**
2089 * Parses a D probe statement.
2090 *
2091 * @returns Suitable exit code, errors message already written on failure.
2092 * @param pStrm The stream.
2093 * @param pProv The provider being parsed.
2094 */
2095static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
2096{
2097 size_t const iProbeLine = ScmStreamTellLine(pStrm);
2098
2099 /*
2100 * Next up is a name followed by an opening parenthesis.
2101 */
2102 size_t cchProbe;
2103 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
2104 if (!pszProbe)
2105 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
2106 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2107 if (ch != '(')
2108 return parseError(pStrm, 1, "Expected '(' after the probe name");
2109
2110 /*
2111 * Create a probe instance.
2112 */
2113 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
2114 if (!pProbe)
2115 return parseError(pStrm, 0, "Out of memory");
2116 RTListInit(&pProbe->ArgHead);
2117 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
2118 pProbe->offArgList = UINT32_MAX;
2119 pProbe->iLine = iProbeLine;
2120 pProbe->pszMangledName = RTStrDupN(pszProbe, cchProbe);
2121 if (!pProbe->pszMangledName)
2122 return parseError(pStrm, 0, "Out of memory");
2123 pProbe->pszUnmangledName = parseUnmangleProbeName(pProbe->pszMangledName);
2124 if (!pProbe->pszUnmangledName)
2125 return parseError(pStrm, 0, "Out of memory");
2126
2127 /*
2128 * Parse loop for the argument.
2129 */
2130 PVTGARG pArg = NULL;
2131 size_t cchName = 0;
2132 size_t cchArg = 0;
2133 char szArg[4096];
2134 for (;;)
2135 {
2136 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2137 switch (ch)
2138 {
2139 case ')':
2140 case ',':
2141 {
2142 /* commit the argument */
2143 if (pArg)
2144 {
2145 if (!cchName)
2146 return parseError(pStrm, 1, "Argument has no name");
2147 if (cchArg - cchName - 1 >= 128)
2148 return parseError(pStrm, 1, "Argument type too long");
2149 RTEXITCODE rcExit = parseInitArgument(pProbe, pArg, pStrm,
2150 szArg, cchArg - cchName - 1,
2151 &szArg[cchArg - cchName], cchName);
2152 if (rcExit != RTEXITCODE_SUCCESS)
2153 return rcExit;
2154 if (VTG_TYPE_IS_LARGE(pArg->fType))
2155 pProbe->fHaveLargeArgs = true;
2156 pArg = NULL;
2157 cchName = cchArg = 0;
2158 }
2159 if (ch == ')')
2160 {
2161 size_t off = ScmStreamTell(pStrm);
2162 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2163 if (ch != ';')
2164 return parseErrorAbs(pStrm, off, "Expected ';'");
2165 return RTEXITCODE_SUCCESS;
2166 }
2167 break;
2168 }
2169
2170 default:
2171 {
2172 size_t cchWord;
2173 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
2174 if (!pszWord)
2175 return parseError(pStrm, 0, "Expected argument");
2176 if (!pArg)
2177 {
2178 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
2179 if (!pArg)
2180 return parseError(pStrm, 1, "Out of memory");
2181 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
2182 pArg->iArgNo = pProbe->cArgs++;
2183 pArg->pProbe = pProbe;
2184 pArg->offSrc = ScmStreamTell(pStrm) - cchWord;
2185
2186 if (cchWord + 1 > sizeof(szArg))
2187 return parseError(pStrm, 1, "Too long parameter declaration");
2188 memcpy(szArg, pszWord, cchWord);
2189 szArg[cchWord] = '\0';
2190 cchArg = cchWord;
2191 cchName = 0;
2192 }
2193 else
2194 {
2195 if (cchArg + 1 + cchWord + 1 > sizeof(szArg))
2196 return parseError(pStrm, 1, "Too long parameter declaration");
2197
2198 szArg[cchArg++] = ' ';
2199 memcpy(&szArg[cchArg], pszWord, cchWord);
2200 cchArg += cchWord;
2201 szArg[cchArg] = '\0';
2202 cchName = cchWord;
2203 }
2204 break;
2205 }
2206
2207 case '*':
2208 {
2209 if (!pArg)
2210 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
2211 if (cchArg + sizeof(" *") >= sizeof(szArg))
2212 return parseError(pStrm, 1, "Too long parameter declaration");
2213 szArg[cchArg++] = ' ';
2214 szArg[cchArg++] = '*';
2215 szArg[cchArg ] = '\0';
2216 cchName = 0;
2217 break;
2218 }
2219
2220 case ~(unsigned)0:
2221 return parseError(pStrm, 0, "Missing closing ')' on probe");
2222 }
2223 }
2224}
2225
2226/**
2227 * Parses a D provider statement.
2228 *
2229 * @returns Suitable exit code, errors message already written on failure.
2230 * @param pStrm The stream.
2231 */
2232static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
2233{
2234 /*
2235 * Next up is a name followed by a curly bracket. Ignore comments.
2236 */
2237 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
2238 if (rcExit != RTEXITCODE_SUCCESS)
2239 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
2240 size_t cchName;
2241 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
2242 if (!pszName)
2243 return parseError(pStrm, 0, "Bad provider name");
2244 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
2245 return parseError(pStrm, 1, "A provider name cannot end with digit");
2246
2247 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2248 if (ch != '{')
2249 return parseError(pStrm, 1, "Expected '{' after the provider name");
2250
2251 /*
2252 * Create a provider instance.
2253 */
2254 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
2255 if (!pProv)
2256 return parseError(pStrm, 0, "Out of memory");
2257 RTListInit(&pProv->ProbeHead);
2258 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
2259 pProv->pszName = strtabInsertN(pszName, cchName);
2260 if (!pProv->pszName)
2261 return parseError(pStrm, 0, "Out of memory");
2262
2263 /*
2264 * Parse loop.
2265 */
2266 for (;;)
2267 {
2268 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2269 switch (ch)
2270 {
2271 case 'p':
2272 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
2273 rcExit = parseProbe(pStrm, pProv);
2274 else
2275 rcExit = parseError(pStrm, 1, "Unexpected character");
2276 break;
2277
2278 case '}':
2279 {
2280 size_t off = ScmStreamTell(pStrm);
2281 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2282 if (ch == ';')
2283 return RTEXITCODE_SUCCESS;
2284 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
2285 break;
2286 }
2287
2288 case ~(unsigned)0:
2289 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
2290 break;
2291
2292 default:
2293 rcExit = parseError(pStrm, 1, "Unexpected character");
2294 break;
2295 }
2296 if (rcExit != RTEXITCODE_SUCCESS)
2297 return rcExit;
2298 }
2299}
2300
2301
2302static RTEXITCODE parseScript(const char *pszScript)
2303{
2304 SCMSTREAM Strm;
2305 int rc = ScmStreamInitForReading(&Strm, pszScript);
2306 if (RT_FAILURE(rc))
2307 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
2308 if (g_cVerbosity > 0)
2309 RTMsgInfo("Parsing '%s'...", pszScript);
2310
2311 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2312 unsigned ch;
2313 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
2314 {
2315 if (RT_C_IS_SPACE(ch))
2316 continue;
2317 switch (ch)
2318 {
2319 case '/':
2320 ch = ScmStreamGetCh(&Strm);
2321 if (ch == '*')
2322 rcExit = parseMultiLineComment(&Strm);
2323 else if (ch == '/')
2324 rcExit = parseOneLineComment(&Strm);
2325 else
2326 rcExit = parseError(&Strm, 2, "Unexpected character");
2327 break;
2328
2329 case 'p':
2330 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
2331 rcExit = parseProvider(&Strm);
2332 else
2333 rcExit = parseError(&Strm, 1, "Unexpected character");
2334 break;
2335
2336 case '#':
2337 {
2338 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
2339 if (ch == ~(unsigned)0)
2340 rcExit = RTEXITCODE_FAILURE;
2341 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
2342 rcExit = parsePragma(&Strm);
2343 else
2344 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
2345 break;
2346 }
2347
2348 default:
2349 rcExit = parseError(&Strm, 1, "Unexpected character");
2350 break;
2351 }
2352 if (rcExit != RTEXITCODE_SUCCESS)
2353 return rcExit;
2354 }
2355
2356 ScmStreamDelete(&Strm);
2357 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
2358 RTMsgInfo("Successfully parsed '%s'.", pszScript);
2359 return rcExit;
2360}
2361
2362
2363/**
2364 * Parses the arguments.
2365 */
2366static RTEXITCODE parseArguments(int argc, char **argv)
2367{
2368 /*
2369 * Set / Adjust defaults.
2370 */
2371 int rc = RTPathAbs(g_pszAssemblerIncVal, g_szAssemblerIncVal, sizeof(g_szAssemblerIncVal) - 1);
2372 if (RT_FAILURE(rc))
2373 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed: %Rrc", rc);
2374 strcat(g_szAssemblerIncVal, "/");
2375 g_pszAssemblerIncVal = g_szAssemblerIncVal;
2376
2377 /*
2378 * Option config.
2379 */
2380 enum
2381 {
2382 kVBoxTpGOpt_32Bit = 1000,
2383 kVBoxTpGOpt_64Bit,
2384 kVBoxTpGOpt_GenerateWrapperHeader,
2385 kVBoxTpGOpt_Assembler,
2386 kVBoxTpGOpt_AssemblerFmtOpt,
2387 kVBoxTpGOpt_AssemblerFmtVal,
2388 kVBoxTpGOpt_AssemblerOutputOpt,
2389 kVBoxTpGOpt_AssemblerOption,
2390 kVBoxTpGOpt_Pic,
2391 kVBoxTpGOpt_ProbeFnName,
2392 kVBoxTpGOpt_ProbeFnImported,
2393 kVBoxTpGOpt_ProbeFnNotImported,
2394 kVBoxTpGOpt_Host32Bit,
2395 kVBoxTpGOpt_Host64Bit,
2396 kVBoxTpGOpt_RawModeContext,
2397 kVBoxTpGOpt_Ring0Context,
2398 kVBoxTpGOpt_Ring0ContextAgnostic,
2399 kVBoxTpGOpt_Ring3Context,
2400 kVBoxTpGOpt_End
2401 };
2402
2403 static RTGETOPTDEF const s_aOpts[] =
2404 {
2405 /* dtrace w/ long options */
2406 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
2407 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
2408 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
2409 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
2410 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
2411 { "--output", 'o', RTGETOPT_REQ_STRING },
2412 { "--script", 's', RTGETOPT_REQ_STRING },
2413 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2414 /* our stuff */
2415 { "--generate-wrapper-header", kVBoxTpGOpt_GenerateWrapperHeader, RTGETOPT_REQ_NOTHING },
2416 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
2417 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
2418 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
2419 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
2420 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
2421 { "--pic", kVBoxTpGOpt_Pic, RTGETOPT_REQ_NOTHING },
2422 { "--probe-fn-name", kVBoxTpGOpt_ProbeFnName, RTGETOPT_REQ_STRING },
2423 { "--probe-fn-imported", kVBoxTpGOpt_ProbeFnImported, RTGETOPT_REQ_NOTHING },
2424 { "--probe-fn-not-imported", kVBoxTpGOpt_ProbeFnNotImported, RTGETOPT_REQ_NOTHING },
2425 { "--host-32-bit", kVBoxTpGOpt_Host32Bit, RTGETOPT_REQ_NOTHING },
2426 { "--host-64-bit", kVBoxTpGOpt_Host64Bit, RTGETOPT_REQ_NOTHING },
2427 { "--raw-mode-context", kVBoxTpGOpt_RawModeContext, RTGETOPT_REQ_NOTHING },
2428 { "--ring-0-context", kVBoxTpGOpt_Ring0Context, RTGETOPT_REQ_NOTHING },
2429 { "--ring-0-context-agnostic", kVBoxTpGOpt_Ring0ContextAgnostic, RTGETOPT_REQ_NOTHING },
2430 { "--ring-3-context", kVBoxTpGOpt_Ring3Context, RTGETOPT_REQ_NOTHING },
2431 /** @todo We're missing a bunch of assembler options! */
2432 };
2433
2434 RTGETOPTUNION ValueUnion;
2435 RTGETOPTSTATE GetOptState;
2436 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2437 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2438
2439 /*
2440 * Process the options.
2441 */
2442 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2443 {
2444 switch (rc)
2445 {
2446 /*
2447 * DTrace compatible options.
2448 */
2449 case kVBoxTpGOpt_32Bit:
2450 g_cHostBits = g_cBits = 32;
2451 g_pszAssemblerFmtVal = g_szAssemblerFmtVal32;
2452 break;
2453
2454 case kVBoxTpGOpt_64Bit:
2455 g_cHostBits = g_cBits = 64;
2456 g_pszAssemblerFmtVal = g_szAssemblerFmtVal64;
2457 break;
2458
2459 case 'C':
2460 g_fApplyCpp = true;
2461 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
2462 break;
2463
2464 case 'G':
2465 if ( g_enmAction != kVBoxTpGAction_Nothing
2466 && g_enmAction != kVBoxTpGAction_GenerateObject)
2467 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G does not mix with -h or --generate-wrapper-header");
2468 g_enmAction = kVBoxTpGAction_GenerateObject;
2469 break;
2470
2471 case 'h':
2472 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
2473 {
2474 if ( g_enmAction != kVBoxTpGAction_Nothing
2475 && g_enmAction != kVBoxTpGAction_GenerateHeader)
2476 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h does not mix with -G or --generate-wrapper-header");
2477 g_enmAction = kVBoxTpGAction_GenerateHeader;
2478 }
2479 else
2480 {
2481 /* --help or similar */
2482 RTPrintf("VirtualBox Tracepoint Generator\n"
2483 "\n"
2484 "Usage: %s [options]\n"
2485 "\n"
2486 "Options:\n", RTProcShortName());
2487 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
2488 if ((unsigned)s_aOpts[i].iShort < 128)
2489 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
2490 else
2491 RTPrintf(" %s\n", s_aOpts[i].pszLong);
2492 return RTEXITCODE_SUCCESS;
2493 }
2494 break;
2495
2496 case 'o':
2497 if (g_pszOutput)
2498 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
2499 g_pszOutput = ValueUnion.psz;
2500 break;
2501
2502 case 's':
2503 if (g_pszScript)
2504 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
2505 g_pszScript = ValueUnion.psz;
2506 break;
2507
2508 case 'v':
2509 g_cVerbosity++;
2510 break;
2511
2512 case 'V':
2513 {
2514 /* The following is assuming that svn does it's job here. */
2515 static const char s_szRev[] = "$Revision: 104620 $";
2516 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2517 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2518 return RTEXITCODE_SUCCESS;
2519 }
2520
2521 case VINF_GETOPT_NOT_OPTION:
2522 if (g_enmAction == kVBoxTpGAction_GenerateObject)
2523 break; /* object files, ignore them. */
2524 return RTGetOptPrintError(rc, &ValueUnion);
2525
2526
2527 /*
2528 * Our options.
2529 */
2530 case kVBoxTpGOpt_GenerateWrapperHeader:
2531 if ( g_enmAction != kVBoxTpGAction_Nothing
2532 && g_enmAction != kVBoxTpGAction_GenerateWrapperHeader)
2533 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-wrapper-header does not mix with -h or -G");
2534 g_enmAction = kVBoxTpGAction_GenerateWrapperHeader;
2535 break;
2536
2537 case kVBoxTpGOpt_Assembler:
2538 g_pszAssembler = ValueUnion.psz;
2539 break;
2540
2541 case kVBoxTpGOpt_AssemblerFmtOpt:
2542 g_pszAssemblerFmtOpt = ValueUnion.psz;
2543 break;
2544
2545 case kVBoxTpGOpt_AssemblerFmtVal:
2546 g_pszAssemblerFmtVal = ValueUnion.psz;
2547 break;
2548
2549 case kVBoxTpGOpt_AssemblerOutputOpt:
2550 g_pszAssemblerOutputOpt = ValueUnion.psz;
2551 break;
2552
2553 case kVBoxTpGOpt_AssemblerOption:
2554 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
2555 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
2556 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
2557 g_cAssemblerOptions++;
2558 break;
2559
2560 case kVBoxTpGOpt_Pic:
2561 g_fPic = true;
2562 break;
2563
2564 case kVBoxTpGOpt_ProbeFnName:
2565 g_pszProbeFnName = ValueUnion.psz;
2566 break;
2567
2568 case kVBoxTpGOpt_ProbeFnImported:
2569 g_fProbeFnImported = true;
2570 break;
2571
2572 case kVBoxTpGOpt_ProbeFnNotImported:
2573 g_fProbeFnImported = false;
2574 break;
2575
2576 case kVBoxTpGOpt_Host32Bit:
2577 g_cHostBits = 32;
2578 break;
2579
2580 case kVBoxTpGOpt_Host64Bit:
2581 g_cHostBits = 64;
2582 break;
2583
2584 case kVBoxTpGOpt_RawModeContext:
2585 g_fTypeContext = VTG_TYPE_CTX_RC;
2586 g_pszContextDefine = "IN_RC";
2587 g_pszContextDefine2 = NULL;
2588 break;
2589
2590 case kVBoxTpGOpt_Ring0Context:
2591 g_fTypeContext = VTG_TYPE_CTX_R0;
2592 g_pszContextDefine = "IN_RING0";
2593 g_pszContextDefine2 = NULL;
2594 break;
2595
2596 case kVBoxTpGOpt_Ring0ContextAgnostic:
2597 g_fTypeContext = VTG_TYPE_CTX_R0;
2598 g_pszContextDefine = "IN_RING0_AGNOSTIC";
2599 g_pszContextDefine2 = "IN_RING0";
2600 break;
2601
2602 case kVBoxTpGOpt_Ring3Context:
2603 g_fTypeContext = VTG_TYPE_CTX_R3;
2604 g_pszContextDefine = "IN_RING3";
2605 g_pszContextDefine2 = NULL;
2606 break;
2607
2608
2609 /*
2610 * Errors and bugs.
2611 */
2612 default:
2613 return RTGetOptPrintError(rc, &ValueUnion);
2614 }
2615 }
2616
2617 /*
2618 * Check that we've got all we need.
2619 */
2620 if (g_enmAction == kVBoxTpGAction_Nothing)
2621 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h, -G or --generate-wrapper-header)");
2622 if (!g_pszScript)
2623 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
2624 if (!g_pszOutput)
2625 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
2626
2627 return RTEXITCODE_SUCCESS;
2628}
2629
2630
2631int main(int argc, char **argv)
2632{
2633 int rc = RTR3InitExe(argc, &argv, 0);
2634 if (RT_FAILURE(rc))
2635 return 1;
2636
2637 RTEXITCODE rcExit = parseArguments(argc, argv);
2638 if (rcExit == RTEXITCODE_SUCCESS)
2639 {
2640 /*
2641 * Parse the script.
2642 */
2643 RTListInit(&g_ProviderHead);
2644 rcExit = parseScript(g_pszScript);
2645 if (rcExit == RTEXITCODE_SUCCESS)
2646 {
2647 /*
2648 * Take action.
2649 */
2650 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
2651 rcExit = generateFile(g_pszOutput, "header", generateHeader);
2652 else if (g_enmAction == kVBoxTpGAction_GenerateWrapperHeader)
2653 rcExit = generateFile(g_pszOutput, "wrapper header", generateWrapperHeader);
2654 else
2655 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
2656 }
2657 }
2658
2659 if (rcExit == RTEXITCODE_SUCCESS && g_cTypeErrors > 0)
2660 rcExit = RTEXITCODE_FAILURE;
2661 return rcExit;
2662}
2663
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use