VirtualBox

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

Last change on this file since 40754 was 40641, checked in by vboxsync, 12 years ago

VBoxTpG: bool vs const char * returns spotted by gcc 4.7 & Frank.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use