VirtualBox

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

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

VBoxTpG.cpp: Microsoft Visual C++ support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 73.3 KB
Line 
1/* $Id: VBoxTpG.cpp 40826 2012-04-08 18:41:46Z 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 = "SUPR0TracerFireProbe";
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' (assembler): %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 += (uint32_t)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 40826 2012-04-08 18:41:46Z 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 VTGPrLc.Begin data align=64]\n"
463 /*" times 24 db 0xcc\n"*/
464 "VTG_GLOBAL g_aVTGPrLc, data\n"
465 " [section VTGPrLc.Data data align=4]\n"
466 " [section VTGPrLc.End data align=4]\n"
467 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
468 /*" times 24 db 0xcc\n"*/
469 " [section VTGObj data align=32]\n"
470 "\n"
471 "%%elifdef ASM_FORMAT_ELF\n"
472 " %%macro VTG_GLOBAL 2\n"
473 " global NAME(%%1):%%2 hidden\n"
474 " NAME(%%1):\n"
475 " %%endmacro\n"
476 " [section .VTGPrLc.Start progbits alloc noexec write align=1]\n"
477 "VTG_GLOBAL g_aVTGPrLc, data\n"
478 " [section .VTGPrLc progbits alloc noexec write align=1]\n"
479 " [section .VTGPrLc.End progbits alloc noexec write align=1]\n"
480 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
481 " [section .VTGData progbits alloc noexec write align=4096]\n"
482 "\n"
483 "%%else\n"
484 " %%error \"ASM_FORMAT_XXX is not defined\"\n"
485 "%%endif\n"
486 "\n"
487 "\n"
488 "VTG_GLOBAL g_VTGObjHeader, data\n"
489 " ;0 1 2 3\n"
490 " ;012345678901234567890123456789012\n"
491 " db 'VTG Object Header v1.0', 0, 0\n"
492 " dd %u\n"
493 " dd 0\n"
494 " RTCCPTR_DEF NAME(g_aVTGProviders)\n"
495 " RTCCPTR_DEF NAME(g_aVTGProviders_End) - NAME(g_aVTGProviders)\n"
496 " RTCCPTR_DEF NAME(g_aVTGProbes)\n"
497 " RTCCPTR_DEF NAME(g_aVTGProbes_End) - NAME(g_aVTGProbes)\n"
498 " RTCCPTR_DEF NAME(g_afVTGProbeEnabled)\n"
499 " RTCCPTR_DEF NAME(g_afVTGProbeEnabled_End) - NAME(g_afVTGProbeEnabled)\n"
500 " RTCCPTR_DEF NAME(g_achVTGStringTable)\n"
501 " RTCCPTR_DEF NAME(g_achVTGStringTable_End) - NAME(g_achVTGStringTable)\n"
502 " RTCCPTR_DEF NAME(g_aVTGArgLists)\n"
503 " RTCCPTR_DEF NAME(g_aVTGArgLists_End) - NAME(g_aVTGArgLists)\n"
504 " RTCCPTR_DEF NAME(g_aVTGPrLc)\n"
505 " RTCCPTR_DEF NAME(g_aVTGPrLc_End) ; cross section/segment size not possible\n"
506 " RTCCPTR_DEF 0\n"
507 " RTCCPTR_DEF 0\n"
508 " RTCCPTR_DEF 0\n"
509 " RTCCPTR_DEF 0\n"
510 ,
511 g_pszScript, g_cBits);
512
513 /*
514 * Declare the probe enable flags.
515 */
516 ScmStreamPrintf(pStrm,
517 ";\n"
518 "; Probe enabled flags. Since these will be accessed all the time\n"
519 "; they are placed together and early in the section to get some more\n"
520 "; cache and TLB hits when the probes are disabled.\n"
521 ";\n"
522 "VTG_GLOBAL g_afVTGProbeEnabled, data\n"
523 );
524 uint32_t cProbes = 0;
525 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
526 {
527 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
528 {
529 ScmStreamPrintf(pStrm,
530 "VTG_GLOBAL g_fVTGProbeEnabled_%s_%s, data\n"
531 " db 0\n",
532 pProvider->pszName, pProbe->pszMangledName);
533 cProbes++;
534 }
535 }
536 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_afVTGProbeEnabled_End, data\n");
537 if (cProbes >= _32K)
538 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many probes: %u (max %u)", cProbes, _32K - 1);
539
540 /*
541 * Dump the string table before we start using the strings.
542 */
543 ScmStreamPrintf(pStrm,
544 "\n"
545 ";\n"
546 "; The string table.\n"
547 ";\n"
548 "VTG_GLOBAL g_achVTGStringTable, data\n");
549 g_offStrTab = 0;
550 RTStrSpaceEnumerate(&g_StrSpace, generateAssemblyStrTabCallback, pStrm);
551 ScmStreamPrintf(pStrm,
552 "VTG_GLOBAL g_achVTGStringTable_End, data\n");
553
554 /*
555 * Write out the argument lists before we use them.
556 */
557 ScmStreamPrintf(pStrm,
558 "\n"
559 ";\n"
560 "; The argument lists.\n"
561 ";\n"
562 "VTG_GLOBAL g_aVTGArgLists, data\n");
563 uint32_t off = 0;
564 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
565 {
566 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
567 {
568 if (pProbe->offArgList != UINT32_MAX)
569 continue;
570
571 /* Write it. */
572 pProbe->offArgList = off;
573 ScmStreamPrintf(pStrm,
574 " ; off=%u\n"
575 " db %2u ; Argument count\n"
576 " db 0, 0, 0 ; Reserved\n"
577 , off, pProbe->cArgs);
578 off += 4;
579 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
580 {
581 ScmStreamPrintf(pStrm,
582 " dd %6u ; type '%s'\n"
583 " dd %6u ; name '%s'\n",
584 strtabGetOff(pArg->pszType), pArg->pszType,
585 strtabGetOff(pArg->pszName), pArg->pszName);
586 off += 8;
587 }
588
589 /* Look for matching argument lists (lazy bird walks the whole list). */
590 PVTGPROVIDER pProv2;
591 RTListForEach(&g_ProviderHead, pProv2, VTGPROVIDER, ListEntry)
592 {
593 PVTGPROBE pProbe2;
594 RTListForEach(&pProvider->ProbeHead, pProbe2, VTGPROBE, ListEntry)
595 {
596 if (pProbe2->offArgList != UINT32_MAX)
597 continue;
598 if (pProbe2->cArgs != pProbe->cArgs)
599 continue;
600
601 PVTGARG pArg2;
602 pArg = RTListNodeGetNext(&pProbe->ArgHead, VTGARG, ListEntry);
603 pArg2 = RTListNodeGetNext(&pProbe2->ArgHead, VTGARG, ListEntry);
604 int32_t cArgs = pProbe->cArgs;
605 while ( cArgs-- > 0
606 && pArg2->pszName == pArg2->pszName
607 && pArg2->pszType == pArg2->pszType)
608 {
609 pArg = RTListNodeGetNext(&pArg->ListEntry, VTGARG, ListEntry);
610 pArg2 = RTListNodeGetNext(&pArg2->ListEntry, VTGARG, ListEntry);
611 }
612 if (cArgs >= 0)
613 continue;
614 pProbe2->offArgList = pProbe->offArgList;
615 }
616 }
617 }
618 }
619 ScmStreamPrintf(pStrm,
620 "VTG_GLOBAL g_aVTGArgLists_End, data\n");
621
622
623 /*
624 * Probe definitions.
625 */
626 ScmStreamPrintf(pStrm,
627 "\n"
628 ";\n"
629 "; Prob definitions.\n"
630 ";\n"
631 "VTG_GLOBAL g_aVTGProbes, data\n"
632 "\n");
633 uint32_t iProvider = 0;
634 uint32_t iProbe = 0;
635 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
636 {
637 pProvider->iFirstProbe = iProbe;
638 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
639 {
640 ScmStreamPrintf(pStrm,
641 "VTG_GLOBAL g_VTGProbeData_%s_%s, data ; idx=#%4u\n"
642 " dd %6u ; name\n"
643 " dd %6u ; Argument list offset\n"
644 " dw g_fVTGProbeEnabled_%s_%s - g_afVTGProbeEnabled\n"
645 " dw %6u ; provider index\n"
646 " dd 0 ; for the application\n"
647 ,
648 pProvider->pszName, pProbe->pszMangledName, iProbe,
649 strtabGetOff(pProbe->pszUnmangledName),
650 pProbe->offArgList,
651 pProvider->pszName, pProbe->pszMangledName,
652 iProvider);
653 pProbe->iProbe = iProbe;
654 iProbe++;
655 }
656 pProvider->cProbes = iProbe - pProvider->iFirstProbe;
657 iProvider++;
658 }
659 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProbes_End, data\n");
660
661 /*
662 * The providers data.
663 */
664 ScmStreamPrintf(pStrm,
665 "\n"
666 ";\n"
667 "; Provider data.\n"
668 ";\n"
669 "VTG_GLOBAL g_aVTGProviders, data\n");
670 iProvider = 0;
671 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
672 {
673 ScmStreamPrintf(pStrm,
674 " ; idx=#%4u - %s\n"
675 " dd %6u ; name\n"
676 " dw %6u ; index of first probe\n"
677 " dw %6u ; count of probes\n"
678 " db %d, %d, %d ; AttrSelf\n"
679 " db %d, %d, %d ; AttrModules\n"
680 " db %d, %d, %d ; AttrFunctions\n"
681 " db %d, %d, %d ; AttrName\n"
682 " db %d, %d, %d ; AttrArguments\n"
683 " db 0 ; reserved\n"
684 ,
685 iProvider, pProvider->pszName,
686 strtabGetOff(pProvider->pszName),
687 pProvider->iFirstProbe,
688 pProvider->cProbes,
689 pProvider->AttrSelf.enmCode, pProvider->AttrSelf.enmData, pProvider->AttrSelf.enmDataDep,
690 pProvider->AttrModules.enmCode, pProvider->AttrModules.enmData, pProvider->AttrModules.enmDataDep,
691 pProvider->AttrFunctions.enmCode, pProvider->AttrFunctions.enmData, pProvider->AttrFunctions.enmDataDep,
692 pProvider->AttrName.enmCode, pProvider->AttrName.enmData, pProvider->AttrName.enmDataDep,
693 pProvider->AttrArguments.enmCode, pProvider->AttrArguments.enmData, pProvider->AttrArguments.enmDataDep);
694 iProvider++;
695 }
696 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProviders_End, data\n");
697
698 /*
699 * Emit code for the stub functions.
700 */
701 ScmStreamPrintf(pStrm,
702 "\n"
703 ";\n"
704 "; Prob stubs.\n"
705 ";\n"
706 "BEGINCODE\n"
707 "extern %sNAME(%s)\n",
708 g_fProbeFnImported ? "IMP" : "",
709 g_pszProbeFnName);
710 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
711 {
712 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
713 {
714 ScmStreamPrintf(pStrm,
715 "\n"
716 "VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc",
717 pProvider->pszName, pProbe->pszMangledName);
718 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
719 {
720 ScmStreamPrintf(pStrm, ", %s %s", pArg->pszType, pArg->pszName);
721 }
722 ScmStreamPrintf(pStrm,
723 ");\n");
724
725 bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64"));
726 bool const fMachO32 = g_cBits == 32 && !strcmp(g_pszAssemblerFmtVal, "macho32");
727
728 /*
729 * Check if the probe in question is enabled.
730 */
731 if (g_cBits == 32)
732 ScmStreamPrintf(pStrm,
733 " mov eax, [esp + 4]\n"
734 " test byte [eax+3], 0x80 ; fEnabled == true?\n"
735 " jz .return ; jump on false\n");
736 else if (fWin64)
737 ScmStreamPrintf(pStrm,
738 " test byte [rcx+3], 0x80 ; fEnabled == true?\n"
739 " jz .return ; jump on false\n");
740 else
741 ScmStreamPrintf(pStrm,
742 " test byte [rdi+3], 0x80 ; fEnabled == true?\n"
743 " jz .return ; jump on false\n");
744
745 /*
746 * Shuffle the arguments around, replacing the location pointer with the probe ID.
747 */
748 if (fMachO32)
749 {
750 /* Need to recreate the stack frame entirely here as the probe
751 function differs by taking all uint64_t arguments instead
752 of uintptr_t. Understandable, but real PITA. */
753 ScmStreamPrintf(pStrm, "int3\n");
754 }
755 else if (g_cBits == 32)
756 /* Assumes the size of the arguments are no larger than a
757 pointer. This is asserted in the header. */
758 ScmStreamPrintf(pStrm, g_fProbeFnImported ?
759 " mov edx, [eax + 4] ; idProbe\n"
760 " mov ecx, IMP2(%s)\n"
761 " mov [esp + 4], edx ; Replace pVTGProbeLoc with idProbe.\n"
762 " jmp ecx\n"
763 :
764 " mov edx, [eax + 4] ; idProbe\n"
765 " mov [esp + 4], edx ; Replace pVTGProbeLoc with idProbe.\n"
766 " jmp NAME(%s)\n"
767 , g_pszProbeFnName);
768 else if (fWin64)
769 ScmStreamPrintf(pStrm, g_fProbeFnImported ?
770 " mov rax, IMP2(%s)\n"
771 " mov ecx, [rcx + 4] ; idProbe replaces pVTGProbeLoc.\n"
772 " jmp rax\n"
773 :
774 " mov ecx, [rcx + 4] ; idProbe replaces pVTGProbeLoc.\n"
775 " jmp NAME(SUPR0FireProbe)\n"
776 , g_pszProbeFnName);
777 else
778 ScmStreamPrintf(pStrm, g_fProbeFnImported ?
779 " lea rax, [IMP2(%s)]\n" //??? macho64?
780 " mov edi, [rdi + 4] ; idProbe replaces pVTGProbeLoc.\n"
781 " jmp rax\n"
782 :
783 " mov edi, [rdi + 4] ; idProbe replaces pVTGProbeLoc.\n"
784 " jmp NAME(SUPR0FireProbe)\n"
785 , g_pszProbeFnName);
786
787 ScmStreamPrintf(pStrm,
788 ".return:\n"
789 " ret ; The probe was disabled, return\n"
790 "\n");
791 }
792 }
793
794 return RTEXITCODE_SUCCESS;
795}
796
797
798static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm)
799{
800 if (!pszTempAsm)
801 {
802 size_t cch = strlen(pszOutput);
803 char *psz = (char *)alloca(cch + sizeof(".asm"));
804 memcpy(psz, pszOutput, cch);
805 memcpy(psz + cch, ".asm", sizeof(".asm"));
806 pszTempAsm = psz;
807 }
808
809 RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly);
810 if (rcExit == RTEXITCODE_SUCCESS)
811 rcExit = generateInvokeAssembler(pszOutput, pszTempAsm);
812 RTFileDelete(pszTempAsm);
813 return rcExit;
814}
815
816
817static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe)
818{
819 size_t cbMax = strlen(pszProvider) + 1 + strlen(pszProbe) + 1;
820 if (cbMax > cbBuf || cbMax > 80)
821 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider);
822
823 while (*pszProvider)
824 *pszBuf++ = RT_C_TO_UPPER(*pszProvider++);
825
826 *pszBuf++ = '_';
827
828 while (*pszProbe)
829 {
830 if (pszProbe[0] == '_' && pszProbe[1] == '_')
831 pszProbe++;
832 *pszBuf++ = RT_C_TO_UPPER(*pszProbe++);
833 }
834
835 *pszBuf = '\0';
836 return RTEXITCODE_SUCCESS;
837}
838
839static RTEXITCODE generateHeaderInner(PSCMSTREAM pStrm)
840{
841 /*
842 * Calc the double inclusion blocker define and then write the file header.
843 */
844 char szTmp[4096];
845 const char *pszName = RTPathFilename(g_pszScript);
846 size_t cchName = strlen(pszName);
847 if (cchName >= sizeof(szTmp) - 64)
848 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
849 szTmp[0] = '_';
850 szTmp[1] = '_';
851 szTmp[2] = '_';
852 memcpy(&szTmp[3], pszName, cchName);
853 szTmp[3 + cchName + 0] = '_';
854 szTmp[3 + cchName + 1] = '_';
855 szTmp[3 + cchName + 2] = '_';
856 szTmp[3 + cchName + 3] = '\0';
857 char *psz = &szTmp[3];
858 while (*psz)
859 {
860 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
861 *psz = '_';
862 psz++;
863 }
864
865 ScmStreamPrintf(pStrm,
866 "/* $Id: VBoxTpG.cpp 40826 2012-04-08 18:41:46Z vboxsync $ */\n"
867 "/** @file\n"
868 " * Automatically generated from %s. Do NOT edit!\n"
869 " */\n"
870 "\n"
871 "#ifndef %s\n"
872 "#define %s\n"
873 "\n"
874 "#include <VBox/VBoxTpG.h>\n"
875 "\n"
876 "RT_C_DECLS_BEGIN\n"
877 "\n"
878 "#ifdef VBOX_WITH_DTRACE\n"
879 "\n"
880 "# ifdef _MSC_VER\n"
881 "# pragma data_seg(VTG_LOC_SECT)\n"
882 "# pragma data_seg()\n"
883 "# endif\n"
884 "\n"
885 ,
886 g_pszScript,
887 szTmp,
888 szTmp);
889
890 /*
891 * Declare data, code and macros for each probe.
892 */
893 PVTGPROVIDER pProv;
894 PVTGPROBE pProbe;
895 PVTGARG pArg;
896 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
897 {
898 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
899 {
900 ScmStreamPrintf(pStrm,
901 "extern bool g_fVTGProbeEnabled_%s_%s;\n"
902 "extern uint8_t g_VTGProbeData_%s_%s;\n"
903 "DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC",
904 pProv->pszName, pProbe->pszMangledName,
905 pProv->pszName, pProbe->pszMangledName,
906 pProv->pszName, pProbe->pszMangledName);
907 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
908 {
909 ScmStreamPrintf(pStrm, ", %s", pArg->pszType);
910 }
911 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
912 ScmStreamPrintf(pStrm,
913 ");\n"
914 "# define %s_ENABLED() \\\n"
915 " (RT_UNLIKELY(g_fVTGProbeEnabled_%s_%s)) \n"
916 "# define %s("
917 , szTmp,
918 pProv->pszName, pProbe->pszMangledName,
919 szTmp);
920 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
921 {
922 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
923 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
924 else
925 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
926 }
927 ScmStreamPrintf(pStrm,
928 ") \\\n"
929 " do { \\\n"
930 " if (RT_UNLIKELY(g_fVTGProbeEnabled_%s_%s)) \\\n"
931 " { \\\n"
932 " VTG_DECL_VTGPROBELOC(s_VTGProbeLoc) = \\\n"
933 " { __LINE__, 0, UINT32_MAX, __PRETTY_FUNCTION__, __FILE__, &g_VTGProbeData_%s_%s }; \\\n"
934 " VTGProbeStub_%s_%s(&s_VTGProbeLoc",
935 pProv->pszName, pProbe->pszMangledName,
936 pProv->pszName, pProbe->pszMangledName,
937 pProv->pszName, pProbe->pszMangledName);
938 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
939 {
940 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
941 }
942 ScmStreamPrintf(pStrm,
943 "); \\\n"
944 " } \\\n"
945 " { \\\n" );
946 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
947 {
948 ScmStreamPrintf(pStrm,
949 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
950 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
951 pArg->pszName,
952 pArg->pszType);
953 }
954 ScmStreamPrintf(pStrm,
955 " } \\\n"
956 " } while (0)\n"
957 "\n");
958 }
959 }
960
961 ScmStreamPrintf(pStrm,
962 "\n"
963 "#else\n"
964 "\n");
965 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
966 {
967 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
968 {
969 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
970 ScmStreamPrintf(pStrm,
971 "# define %s_ENABLED() (false)\n"
972 "# define %s("
973 , szTmp, szTmp);
974 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
975 {
976 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
977 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
978 else
979 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
980 }
981 ScmStreamPrintf(pStrm,
982 ") do { } while (0)\n");
983 }
984 }
985
986 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
987 "#endif\n"
988 "\n"
989 "RT_C_DECLS_END\n"
990 "#endif\n"));
991 return RTEXITCODE_SUCCESS;
992}
993
994
995static RTEXITCODE generateHeader(const char *pszHeader)
996{
997 return generateFile(pszHeader, "header", generateHeaderInner);
998}
999
1000/**
1001 * If the given C word is at off - 1, return @c true and skip beyond it,
1002 * otherwise return @c false.
1003 *
1004 * @retval true if the given C-word is at the current position minus one char.
1005 * The stream position changes.
1006 * @retval false if not. The stream position is unchanged.
1007 *
1008 * @param pStream The stream.
1009 * @param cchWord The length of the word.
1010 * @param pszWord The word.
1011 */
1012bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
1013{
1014 /* Check stream state. */
1015 AssertReturn(!pStream->fWriteOrRead, false);
1016 AssertReturn(RT_SUCCESS(pStream->rc), false);
1017 AssertReturn(pStream->fFullyLineated, false);
1018
1019 /* Sufficient chars left on the line? */
1020 size_t const iLine = pStream->iLine;
1021 AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
1022 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
1023 if (cchWord > cchLeft)
1024 return false;
1025
1026 /* Do they match? */
1027 const char *psz = &pStream->pch[pStream->off - 1];
1028 if (memcmp(psz, pszWord, cchWord))
1029 return false;
1030
1031 /* Is it the end of a C word? */
1032 if (cchWord < cchLeft)
1033 {
1034 psz += cchWord;
1035 if (RT_C_IS_ALNUM(*psz) || *psz == '_')
1036 return false;
1037 }
1038
1039 /* Skip ahead. */
1040 pStream->off += cchWord - 1;
1041 return true;
1042}
1043
1044/**
1045 * Get's the C word starting at the current position.
1046 *
1047 * @returns Pointer to the word on success and the stream position advanced to
1048 * the end of it.
1049 * NULL on failure, stream position normally unchanged.
1050 * @param pStream The stream to get the C word from.
1051 * @param pcchWord Where to return the word length.
1052 */
1053const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
1054{
1055 /* Check stream state. */
1056 AssertReturn(!pStream->fWriteOrRead, NULL);
1057 AssertReturn(RT_SUCCESS(pStream->rc), NULL);
1058 AssertReturn(pStream->fFullyLineated, NULL);
1059
1060 /* Get the number of chars left on the line and locate the current char. */
1061 size_t const iLine = pStream->iLine;
1062 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
1063 const char *psz = &pStream->pch[pStream->off];
1064
1065 /* Is it a leading C character. */
1066 if (!RT_C_IS_ALPHA(*psz) && *psz == '_')
1067 return NULL;
1068
1069 /* Find the end of the word. */
1070 char ch;
1071 size_t off = 1;
1072 while ( off < cchLeft
1073 && ( (ch = psz[off]) == '_'
1074 || RT_C_IS_ALNUM(ch)))
1075 off++;
1076
1077 pStream->off += off;
1078 *pcchWord = off;
1079 return psz;
1080}
1081
1082
1083/**
1084 * Get's the C word starting at the current position minus one.
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 *ScmStreamCGetWordM1(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 - 1);
1102 const char *psz = &pStream->pch[pStream->off - 1];
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 - 1;
1117 *pcchWord = off;
1118 return psz;
1119}
1120
1121
1122/**
1123 * Parser error with line and position.
1124 *
1125 * @returns RTEXITCODE_FAILURE.
1126 * @param pStrm The stream.
1127 * @param cb The offset from the current position to the
1128 * point of failure.
1129 * @param pszMsg The message to display.
1130 */
1131static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t cb, const char *pszMsg)
1132{
1133 if (cb)
1134 ScmStreamSeekRelative(pStrm, -(ssize_t)cb);
1135 size_t const off = ScmStreamTell(pStrm);
1136 size_t const iLine = ScmStreamTellLine(pStrm);
1137 ScmStreamSeekByLine(pStrm, iLine);
1138 size_t const offLine = ScmStreamTell(pStrm);
1139
1140 RTPrintf("%s:%d:%zd: error: %s.\n", g_pszScript, iLine + 1, off - offLine + 1, pszMsg);
1141
1142 size_t cchLine;
1143 SCMEOL enmEof;
1144 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
1145 if (pszLine)
1146 RTPrintf(" %.*s\n"
1147 " %*s^\n",
1148 cchLine, pszLine, off - offLine, "");
1149 return RTEXITCODE_FAILURE;
1150}
1151
1152
1153/**
1154 * Parser error with line and position.
1155 *
1156 * @returns RTEXITCODE_FAILURE.
1157 * @param pStrm The stream.
1158 * @param cb The offset from the current position to the
1159 * point of failure.
1160 * @param pszMsg The message to display.
1161 */
1162static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszMsg)
1163{
1164 ScmStreamSeekAbsolute(pStrm, off);
1165 return parseError(pStrm, 0, pszMsg);
1166}
1167
1168/**
1169 * Handles a C++ one line comment.
1170 *
1171 * @returns Exit code.
1172 * @param pStrm The stream.
1173 */
1174static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
1175{
1176 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
1177 return RTEXITCODE_SUCCESS;
1178}
1179
1180/**
1181 * Handles a multi-line C/C++ comment.
1182 *
1183 * @returns Exit code.
1184 * @param pStrm The stream.
1185 */
1186static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
1187{
1188 unsigned ch;
1189 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1190 {
1191 if (ch == '*')
1192 {
1193 do
1194 ch = ScmStreamGetCh(pStrm);
1195 while (ch == '*');
1196 if (ch == '/')
1197 return RTEXITCODE_SUCCESS;
1198 }
1199 }
1200
1201 parseError(pStrm, 1, "Expected end of comment, got end of file");
1202 return RTEXITCODE_FAILURE;
1203}
1204
1205
1206/**
1207 * Skips spaces and comments.
1208 *
1209 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
1210 * @param pStrm The stream..
1211 */
1212static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
1213{
1214 unsigned ch;
1215 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
1216 {
1217 if (!RT_C_IS_SPACE(ch) && ch != '/')
1218 return RTEXITCODE_SUCCESS;
1219 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
1220 if (ch == '/')
1221 {
1222 ch = ScmStreamGetCh(pStrm);
1223 RTEXITCODE rcExit;
1224 if (ch == '*')
1225 rcExit = parseMultiLineComment(pStrm);
1226 else if (ch == '/')
1227 rcExit = parseOneLineComment(pStrm);
1228 else
1229 rcExit = parseError(pStrm, 2, "Unexpected character");
1230 if (rcExit != RTEXITCODE_SUCCESS)
1231 return rcExit;
1232 }
1233 }
1234
1235 return parseError(pStrm, 0, "Unexpected end of file");
1236}
1237
1238
1239/**
1240 * Skips spaces and comments, returning the next character.
1241 *
1242 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
1243 * failure.
1244 * @param pStrm The stream.
1245 */
1246static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
1247{
1248 unsigned ch;
1249 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1250 {
1251 if (!RT_C_IS_SPACE(ch) && ch != '/')
1252 return ch;
1253 if (ch == '/')
1254 {
1255 ch = ScmStreamGetCh(pStrm);
1256 RTEXITCODE rcExit;
1257 if (ch == '*')
1258 rcExit = parseMultiLineComment(pStrm);
1259 else if (ch == '/')
1260 rcExit = parseOneLineComment(pStrm);
1261 else
1262 rcExit = parseError(pStrm, 2, "Unexpected character");
1263 if (rcExit != RTEXITCODE_SUCCESS)
1264 return ~(unsigned)0;
1265 }
1266 }
1267
1268 parseError(pStrm, 0, "Unexpected end of file");
1269 return ~(unsigned)0;
1270}
1271
1272
1273/**
1274 * Get the next non-space-non-comment character on a preprocessor line.
1275 *
1276 * @returns The next character. On error message and ~(unsigned)0.
1277 * @param pStrm The stream.
1278 */
1279static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
1280{
1281 size_t off = ScmStreamTell(pStrm) - 1;
1282 unsigned ch;
1283 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1284 {
1285 if (RT_C_IS_SPACE(ch))
1286 {
1287 if (ch == '\n' || ch == '\r')
1288 {
1289 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
1290 break;
1291 }
1292 }
1293 else if (ch == '\\')
1294 {
1295 size_t off2 = ScmStreamTell(pStrm) - 1;
1296 ch = ScmStreamGetCh(pStrm);
1297 if (ch == '\r')
1298 ch = ScmStreamGetCh(pStrm);
1299 if (ch != '\n')
1300 {
1301 parseErrorAbs(pStrm, off2, "Expected new line");
1302 break;
1303 }
1304 }
1305 else
1306 return ch;
1307 }
1308 return ~(unsigned)0;
1309}
1310
1311
1312
1313/**
1314 * Skips spaces and comments.
1315 *
1316 * @returns Same as ScmStreamCGetWord
1317 * @param pStrm The stream..
1318 * @param pcchWord Where to return the length.
1319 */
1320static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
1321{
1322 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
1323 return NULL;
1324 return ScmStreamCGetWord(pStrm, pcchWord);
1325}
1326
1327
1328
1329/**
1330 * Parses interface stability.
1331 *
1332 * @returns Interface stability if parsed correctly, otherwise error message and
1333 * kVTGStability_Invalid.
1334 * @param pStrm The stream.
1335 * @param ch The first character in the stability spec.
1336 */
1337static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
1338{
1339 switch (ch)
1340 {
1341 case 'E':
1342 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
1343 return kVTGStability_External;
1344 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
1345 return kVTGStability_Evolving;
1346 break;
1347 case 'I':
1348 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
1349 return kVTGStability_Internal;
1350 break;
1351 case 'O':
1352 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
1353 return kVTGStability_Obsolete;
1354 break;
1355 case 'P':
1356 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
1357 return kVTGStability_Private;
1358 break;
1359 case 'S':
1360 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
1361 return kVTGStability_Stable;
1362 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
1363 return kVTGStability_Standard;
1364 break;
1365 case 'U':
1366 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
1367 return kVTGStability_Unstable;
1368 break;
1369 }
1370 parseError(pStrm, 1, "Unknown stability specifier");
1371 return kVTGStability_Invalid;
1372}
1373
1374
1375/**
1376 * Parses data depndency class.
1377 *
1378 * @returns Data dependency class if parsed correctly, otherwise error message
1379 * and kVTGClass_Invalid.
1380 * @param pStrm The stream.
1381 * @param ch The first character in the stability spec.
1382 */
1383static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
1384{
1385 switch (ch)
1386 {
1387 case 'C':
1388 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
1389 return kVTGClass_Common;
1390 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
1391 return kVTGClass_Cpu;
1392 break;
1393 case 'G':
1394 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
1395 return kVTGClass_Group;
1396 break;
1397 case 'I':
1398 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
1399 return kVTGClass_Isa;
1400 break;
1401 case 'P':
1402 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
1403 return kVTGClass_Platform;
1404 break;
1405 case 'U':
1406 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
1407 return kVTGClass_Unknown;
1408 break;
1409 }
1410 parseError(pStrm, 1, "Unknown data dependency class specifier");
1411 return kVTGClass_Invalid;
1412}
1413
1414/**
1415 * Parses a pragma D attributes statement.
1416 *
1417 * @returns Suitable exit code, errors message already written on failure.
1418 * @param pStrm The stream.
1419 */
1420static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
1421{
1422 /*
1423 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
1424 */
1425 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1426 if (ch == ~(unsigned)0)
1427 return RTEXITCODE_FAILURE;
1428
1429 kVTGStability enmCode = parseStability(pStrm, ch);
1430 if (enmCode == kVTGStability_Invalid)
1431 return RTEXITCODE_FAILURE;
1432 ch = ScmStreamGetCh(pStrm);
1433 if (ch != '/')
1434 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
1435
1436 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
1437 if (enmData == kVTGStability_Invalid)
1438 return RTEXITCODE_FAILURE;
1439 ch = ScmStreamGetCh(pStrm);
1440 if (ch != '/')
1441 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
1442
1443 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
1444 if (enmDataDep == kVTGClass_Invalid)
1445 return RTEXITCODE_FAILURE;
1446
1447 /*
1448 * Expecting 'provider' followed by the name of an provider defined earlier.
1449 */
1450 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1451 if (ch == ~(unsigned)0)
1452 return RTEXITCODE_FAILURE;
1453 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
1454 return parseError(pStrm, 1, "Expected 'provider'");
1455
1456 size_t cchName;
1457 const char *pszName = parseGetNextCWord(pStrm, &cchName);
1458 if (!pszName)
1459 return parseError(pStrm, 1, "Expected provider name");
1460
1461 PVTGPROVIDER pProv;
1462 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1463 {
1464 if ( !strncmp(pProv->pszName, pszName, cchName)
1465 && pProv->pszName[cchName] == '\0')
1466 break;
1467 }
1468 if (RTListNodeIsDummy(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry))
1469 return parseError(pStrm, cchName, "Provider not found");
1470
1471 /*
1472 * Which aspect of the provider?
1473 */
1474 size_t cchAspect;
1475 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
1476 if (!pszAspect)
1477 return parseError(pStrm, 1, "Expected provider aspect");
1478
1479 PVTGATTRS pAttrs;
1480 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
1481 pAttrs = &pProv->AttrSelf;
1482 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
1483 pAttrs = &pProv->AttrFunctions;
1484 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
1485 pAttrs = &pProv->AttrModules;
1486 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
1487 pAttrs = &pProv->AttrName;
1488 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
1489 pAttrs = &pProv->AttrArguments;
1490 else
1491 return parseError(pStrm, cchAspect, "Unknown aspect");
1492
1493 if (pAttrs->enmCode != kVTGStability_Invalid)
1494 return parseError(pStrm, cchAspect, "You have already specified these attributes");
1495
1496 pAttrs->enmCode = enmCode;
1497 pAttrs->enmData = enmData;
1498 pAttrs->enmDataDep = enmDataDep;
1499 return RTEXITCODE_SUCCESS;
1500}
1501
1502/**
1503 * Parses a D pragma statement.
1504 *
1505 * @returns Suitable exit code, errors message already written on failure.
1506 * @param pStrm The stream.
1507 */
1508static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
1509{
1510 RTEXITCODE rcExit;
1511 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1512 if (ch == ~(unsigned)0)
1513 rcExit = RTEXITCODE_FAILURE;
1514 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
1515 {
1516 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1517 if (ch == ~(unsigned)0)
1518 rcExit = RTEXITCODE_FAILURE;
1519 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
1520 rcExit = parsePragmaDAttributes(pStrm);
1521 else
1522 rcExit = parseError(pStrm, 1, "Unknown pragma D");
1523 }
1524 else
1525 rcExit = parseError(pStrm, 1, "Unknown pragma");
1526 return rcExit;
1527}
1528
1529
1530/**
1531 * Unmangles the probe name.
1532 *
1533 * This involves translating double underscore to dash.
1534 *
1535 * @returns Pointer to the unmangled name in the string table.
1536 * @param pszMangled The mangled name.
1537 */
1538static const char *parseUnmangleProbeName(const char *pszMangled)
1539{
1540 size_t cchMangled = strlen(pszMangled);
1541 char *pszTmp = (char *)alloca(cchMangled + 2);
1542 const char *pszSrc = pszMangled;
1543 char *pszDst = pszTmp;
1544
1545 while (*pszSrc)
1546 {
1547 if (pszSrc[0] == '_' && pszSrc[1] == '_' && pszSrc[2] != '_')
1548 {
1549 *pszDst++ = '-';
1550 pszSrc += 2;
1551 }
1552 else
1553 *pszDst++ = *pszSrc++;
1554 }
1555 *pszDst = '\0';
1556
1557 return strtabInsertN(pszTmp, pszDst - pszTmp);
1558}
1559
1560
1561/**
1562 * Parses a D probe statement.
1563 *
1564 * @returns Suitable exit code, errors message already written on failure.
1565 * @param pStrm The stream.
1566 * @param pProv The provider being parsed.
1567 */
1568static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
1569{
1570 /*
1571 * Next up is a name followed by an opening parenthesis.
1572 */
1573 size_t cchProbe;
1574 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
1575 if (!pszProbe)
1576 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
1577 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1578 if (ch != '(')
1579 return parseError(pStrm, 1, "Expected '(' after the probe name");
1580
1581 /*
1582 * Create a probe instance.
1583 */
1584 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
1585 if (!pProbe)
1586 return parseError(pStrm, 0, "Out of memory");
1587 RTListInit(&pProbe->ArgHead);
1588 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
1589 pProbe->offArgList = UINT32_MAX;
1590 pProbe->pszMangledName = RTStrDupN(pszProbe, cchProbe);
1591 if (!pProbe->pszMangledName)
1592 return parseError(pStrm, 0, "Out of memory");
1593 pProbe->pszUnmangledName = parseUnmangleProbeName(pProbe->pszMangledName);
1594 if (!pProbe->pszUnmangledName)
1595 return parseError(pStrm, 0, "Out of memory");
1596
1597 /*
1598 * Parse loop for the argument.
1599 */
1600 PVTGARG pArg = NULL;
1601 size_t cchName = 0;
1602 size_t cchArg = 0;
1603 char szArg[4096];
1604 for (;;)
1605 {
1606 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1607 switch (ch)
1608 {
1609 case ')':
1610 case ',':
1611 {
1612 /* commit the argument */
1613 if (pArg)
1614 {
1615 if (!cchName)
1616 return parseError(pStrm, 1, "Argument has no name");
1617 if (cchArg - cchName - 1 >= 128)
1618 return parseError(pStrm, 1, "Argument type too long");
1619 pArg->pszType = strtabInsertN(szArg, cchArg - cchName - 1);
1620 pArg->pszName = strtabInsertN(&szArg[cchArg - cchName], cchName);
1621 if (!pArg->pszType || !pArg->pszName)
1622 return parseError(pStrm, 1, "Out of memory");
1623 pArg = NULL;
1624 cchName = cchArg = 0;
1625 }
1626 if (ch == ')')
1627 {
1628 size_t off = ScmStreamTell(pStrm);
1629 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1630 if (ch != ';')
1631 return parseErrorAbs(pStrm, off, "Expected ';'");
1632 return RTEXITCODE_SUCCESS;
1633 }
1634 break;
1635 }
1636
1637 default:
1638 {
1639 size_t cchWord;
1640 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
1641 if (!pszWord)
1642 return parseError(pStrm, 0, "Expected argument");
1643 if (!pArg)
1644 {
1645 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
1646 if (!pArg)
1647 return parseError(pStrm, 1, "Out of memory");
1648 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
1649 pProbe->cArgs++;
1650
1651 if (cchWord + 1 > sizeof(szArg))
1652 return parseError(pStrm, 1, "Too long parameter declaration");
1653 memcpy(szArg, pszWord, cchWord);
1654 szArg[cchWord] = '\0';
1655 cchArg = cchWord;
1656 cchName = 0;
1657 }
1658 else
1659 {
1660 if (cchArg + 1 + cchWord + 1 > sizeof(szArg))
1661 return parseError(pStrm, 1, "Too long parameter declaration");
1662
1663 szArg[cchArg++] = ' ';
1664 memcpy(&szArg[cchArg], pszWord, cchWord);
1665 cchArg += cchWord;
1666 szArg[cchArg] = '\0';
1667 cchName = cchWord;
1668 }
1669 break;
1670 }
1671
1672 case '*':
1673 {
1674 if (!pArg)
1675 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
1676 if (cchArg + sizeof(" *") >= sizeof(szArg))
1677 return parseError(pStrm, 1, "Too long parameter declaration");
1678 szArg[cchArg++] = ' ';
1679 szArg[cchArg++] = '*';
1680 szArg[cchArg ] = '\0';
1681 cchName = 0;
1682 break;
1683 }
1684
1685 case ~(unsigned)0:
1686 return parseError(pStrm, 0, "Missing closing ')' on probe");
1687 }
1688 }
1689}
1690
1691/**
1692 * Parses a D provider statement.
1693 *
1694 * @returns Suitable exit code, errors message already written on failure.
1695 * @param pStrm The stream.
1696 */
1697static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
1698{
1699 /*
1700 * Next up is a name followed by a curly bracket. Ignore comments.
1701 */
1702 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
1703 if (rcExit != RTEXITCODE_SUCCESS)
1704 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
1705 size_t cchName;
1706 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
1707 if (!pszName)
1708 return parseError(pStrm, 0, "Bad provider name");
1709 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
1710 return parseError(pStrm, 1, "A provider name cannot end with digit");
1711
1712 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1713 if (ch != '{')
1714 return parseError(pStrm, 1, "Expected '{' after the provider name");
1715
1716 /*
1717 * Create a provider instance.
1718 */
1719 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
1720 if (!pProv)
1721 return parseError(pStrm, 0, "Out of memory");
1722 RTListInit(&pProv->ProbeHead);
1723 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
1724 pProv->pszName = strtabInsertN(pszName, cchName);
1725 if (!pProv->pszName)
1726 return parseError(pStrm, 0, "Out of memory");
1727
1728 /*
1729 * Parse loop.
1730 */
1731 for (;;)
1732 {
1733 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1734 switch (ch)
1735 {
1736 case 'p':
1737 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
1738 rcExit = parseProbe(pStrm, pProv);
1739 else
1740 rcExit = parseError(pStrm, 1, "Unexpected character");
1741 break;
1742
1743 case '}':
1744 {
1745 size_t off = ScmStreamTell(pStrm);
1746 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1747 if (ch == ';')
1748 return RTEXITCODE_SUCCESS;
1749 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
1750 break;
1751 }
1752
1753 case ~(unsigned)0:
1754 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
1755 break;
1756
1757 default:
1758 rcExit = parseError(pStrm, 1, "Unexpected character");
1759 break;
1760 }
1761 if (rcExit != RTEXITCODE_SUCCESS)
1762 return rcExit;
1763 }
1764}
1765
1766
1767static RTEXITCODE parseScript(const char *pszScript)
1768{
1769 SCMSTREAM Strm;
1770 int rc = ScmStreamInitForReading(&Strm, pszScript);
1771 if (RT_FAILURE(rc))
1772 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
1773 if (g_cVerbosity > 0)
1774 RTMsgInfo("Parsing '%s'...", pszScript);
1775
1776 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1777 unsigned ch;
1778 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
1779 {
1780 if (RT_C_IS_SPACE(ch))
1781 continue;
1782 switch (ch)
1783 {
1784 case '/':
1785 ch = ScmStreamGetCh(&Strm);
1786 if (ch == '*')
1787 rcExit = parseMultiLineComment(&Strm);
1788 else if (ch == '/')
1789 rcExit = parseOneLineComment(&Strm);
1790 else
1791 rcExit = parseError(&Strm, 2, "Unexpected character");
1792 break;
1793
1794 case 'p':
1795 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
1796 rcExit = parseProvider(&Strm);
1797 else
1798 rcExit = parseError(&Strm, 1, "Unexpected character");
1799 break;
1800
1801 case '#':
1802 {
1803 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
1804 if (ch == ~(unsigned)0)
1805 rcExit = RTEXITCODE_FAILURE;
1806 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
1807 rcExit = parsePragma(&Strm);
1808 else
1809 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
1810 break;
1811 }
1812
1813 default:
1814 rcExit = parseError(&Strm, 1, "Unexpected character");
1815 break;
1816 }
1817 if (rcExit != RTEXITCODE_SUCCESS)
1818 return rcExit;
1819 }
1820
1821 ScmStreamDelete(&Strm);
1822 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
1823 RTMsgInfo("Successfully parsed '%s'.", pszScript);
1824 return rcExit;
1825}
1826
1827
1828/**
1829 * Parses the arguments.
1830 */
1831static RTEXITCODE parseArguments(int argc, char **argv)
1832{
1833 /*
1834 * Set / Adjust defaults.
1835 */
1836 int rc = RTPathAbs(g_pszAssemblerIncVal, g_szAssemblerIncVal, sizeof(g_szAssemblerIncVal) - 1);
1837 if (RT_FAILURE(rc))
1838 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed: %Rrc", rc);
1839 strcat(g_szAssemblerIncVal, "/");
1840 g_pszAssemblerIncVal = g_szAssemblerIncVal;
1841
1842 /*
1843 * Option config.
1844 */
1845 enum
1846 {
1847 kVBoxTpGOpt_32Bit = 1000,
1848 kVBoxTpGOpt_64Bit,
1849 kVBoxTpGOpt_Assembler,
1850 kVBoxTpGOpt_AssemblerFmtOpt,
1851 kVBoxTpGOpt_AssemblerFmtVal,
1852 kVBoxTpGOpt_AssemblerOutputOpt,
1853 kVBoxTpGOpt_AssemblerOption,
1854 kVBoxTpGOpt_ProbeFnName,
1855 kVBoxTpGOpt_ProbeFnImported,
1856 kVBoxTpGOpt_End
1857 };
1858
1859 static RTGETOPTDEF const s_aOpts[] =
1860 {
1861 /* dtrace w/ long options */
1862 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
1863 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
1864 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
1865 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
1866 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
1867 { "--output", 'o', RTGETOPT_REQ_STRING },
1868 { "--script", 's', RTGETOPT_REQ_STRING },
1869 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1870 /* out stuff */
1871 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
1872 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
1873 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
1874 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
1875 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
1876 { "--probe-fn-name", kVBoxTpGOpt_ProbeFnName, RTGETOPT_REQ_STRING },
1877 { "--probe-fn-imported", kVBoxTpGOpt_ProbeFnImported, RTGETOPT_REQ_BOOL },
1878 };
1879
1880 RTGETOPTUNION ValueUnion;
1881 RTGETOPTSTATE GetOptState;
1882 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1883 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
1884
1885 /*
1886 * Process \the options.
1887 */
1888 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1889 {
1890 switch (rc)
1891 {
1892 /*
1893 * DTrace compatible options.
1894 */
1895 case kVBoxTpGOpt_32Bit:
1896 g_cBits = 32;
1897 g_pszAssemblerFmtVal = g_szAssemblerFmtVal32;
1898 break;
1899
1900 case kVBoxTpGOpt_64Bit:
1901 g_cBits = 64;
1902 g_pszAssemblerFmtVal = g_szAssemblerFmtVal64;
1903 break;
1904
1905 case 'C':
1906 g_fApplyCpp = true;
1907 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
1908 break;
1909
1910 case 'G':
1911 if ( g_enmAction != kVBoxTpGAction_Nothing
1912 && g_enmAction != kVBoxTpGAction_GenerateObject)
1913 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G and -h does not mix");
1914 g_enmAction = kVBoxTpGAction_GenerateObject;
1915 break;
1916
1917 case 'h':
1918 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
1919 {
1920 if ( g_enmAction != kVBoxTpGAction_Nothing
1921 && g_enmAction != kVBoxTpGAction_GenerateHeader)
1922 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h and -G does not mix");
1923 g_enmAction = kVBoxTpGAction_GenerateHeader;
1924 }
1925 else
1926 {
1927 /* --help or similar */
1928 RTPrintf("VirtualBox Tracepoint Generator\n"
1929 "\n"
1930 "Usage: %s [options]\n"
1931 "\n"
1932 "Options:\n", RTProcShortName());
1933 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
1934 if ((unsigned)s_aOpts[i].iShort < 128)
1935 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
1936 else
1937 RTPrintf(" %s\n", s_aOpts[i].pszLong);
1938 return RTEXITCODE_SUCCESS;
1939 }
1940 break;
1941
1942 case 'o':
1943 if (g_pszOutput)
1944 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
1945 g_pszOutput = ValueUnion.psz;
1946 break;
1947
1948 case 's':
1949 if (g_pszScript)
1950 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
1951 g_pszScript = ValueUnion.psz;
1952 break;
1953
1954 case 'v':
1955 g_cVerbosity++;
1956 break;
1957
1958 case 'V':
1959 {
1960 /* The following is assuming that svn does it's job here. */
1961 static const char s_szRev[] = "$Revision: 40826 $";
1962 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
1963 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
1964 return RTEXITCODE_SUCCESS;
1965 }
1966
1967 case VINF_GETOPT_NOT_OPTION:
1968 if (g_enmAction == kVBoxTpGAction_GenerateObject)
1969 break; /* object files, ignore them. */
1970 return RTGetOptPrintError(rc, &ValueUnion);
1971
1972
1973 /*
1974 * Our options.
1975 */
1976 case kVBoxTpGOpt_Assembler:
1977 g_pszAssembler = ValueUnion.psz;
1978 break;
1979
1980 case kVBoxTpGOpt_AssemblerFmtOpt:
1981 g_pszAssemblerFmtOpt = ValueUnion.psz;
1982 break;
1983
1984 case kVBoxTpGOpt_AssemblerFmtVal:
1985 g_pszAssemblerFmtVal = ValueUnion.psz;
1986 break;
1987
1988 case kVBoxTpGOpt_AssemblerOutputOpt:
1989 g_pszAssemblerOutputOpt = ValueUnion.psz;
1990 break;
1991
1992 case kVBoxTpGOpt_AssemblerOption:
1993 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
1994 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
1995 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
1996 g_cAssemblerOptions++;
1997 break;
1998
1999 case kVBoxTpGOpt_ProbeFnName:
2000 g_pszProbeFnName = ValueUnion.psz;
2001 break;
2002
2003 case kVBoxTpGOpt_ProbeFnImported:
2004 g_pszProbeFnName = ValueUnion.psz;
2005 break;
2006
2007 /*
2008 * Errors and bugs.
2009 */
2010 default:
2011 return RTGetOptPrintError(rc, &ValueUnion);
2012 }
2013 }
2014
2015 /*
2016 * Check that we've got all we need.
2017 */
2018 if (g_enmAction == kVBoxTpGAction_Nothing)
2019 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h or -G)");
2020 if (!g_pszScript)
2021 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
2022 if (!g_pszOutput)
2023 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
2024
2025 return RTEXITCODE_SUCCESS;
2026}
2027
2028
2029int main(int argc, char **argv)
2030{
2031 int rc = RTR3InitExe(argc, &argv, 0);
2032 if (RT_FAILURE(rc))
2033 return 1;
2034
2035 RTEXITCODE rcExit = parseArguments(argc, argv);
2036 if (rcExit == RTEXITCODE_SUCCESS)
2037 {
2038 /*
2039 * Parse the script.
2040 */
2041 RTListInit(&g_ProviderHead);
2042 rcExit = parseScript(g_pszScript);
2043 if (rcExit == RTEXITCODE_SUCCESS)
2044 {
2045 /*
2046 * Take action.
2047 */
2048 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
2049 rcExit = generateHeader(g_pszOutput);
2050 else
2051 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
2052 }
2053 }
2054
2055 return rcExit;
2056}
2057
Note: See TracBrowser for help on using the repository browser.

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