VirtualBox

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

Last change on this file since 73788 was 73097, checked in by vboxsync, 6 years ago

*: Made RT_UOFFSETOF, RT_OFFSETOF, RT_UOFFSETOF_ADD and RT_OFFSETOF_ADD work like builtin_offsetof() and require compile time resolvable requests, adding RT_UOFFSETOF_DYN for the dynamic questions that can only be answered at runtime.

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

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