VirtualBox

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

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

Almost there... :-)

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