VirtualBox

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

Last change on this file since 40887 was 40887, checked in by vboxsync, 13 years ago

VBoxTpG: another bugfix in the arg list handling.

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

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