VirtualBox

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

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

string table and a bit more.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.8 KB
Line 
1/* $Id: VBoxTpG.cpp 40569 2012-03-21 16:13:52Z 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} VTGPROBE;
98typedef VTGPROBE *PVTGPROBE;
99
100typedef struct VTGPROVIDER
101{
102 RTLISTNODE ListEntry;
103 const char *pszName;
104
105 VTGATTRS AttrSelf;
106 VTGATTRS AttrModules;
107 VTGATTRS AttrFunctions;
108 VTGATTRS AttrName;
109 VTGATTRS AttrArguments;
110
111 RTLISTANCHOR ProbeHead;
112} VTGPROVIDER;
113typedef VTGPROVIDER *PVTGPROVIDER;
114
115/**
116 * A string table string.
117 */
118typedef struct VTGSTRING
119{
120 /** The string space core. */
121 RTSTRSPACECORE Core;
122 /** The string table offset. */
123 uint32_t offStrTab;
124 /** The actual string. */
125 char szString[1];
126} VTGSTRING;
127typedef VTGSTRING *PVTGSTRING;
128
129
130/*******************************************************************************
131* Global Variables *
132*******************************************************************************/
133/** The string space organizing the string table strings. Each node is a VTGSTRING. */
134static RTSTRSPACE g_StrSpace = NULL;
135/** Used by the string table enumerator to set VTGSTRING::offStrTab. */
136static uint32_t g_offStrTab;
137/** List of providers created by the parser. */
138static RTLISTANCHOR g_ProviderHead;
139
140/** @name Options
141 * @{ */
142static enum
143{
144 kVBoxTpGAction_Nothing,
145 kVBoxTpGAction_GenerateHeader,
146 kVBoxTpGAction_GenerateObject
147} g_enmAction = kVBoxTpGAction_Nothing;
148static uint32_t g_cBits = ARCH_BITS;
149static bool g_fApplyCpp = false;
150static uint32_t g_cVerbosity = 0;
151static const char *g_pszOutput = NULL;
152static const char *g_pszScript = NULL;
153static const char *g_pszTempAsm = NULL;
154#ifdef RT_OS_DARWIN
155static const char *g_pszAssembler = "yasm";
156static const char *g_pszAssemblerFmtOpt = "--oformat";
157static const char g_szAssemblerFmtVal32[] = "macho32";
158static const char g_szAssemblerFmtVal64[] = "macho64";
159#elif defined(RT_OS_OS2)
160static const char *pszAssembler = "nasm.exe";
161static const char *pszAssemblerFmtOpt = "-f";
162static const char g_szAssemblerFmtVal32[] = "obj";
163static const char g_szAssemblerFmtVal64[] = "elf64";
164#elif defined(RT_OS_WINDOWS)
165static const char *g_pszAssembler = "yasm.exe";
166static const char *g_pszAssemblerFmtOpt = "--oformat";
167static const char g_szAssemblerFmtVal32[] = "win32";
168static const char g_szAssemblerFmtVal64[] = "win64";
169#else
170static const char *g_pszAssembler = "yasm";
171static const char *g_pszAssemblerFmtOpt = "--oformat";
172static const char g_szAssemblerFmtVal32[] = "elf32";
173static const char g_szAssemblerFmtVal64[] = "elf64";
174#endif
175static const char *g_pszAssemblerFmtVal = RT_CONCAT(g_szAssemblerFmtVal, ARCH_BITS);
176static const char *g_pszAssemblerOutputOpt = "-o";
177static unsigned g_cAssemblerOptions = 0;
178static const char *g_apszAssemblerOptions[32];
179/** @} */
180
181
182/**
183 * Inserts a string into the string table, reusing any matching existing string
184 * if possible.
185 *
186 * @returns Read only string.
187 * @param pch The string to insert (need not be terminated).
188 * @param cch The length of the string.
189 */
190static const char *strtabInsertN(const char *pch, size_t cch)
191{
192 PVTGSTRING pStr = (PVTGSTRING)RTStrSpaceGetN(&g_StrSpace, pch, cch);
193 if (pStr)
194 return pStr->szString;
195
196 /*
197 * Create a new entry.
198 */
199 pStr = (PVTGSTRING)RTMemAlloc(RT_OFFSETOF(VTGSTRING, szString[cch + 1]));
200 if (!pStr)
201 return NULL;
202
203 pStr->Core.pszString = pStr->szString;
204 memcpy(pStr->szString, pch, cch);
205 pStr->szString[cch] = '\0';
206 pStr->offStrTab = UINT32_MAX;
207
208 bool fRc = RTStrSpaceInsert(&g_StrSpace, &pStr->Core);
209 Assert(fRc); NOREF(fRc);
210 return pStr->szString;
211}
212
213
214static RTEXITCODE generateInvokeAssembler(const char *pszOutput, const char *pszTempAsm)
215{
216 RTPrintf("Todo invoke the assembler\n");
217 return RTEXITCODE_SKIPPED;
218}
219
220
221static RTEXITCODE generateFile(const char *pszOutput, const char *pszWhat,
222 RTEXITCODE (*pfnGenerator)(PSCMSTREAM))
223{
224 SCMSTREAM Strm;
225 int rc = ScmStreamInitForWriting(&Strm, NULL);
226 if (RT_FAILURE(rc))
227 return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file",
228 rc, pszWhat);
229
230 RTEXITCODE rcExit = pfnGenerator(&Strm);
231 if (RT_FAILURE(ScmStreamGetStatus(&Strm)))
232 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Stream error %Rrc generating the %s file",
233 ScmStreamGetStatus(&Strm), pszWhat);
234 if (rcExit == RTEXITCODE_SUCCESS)
235 {
236 rc = ScmStreamWriteToFile(&Strm, "%s", pszOutput);
237 if (RT_FAILURE(rc))
238 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)",
239 rc, pszOutput, pszWhat);
240 if (rcExit == RTEXITCODE_SUCCESS)
241 {
242 if (g_cVerbosity > 0)
243 RTMsgInfo("Successfully generated '%s'.", pszOutput);
244 if (g_cVerbosity > 1)
245 {
246 RTMsgInfo("================ %s - start ================", pszWhat);
247 ScmStreamRewindForReading(&Strm);
248 const char *pszLine;
249 size_t cchLine;
250 SCMEOL enmEol;
251 while ((pszLine = ScmStreamGetLine(&Strm, &cchLine, &enmEol)) != NULL)
252 RTPrintf("%.*s\n", cchLine, pszLine);
253 RTMsgInfo("================ %s - end ================", pszWhat);
254 }
255 }
256 }
257 ScmStreamDelete(&Strm);
258 return rcExit;
259}
260
261static ssize_t ScmStreamPrintfV(PSCMSTREAM pStream, const char *pszFormat, va_list va)
262{
263 char *psz;
264 ssize_t cch = RTStrAPrintfV(&psz, pszFormat, va);
265 if (cch)
266 {
267 int rc = ScmStreamWrite(pStream, psz, cch);
268 RTStrFree(psz);
269 if (RT_FAILURE(rc))
270 cch = rc;
271 }
272 return cch;
273}
274
275static ssize_t ScmStreamPrintf(PSCMSTREAM pStream, const char *pszFormat, ...)
276{
277 va_list va;
278 va_start(va, pszFormat);
279 ssize_t cch = ScmStreamPrintfV(pStream, pszFormat, va);
280 va_end(va);
281 return cch;
282}
283
284
285/**
286 * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes the string table strings.}
287 */
288static DECLCALLBACK(int) generateAssemblyStrTabCallback(PRTSTRSPACECORE pStr, void *pvUser)
289{
290 PVTGSTRING pVtgStr = (PVTGSTRING)pStr;
291 PSCMSTREAM pStrm = (PSCMSTREAM)pvUser;
292
293 pVtgStr->offStrTab = g_offStrTab;
294 g_offStrTab += pVtgStr->Core.cchString + 1;
295
296 ScmStreamPrintf(pStrm,
297 " db '%s', 0 ; off=%u len=%zu\n",
298 pVtgStr->szString, pVtgStr->offStrTab, pVtgStr->Core.cchString);
299 return VINF_SUCCESS;
300}
301
302
303static RTEXITCODE generateAssembly(PSCMSTREAM pStrm)
304{
305 if (g_cVerbosity > 0)
306 RTMsgInfo("Generating assembly code...");
307
308 /*
309 * Write the file header.
310 */
311 ScmStreamPrintf(pStrm,
312 "; $Id: VBoxTpG.cpp 40569 2012-03-21 16:13:52Z vboxsync $ \n"
313 ";; @file\n"
314 "; Automatically generated from %s. Do NOT edit!\n"
315 ";\n"
316 "\n"
317 "%include \"iprt/asmdefs.h\"\n"
318 "\n"
319 "%ifdef ASM_FORMAT_OMF\n"
320 " segment VTGObject public CLASS=VTGObject align=16 use32"
321 ,
322 g_pszScript);
323
324 /*
325 * Dump the string table to set the offsets before we use them anywhere.
326 */
327 ScmStreamPrintf(pStrm,
328 ";\n"
329 "; The string table.\n"
330 ";\n"
331 "BEGINDATA\n"
332 "GLOBALNAME g_achVTGStringTable\n");
333 g_offStrTab = 0;
334 RTStrSpaceEnumerate(&g_StrSpace, generateAssemblyStrTabCallback, pStrm);
335 ScmStreamPrintf(pStrm,
336 "GLOBALNAME g_achVTGStringTable_End\n");
337
338 /*
339 * Declare the probe enable flags.
340 */
341 ScmStreamPrintf(pStrm,
342 ";\n"
343 "; Probe enabled flags. Since these will be accessed all the time\n"
344 "; they are placed together to get some more cache and TLB hits when\n"
345 "; the probes are disabled."
346 ";\n"
347 "BEGINDATA\n"
348 "ALIGNDATA(16)\n"
349 "GLOBALNAME g_afVTGProbeEnabled\n"
350 );
351 PVTGPROVIDER pProv;
352 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
353 {
354 PVTGPROBE pProbe;
355 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
356 {
357 ScmStreamPrintf(pStrm,
358 "GLOBALNAME g_fVTGProbeEnabled_%s_%s\n"
359 " db 0\n",
360 pProv->pszName, pProbe->pszName);
361 }
362 }
363 ScmStreamPrintf(pStrm, "GLOBALNAME g_afVTGProbeEnabled_End\n");
364
365 /*
366 * Declare the probe data.
367 */
368 ScmStreamPrintf(pStrm,
369 "\n"
370 ";\n"
371 "; Prob data.\n"
372 ";\n"
373 "BEGINDATA\n"
374 "GLOBALNAME g_abVTGProbeData\n"
375 "\n");
376 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
377 {
378 PVTGPROBE pProbe;
379 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
380 {
381 /** @todo */
382 //ScmStreamPrintf(pStrm,
383 // "GLOBALNAME g_fVTGProbeEnabled_%s_%s\n"
384 // " db 0\n",
385 // pProv->pszName, pProbe->pszName);
386 }
387 }
388 ScmStreamPrintf(pStrm, "GLOBALNAME g_abVTGProbeData_End\n");
389
390 return RTEXITCODE_SUCCESS;
391}
392
393
394static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm)
395{
396 if (!pszTempAsm)
397 {
398 size_t cch = strlen(pszOutput);
399 char *psz = (char *)alloca(cch + sizeof(".asm"));
400 memcpy(psz, pszOutput, cch);
401 memcpy(psz + cch, ".asm", sizeof(".asm"));
402 pszTempAsm = psz;
403 }
404
405 RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly);
406 if (rcExit == RTEXITCODE_SUCCESS)
407 rcExit = generateInvokeAssembler(pszOutput, pszTempAsm);
408 RTFileDelete(pszTempAsm);
409 return rcExit;
410}
411
412
413static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe)
414{
415 size_t cbMax = strlen(pszProvider) + 1 + strlen(pszProbe) + 1;
416 if (cbMax > cbBuf || cbMax > 80)
417 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider);
418
419 while (*pszProvider)
420 *pszBuf++ = RT_C_TO_UPPER(*pszProvider++);
421
422 *pszBuf++ = '_';
423
424 while (*pszProbe)
425 {
426 if (pszProbe[0] == '_' && pszProbe[1] == '_')
427 pszProbe++;
428 *pszBuf++ = RT_C_TO_UPPER(*pszProbe++);
429 }
430
431 *pszBuf = '\0';
432 return RTEXITCODE_SUCCESS;
433}
434
435static RTEXITCODE generateHeaderInner(PSCMSTREAM pStrm)
436{
437 /*
438 * Calc the double inclusion blocker define and then write the file header.
439 */
440 char szTmp[4096];
441 const char *pszName = RTPathFilename(g_pszScript);
442 size_t cchName = strlen(pszName);
443 if (cchName >= sizeof(szTmp) - 64)
444 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
445 szTmp[0] = '_';
446 szTmp[1] = '_';
447 szTmp[2] = '_';
448 memcpy(&szTmp[3], pszName, cchName);
449 szTmp[3 + cchName + 0] = '_';
450 szTmp[3 + cchName + 1] = '_';
451 szTmp[3 + cchName + 2] = '_';
452 szTmp[3 + cchName + 3] = '\0';
453 char *psz = &szTmp[3];
454 while (*psz)
455 {
456 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
457 *psz = '_';
458 psz++;
459 }
460
461 ScmStreamPrintf(pStrm,
462 "/* $Id: VBoxTpG.cpp 40569 2012-03-21 16:13:52Z vboxsync $ */\n"
463 "/** @file\n"
464 " * Automatically generated from %s. Do NOT edit!\n"
465 " */\n"
466 "\n"
467 "#ifndef %s\n"
468 "#define %s\n"
469 "\n"
470 "#include <iprt/types.h>\n"
471 "\n"
472 "#ifdef _MSC_VER\n"
473 "# define DECL_DATA_SECT(scope, type, name, sect) __declspec(allocate(#sect)) scope type name\n"
474 "#elif defined(__GNUC__)\n"
475 "# ifdef RT_OS_DARWIN\n"
476 "# define DECL_DATA_SECT(scope, type, name, sect) scope type __attribute__((section(#sect \",\" #sect))) name\n"
477 "# else\n"
478 "# define DECL_DATA_SECT(scope, type, name, sect) scope type __attribute__((section(#sect))) name\n"
479 "# endif\n"
480 "#else\n"
481 "# error portme\n"
482 "#endif\n"
483 "\n"
484 "typedef struct VBOXTPGPROBELOC\n"
485 "{\n"
486 " uint32_t uLine : 31;\n"
487 " uint32_t fEnabled : 1;\n"
488 " uint32_t idProbe;\n"
489 " const char *pszFunction;\n"
490 " const char *pszFile;\n"
491 " uint8_t *pbProbe;\n"
492 "} VBOXTPGPROBELOC;\n"
493 "\n"
494 "RT_C_DECLS_BEGIN\n"
495 ,
496 g_pszScript,
497 szTmp,
498 szTmp);
499
500 /*
501 * Declare data, code and macros for each probe.
502 */
503 PVTGPROVIDER pProv;
504 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
505 {
506 PVTGPROBE pProbe;
507 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
508 {
509 PVTGARG pArg;
510 ScmStreamPrintf(pStrm,
511 "extern bool g_fVTGProbeEnabled_%s_%s;\n"
512 "extern uint8_t g_VTGProbeData_%s_%s;\n"
513 "DECLASM(void) VTGProbeStub_%s_%s(VBOXTPGPROBELOC *",
514 pProv->pszName, pProbe->pszName,
515 pProv->pszName, pProbe->pszName,
516 pProv->pszName, pProbe->pszName);
517 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
518 {
519 ScmStreamPrintf(pStrm, ", %s", pArg->pszType);
520 }
521 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszName);
522 ScmStreamPrintf(pStrm,
523 ");\n"
524 "#define %s_ENABLED() \\\n"
525 " (RT_UNLIKELY(g_fVTGProbeEnabled_%s_%s)) \n"
526 "#define %s("
527 , szTmp,
528 pProv->pszName, pProbe->pszName,
529 szTmp);
530 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
531 {
532 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
533 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
534 else
535 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
536 }
537 ScmStreamPrintf(pStrm,
538 ") \\\n"
539 " do { \\\n"
540 " if (RT_UNLIKELY(/*g_fVTGProbeEnabled_%s_%s*/ true)) \\\n"
541 " { \\\n"
542 " DECL_DATA_SECT(static, VBOXTPGPROBELOC, s_VTGProbeLoc, VTGPrLc) = \\\n"
543 " { __LINE__, 0, UINT32_MAX, __PRETTY_FUNCTION__, __FILE__, /*&g_VTGProbeData_%s_%s*/ NULL }; \\\n"
544 " /*VTGProbeStub_%s_%s(&s_VTGProbeLoc",
545 pProv->pszName, pProbe->pszName,
546 pProv->pszName, pProbe->pszName,
547 pProv->pszName, pProbe->pszName);
548 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
549 {
550 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
551 }
552 ScmStreamPrintf(pStrm,
553 ");*/ RTAssertMsg2(\"%p\", &s_VTGProbeLoc); \\\n"
554 " } \\\n"
555 " } while (0)\n"
556 "\n");
557 }
558 }
559
560 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
561 "RT_C_DECLS_END\n"
562 "#endif\n"));
563 return RTEXITCODE_SUCCESS;
564}
565
566
567static RTEXITCODE generateHeader(const char *pszHeader)
568{
569 return generateFile(pszHeader, "header", generateHeaderInner);
570}
571
572/**
573 * If the given C word is at off - 1, return @c true and skip beyond it,
574 * otherwise return @c false.
575 *
576 * @retval true if the given C-word is at the current position minus one char.
577 * The stream position changes.
578 * @retval false if not. The stream position is unchanged.
579 *
580 * @param pStream The stream.
581 * @param cchWord The length of the word.
582 * @param pszWord The word.
583 */
584bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
585{
586 /* Check stream state. */
587 AssertReturn(!pStream->fWriteOrRead, false);
588 AssertReturn(RT_SUCCESS(pStream->rc), false);
589 AssertReturn(pStream->fFullyLineated, false);
590
591 /* Sufficient chars left on the line? */
592 size_t const iLine = pStream->iLine;
593 AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
594 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
595 if (cchWord > cchLeft)
596 return false;
597
598 /* Do they match? */
599 const char *psz = &pStream->pch[pStream->off - 1];
600 if (memcmp(psz, pszWord, cchWord))
601 return false;
602
603 /* Is it the end of a C word? */
604 if (cchWord < cchLeft)
605 {
606 psz += cchWord;
607 if (RT_C_IS_ALNUM(*psz) || *psz == '_')
608 return false;
609 }
610
611 /* Skip ahead. */
612 pStream->off += cchWord - 1;
613 return true;
614}
615
616/**
617 * Get's the C word starting at the current position.
618 *
619 * @returns Pointer to the word on success and the stream position advanced to
620 * the end of it.
621 * NULL on failure, stream position normally unchanged.
622 * @param pStream The stream to get the C word from.
623 * @param pcchWord Where to return the word length.
624 */
625const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
626{
627 /* Check stream state. */
628 AssertReturn(!pStream->fWriteOrRead, false);
629 AssertReturn(RT_SUCCESS(pStream->rc), false);
630 AssertReturn(pStream->fFullyLineated, false);
631
632 /* Get the number of chars left on the line and locate the current char. */
633 size_t const iLine = pStream->iLine;
634 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
635 const char *psz = &pStream->pch[pStream->off];
636
637 /* Is it a leading C character. */
638 if (!RT_C_IS_ALPHA(*psz) && !*psz == '_')
639 return NULL;
640
641 /* Find the end of the word. */
642 char ch;
643 size_t off = 1;
644 while ( off < cchLeft
645 && ( (ch = psz[off]) == '_'
646 || RT_C_IS_ALNUM(ch)))
647 off++;
648
649 pStream->off += off;
650 *pcchWord = off;
651 return psz;
652}
653
654
655/**
656 * Get's the C word starting at the current position minus one.
657 *
658 * @returns Pointer to the word on success and the stream position advanced to
659 * the end of it.
660 * NULL on failure, stream position normally unchanged.
661 * @param pStream The stream to get the C word from.
662 * @param pcchWord Where to return the word length.
663 */
664const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
665{
666 /* Check stream state. */
667 AssertReturn(!pStream->fWriteOrRead, false);
668 AssertReturn(RT_SUCCESS(pStream->rc), false);
669 AssertReturn(pStream->fFullyLineated, false);
670
671 /* Get the number of chars left on the line and locate the current char. */
672 size_t const iLine = pStream->iLine;
673 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
674 const char *psz = &pStream->pch[pStream->off - 1];
675
676 /* Is it a leading C character. */
677 if (!RT_C_IS_ALPHA(*psz) && !*psz == '_')
678 return NULL;
679
680 /* Find the end of the word. */
681 char ch;
682 size_t off = 1;
683 while ( off < cchLeft
684 && ( (ch = psz[off]) == '_'
685 || RT_C_IS_ALNUM(ch)))
686 off++;
687
688 pStream->off += off - 1;
689 *pcchWord = off;
690 return psz;
691}
692
693
694/**
695 * Parser error with line and position.
696 *
697 * @returns RTEXITCODE_FAILURE.
698 * @param pStrm The stream.
699 * @param cb The offset from the current position to the
700 * point of failure.
701 * @param pszMsg The message to display.
702 */
703static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t cb, const char *pszMsg)
704{
705 if (cb)
706 ScmStreamSeekRelative(pStrm, -cb);
707 size_t const off = ScmStreamTell(pStrm);
708 size_t const iLine = ScmStreamTellLine(pStrm);
709 ScmStreamSeekByLine(pStrm, iLine);
710 size_t const offLine = ScmStreamTell(pStrm);
711
712 RTPrintf("%s:%d:%zd: error: %s.\n", g_pszScript, iLine + 1, off - offLine + 1, pszMsg);
713
714 size_t cchLine;
715 SCMEOL enmEof;
716 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
717 if (pszLine)
718 RTPrintf(" %.*s\n"
719 " %*s^\n",
720 cchLine, pszLine, off - offLine, "");
721 return RTEXITCODE_FAILURE;
722}
723
724
725/**
726 * Parser error with line and position.
727 *
728 * @returns RTEXITCODE_FAILURE.
729 * @param pStrm The stream.
730 * @param cb The offset from the current position to the
731 * point of failure.
732 * @param pszMsg The message to display.
733 */
734static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszMsg)
735{
736 ScmStreamSeekAbsolute(pStrm, off);
737 return parseError(pStrm, 0, pszMsg);
738}
739
740/**
741 * Handles a C++ one line comment.
742 *
743 * @returns Exit code.
744 * @param pStrm The stream.
745 */
746static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
747{
748 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
749 return RTEXITCODE_SUCCESS;
750}
751
752/**
753 * Handles a multi-line C/C++ comment.
754 *
755 * @returns Exit code.
756 * @param pStrm The stream.
757 */
758static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
759{
760 unsigned ch;
761 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
762 {
763 if (ch == '*')
764 {
765 do
766 ch = ScmStreamGetCh(pStrm);
767 while (ch == '*');
768 if (ch == '/')
769 return RTEXITCODE_SUCCESS;
770 }
771 }
772
773 parseError(pStrm, 1, "Expected end of comment, got end of file");
774 return RTEXITCODE_FAILURE;
775}
776
777
778/**
779 * Skips spaces and comments.
780 *
781 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
782 * @param pStrm The stream..
783 */
784static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
785{
786 unsigned ch;
787 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
788 {
789 if (!RT_C_IS_SPACE(ch) && ch != '/')
790 return RTEXITCODE_SUCCESS;
791 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
792 if (ch == '/')
793 {
794 ch = ScmStreamGetCh(pStrm);
795 RTEXITCODE rcExit;
796 if (ch == '*')
797 rcExit = parseMultiLineComment(pStrm);
798 else if (ch == '/')
799 rcExit = parseOneLineComment(pStrm);
800 else
801 rcExit = parseError(pStrm, 2, "Unexpected character");
802 if (rcExit != RTEXITCODE_SUCCESS)
803 return rcExit;
804 }
805 }
806
807 return parseError(pStrm, 0, "Unexpected end of file");
808}
809
810
811/**
812 * Skips spaces and comments, returning the next character.
813 *
814 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
815 * failure.
816 * @param pStrm The stream.
817 */
818static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
819{
820 unsigned ch;
821 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
822 {
823 if (!RT_C_IS_SPACE(ch) && ch != '/')
824 return ch;
825 if (ch == '/')
826 {
827 ch = ScmStreamGetCh(pStrm);
828 RTEXITCODE rcExit;
829 if (ch == '*')
830 rcExit = parseMultiLineComment(pStrm);
831 else if (ch == '/')
832 rcExit = parseOneLineComment(pStrm);
833 else
834 rcExit = parseError(pStrm, 2, "Unexpected character");
835 if (rcExit != RTEXITCODE_SUCCESS)
836 return ~(unsigned)0;
837 }
838 }
839
840 parseError(pStrm, 0, "Unexpected end of file");
841 return ~(unsigned)0;
842}
843
844
845/**
846 * Get the next non-space-non-comment character on a preprocessor line.
847 *
848 * @returns The next character. On error message and ~(unsigned)0.
849 * @param pStrm The stream.
850 */
851static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
852{
853 size_t off = ScmStreamTell(pStrm) - 1;
854 unsigned ch;
855 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
856 {
857 if (RT_C_IS_SPACE(ch))
858 {
859 if (ch == '\n' || ch == '\r')
860 {
861 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
862 break;
863 }
864 }
865 else if (ch == '\\')
866 {
867 size_t off2 = ScmStreamTell(pStrm) - 1;
868 ch = ScmStreamGetCh(pStrm);
869 if (ch == '\r')
870 ch = ScmStreamGetCh(pStrm);
871 if (ch != '\n')
872 {
873 parseErrorAbs(pStrm, off2, "Expected new line");
874 break;
875 }
876 }
877 else
878 return ch;
879 }
880 return ~(unsigned)0;
881}
882
883
884
885/**
886 * Skips spaces and comments.
887 *
888 * @returns Same as ScmStreamCGetWord
889 * @param pStrm The stream..
890 * @param pcchWord Where to return the length.
891 */
892static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
893{
894 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
895 return NULL;
896 return ScmStreamCGetWord(pStrm, pcchWord);
897}
898
899
900
901/**
902 * Parses interface stability.
903 *
904 * @returns Interface stability if parsed correctly, otherwise error message and
905 * kVTGStability_Invalid.
906 * @param pStrm The stream.
907 * @param ch The first character in the stability spec.
908 */
909static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
910{
911 switch (ch)
912 {
913 case 'E':
914 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
915 return kVTGStability_External;
916 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
917 return kVTGStability_Evolving;
918 break;
919 case 'I':
920 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
921 return kVTGStability_Internal;
922 break;
923 case 'O':
924 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
925 return kVTGStability_Obsolete;
926 break;
927 case 'P':
928 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
929 return kVTGStability_Private;
930 break;
931 case 'S':
932 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
933 return kVTGStability_Stable;
934 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
935 return kVTGStability_Standard;
936 break;
937 case 'U':
938 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
939 return kVTGStability_Unstable;
940 break;
941 }
942 parseError(pStrm, 1, "Unknown stability specifier");
943 return kVTGStability_Invalid;
944}
945
946
947/**
948 * Parses data depndency class.
949 *
950 * @returns Data dependency class if parsed correctly, otherwise error message
951 * and kVTGClass_Invalid.
952 * @param pStrm The stream.
953 * @param ch The first character in the stability spec.
954 */
955static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
956{
957 switch (ch)
958 {
959 case 'C':
960 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
961 return kVTGClass_Common;
962 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
963 return kVTGClass_Cpu;
964 break;
965 case 'G':
966 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
967 return kVTGClass_Group;
968 break;
969 case 'I':
970 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
971 return kVTGClass_Isa;
972 break;
973 case 'P':
974 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
975 return kVTGClass_Platform;
976 break;
977 case 'U':
978 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
979 return kVTGClass_Unknown;
980 break;
981 }
982 parseError(pStrm, 1, "Unknown data dependency class specifier");
983 return kVTGClass_Invalid;
984}
985
986/**
987 * Parses a pragma D attributes statement.
988 *
989 * @returns Suitable exit code, errors message already written on failure.
990 * @param pStrm The stream.
991 */
992static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
993{
994 /*
995 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
996 */
997 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
998 if (ch == ~(unsigned)0)
999 return RTEXITCODE_FAILURE;
1000
1001 kVTGStability enmCode = parseStability(pStrm, ch);
1002 if (enmCode == kVTGStability_Invalid)
1003 return RTEXITCODE_FAILURE;
1004 ch = ScmStreamGetCh(pStrm);
1005 if (ch != '/')
1006 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
1007
1008 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
1009 if (enmData == kVTGStability_Invalid)
1010 return RTEXITCODE_FAILURE;
1011 ch = ScmStreamGetCh(pStrm);
1012 if (ch != '/')
1013 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
1014
1015 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
1016 if (enmDataDep == kVTGClass_Invalid)
1017 return RTEXITCODE_FAILURE;
1018
1019 /*
1020 * Expecting 'provider' followed by the name of an provider defined earlier.
1021 */
1022 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1023 if (ch == ~(unsigned)0)
1024 return RTEXITCODE_FAILURE;
1025 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
1026 return parseError(pStrm, 1, "Expected 'provider'");
1027
1028 size_t cchName;
1029 const char *pszName = parseGetNextCWord(pStrm, &cchName);
1030 if (!pszName)
1031 return parseError(pStrm, 1, "Expected provider name");
1032
1033 PVTGPROVIDER pProv;
1034 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1035 {
1036 if ( !strncmp(pProv->pszName, pszName, cchName)
1037 && pProv->pszName[cchName] == '\0')
1038 break;
1039 }
1040 if (!pProv)
1041 return parseError(pStrm, cchName, "Provider not found");
1042
1043 /*
1044 * Which aspect of the provider?
1045 */
1046 size_t cchAspect;
1047 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
1048 if (!pszAspect)
1049 return parseError(pStrm, 1, "Expected provider aspect");
1050
1051 PVTGATTRS pAttrs;
1052 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
1053 pAttrs = &pProv->AttrSelf;
1054 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
1055 pAttrs = &pProv->AttrFunctions;
1056 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
1057 pAttrs = &pProv->AttrModules;
1058 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
1059 pAttrs = &pProv->AttrName;
1060 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
1061 pAttrs = &pProv->AttrArguments;
1062 else
1063 return parseError(pStrm, cchAspect, "Unknown aspect");
1064
1065 if (pAttrs->enmCode != kVTGStability_Invalid)
1066 return parseError(pStrm, cchAspect, "You have already specified these attributes");
1067
1068 pAttrs->enmCode = enmCode;
1069 pAttrs->enmData = enmData;
1070 pAttrs->enmDataDep = enmDataDep;
1071 return RTEXITCODE_SUCCESS;
1072}
1073
1074/**
1075 * Parses a D pragma statement.
1076 *
1077 * @returns Suitable exit code, errors message already written on failure.
1078 * @param pStrm The stream.
1079 */
1080static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
1081{
1082 RTEXITCODE rcExit;
1083 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1084 if (ch == ~(unsigned)0)
1085 rcExit = RTEXITCODE_FAILURE;
1086 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
1087 {
1088 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1089 if (ch == ~(unsigned)0)
1090 rcExit = RTEXITCODE_FAILURE;
1091 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
1092 rcExit = parsePragmaDAttributes(pStrm);
1093 else
1094 rcExit = parseError(pStrm, 1, "Unknown pragma D");
1095 }
1096 else
1097 rcExit = parseError(pStrm, 1, "Unknown pragma");
1098 return rcExit;
1099}
1100
1101
1102/**
1103 * Parses a D probe statement.
1104 *
1105 * @returns Suitable exit code, errors message already written on failure.
1106 * @param pStrm The stream.
1107 * @param pProv The provider being parsed.
1108 */
1109static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
1110{
1111 /*
1112 * Next up is a name followed by an opening parenthesis.
1113 */
1114 size_t cchProbe;
1115 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
1116 if (!pszProbe)
1117 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
1118 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1119 if (ch != '(')
1120 return parseError(pStrm, 1, "Expected '(' after the probe name");
1121
1122 /*
1123 * Create a probe instance.
1124 */
1125 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
1126 if (!pProbe)
1127 return parseError(pStrm, 0, "Out of memory");
1128 RTListInit(&pProbe->ArgHead);
1129 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
1130 pProbe->pszName = strtabInsertN(pszProbe, cchProbe);
1131 if (!pProbe->pszName)
1132 return parseError(pStrm, 0, "Out of memory");
1133
1134 /*
1135 * Parse loop for the argument.
1136 */
1137 PVTGARG pArg = NULL;
1138 size_t cchName = 0;
1139 size_t cchArg = 0;
1140 char szArg[4096];
1141 for (;;)
1142 {
1143 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1144 switch (ch)
1145 {
1146 case ')':
1147 case ',':
1148 {
1149 /* commit the argument */
1150 if (pArg)
1151 {
1152 if (!cchName)
1153 return parseError(pStrm, 1, "Argument has no name");
1154 pArg->pszType = strtabInsertN(szArg, cchArg - cchName - 1);
1155 pArg->pszName = strtabInsertN(&szArg[cchArg - cchName], cchName);
1156 if (!pArg->pszType || !pArg->pszName)
1157 return parseError(pStrm, 1, "Out of memory");
1158 pArg = NULL;
1159 cchName = cchArg = 0;
1160 }
1161 if (ch == ')')
1162 {
1163 size_t off = ScmStreamTell(pStrm);
1164 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1165 if (ch != ';')
1166 return parseErrorAbs(pStrm, off, "Expected ';'");
1167 return RTEXITCODE_SUCCESS;
1168 }
1169 break;
1170 }
1171
1172 default:
1173 {
1174 size_t cchWord;
1175 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
1176 if (!pszWord)
1177 return parseError(pStrm, 0, "Expected argument");
1178 if (!pArg)
1179 {
1180 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
1181 if (!pArg)
1182 return parseError(pStrm, 1, "Out of memory");
1183 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
1184 pProbe->cArgs++;
1185
1186 if (cchWord + 1 > sizeof(szArg))
1187 return parseError(pStrm, 1, "Too long parameter declaration");
1188 memcpy(szArg, pszWord, cchWord);
1189 szArg[cchWord] = '\0';
1190 cchArg = cchWord;
1191 cchName = 0;
1192 }
1193 else
1194 {
1195 if (cchArg + 1 + cchWord + 1 > sizeof(szArg))
1196 return parseError(pStrm, 1, "Too long parameter declaration");
1197
1198 szArg[cchArg++] = ' ';
1199 memcpy(&szArg[cchArg], pszWord, cchWord);
1200 cchArg += cchWord;
1201 szArg[cchArg] = '\0';
1202 cchName = cchWord;
1203 }
1204 break;
1205 }
1206
1207 case '*':
1208 {
1209 if (!pArg)
1210 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
1211 if (cchArg + sizeof(" *") >= sizeof(szArg))
1212 return parseError(pStrm, 1, "Too long parameter declaration");
1213 szArg[cchArg++] = ' ';
1214 szArg[cchArg++] = '*';
1215 szArg[cchArg ] = '\0';
1216 cchName = 0;
1217 break;
1218 }
1219
1220 case ~(unsigned)0:
1221 return parseError(pStrm, 0, "Missing closing ')' on probe");
1222 }
1223 }
1224}
1225
1226/**
1227 * Parses a D provider statement.
1228 *
1229 * @returns Suitable exit code, errors message already written on failure.
1230 * @param pStrm The stream.
1231 */
1232static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
1233{
1234 /*
1235 * Next up is a name followed by a curly bracket. Ignore comments.
1236 */
1237 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
1238 if (rcExit != RTEXITCODE_SUCCESS)
1239 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
1240 size_t cchName;
1241 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
1242 if (!pszName)
1243 return parseError(pStrm, 0, "Bad provider name");
1244 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
1245 return parseError(pStrm, 1, "A provider name cannot end with digit");
1246
1247 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1248 if (ch != '{')
1249 return parseError(pStrm, 1, "Expected '{' after the provider name");
1250
1251 /*
1252 * Create a provider instance.
1253 */
1254 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
1255 if (!pProv)
1256 return parseError(pStrm, 0, "Out of memory");
1257 RTListInit(&pProv->ProbeHead);
1258 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
1259 pProv->pszName = strtabInsertN(pszName, cchName);
1260 if (!pProv->pszName)
1261 return parseError(pStrm, 0, "Out of memory");
1262
1263 /*
1264 * Parse loop.
1265 */
1266 for (;;)
1267 {
1268 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1269 switch (ch)
1270 {
1271 case 'p':
1272 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
1273 rcExit = parseProbe(pStrm, pProv);
1274 else
1275 rcExit = parseError(pStrm, 1, "Unexpected character");
1276 break;
1277
1278 case '}':
1279 {
1280 size_t off = ScmStreamTell(pStrm);
1281 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1282 if (ch == ';')
1283 return RTEXITCODE_SUCCESS;
1284 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
1285 break;
1286 }
1287
1288 case ~(unsigned)0:
1289 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
1290 break;
1291
1292 default:
1293 rcExit = parseError(pStrm, 1, "Unexpected character");
1294 break;
1295 }
1296 if (rcExit != RTEXITCODE_SUCCESS)
1297 return rcExit;
1298 }
1299}
1300
1301
1302static RTEXITCODE parseScript(const char *pszScript)
1303{
1304 SCMSTREAM Strm;
1305 int rc = ScmStreamInitForReading(&Strm, pszScript);
1306 if (RT_FAILURE(rc))
1307 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
1308 if (g_cVerbosity > 0)
1309 RTMsgInfo("Parsing '%s'...", pszScript);
1310
1311 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1312 unsigned ch;
1313 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
1314 {
1315 if (RT_C_IS_SPACE(ch))
1316 continue;
1317 switch (ch)
1318 {
1319 case '/':
1320 ch = ScmStreamGetCh(&Strm);
1321 if (ch == '*')
1322 rcExit = parseMultiLineComment(&Strm);
1323 else if (ch == '/')
1324 rcExit = parseOneLineComment(&Strm);
1325 else
1326 rcExit = parseError(&Strm, 2, "Unexpected character");
1327 break;
1328
1329 case 'p':
1330 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
1331 rcExit = parseProvider(&Strm);
1332 else
1333 rcExit = parseError(&Strm, 1, "Unexpected character");
1334 break;
1335
1336 case '#':
1337 {
1338 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
1339 if (ch == ~(unsigned)0)
1340 rcExit != RTEXITCODE_FAILURE;
1341 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
1342 rcExit = parsePragma(&Strm);
1343 else
1344 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
1345 break;
1346 }
1347
1348 default:
1349 rcExit = parseError(&Strm, 1, "Unexpected character");
1350 break;
1351 }
1352 if (rcExit != RTEXITCODE_SUCCESS)
1353 return rcExit;
1354 }
1355
1356 ScmStreamDelete(&Strm);
1357 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
1358 RTMsgInfo("Successfully parsed '%s'.", pszScript);
1359 return rcExit;
1360}
1361
1362
1363/**
1364 * Parses the arguments.
1365 */
1366static RTEXITCODE parseArguments(int argc, char **argv)
1367{
1368 enum
1369 {
1370 kVBoxTpGOpt_32Bit = 1000,
1371 kVBoxTpGOpt_64Bit,
1372 kVBoxTpGOpt_Assembler,
1373 kVBoxTpGOpt_AssemblerFmtOpt,
1374 kVBoxTpGOpt_AssemblerFmtVal,
1375 kVBoxTpGOpt_AssemblerOutputOpt,
1376 kVBoxTpGOpt_AssemblerOption,
1377 kVBoxTpGOpt_End
1378 };
1379
1380 static RTGETOPTDEF const s_aOpts[] =
1381 {
1382 /* dtrace w/ long options */
1383 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
1384 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
1385 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
1386 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
1387 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
1388 { "--output", 'o', RTGETOPT_REQ_STRING },
1389 { "--script", 's', RTGETOPT_REQ_STRING },
1390 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1391 /* out stuff */
1392 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
1393 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
1394 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
1395 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
1396 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
1397 };
1398
1399 RTGETOPTUNION ValueUnion;
1400 RTGETOPTSTATE GetOptState;
1401 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1402 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
1403
1404 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1405 {
1406 switch (rc)
1407 {
1408 /*
1409 * DTrace compatible options.
1410 */
1411 case kVBoxTpGOpt_32Bit:
1412 g_cBits = 32;
1413 g_pszAssemblerFmtOpt = g_szAssemblerFmtVal32;
1414 break;
1415
1416 case kVBoxTpGOpt_64Bit:
1417 g_cBits = 64;
1418 g_pszAssemblerFmtOpt = g_szAssemblerFmtVal64;
1419 break;
1420
1421 case 'C':
1422 g_fApplyCpp = true;
1423 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
1424 break;
1425
1426 case 'G':
1427 if ( g_enmAction != kVBoxTpGAction_Nothing
1428 && g_enmAction != kVBoxTpGAction_GenerateObject)
1429 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G and -h does not mix");
1430 g_enmAction = kVBoxTpGAction_GenerateObject;
1431 break;
1432
1433 case 'h':
1434 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
1435 {
1436 if ( g_enmAction != kVBoxTpGAction_Nothing
1437 && g_enmAction != kVBoxTpGAction_GenerateHeader)
1438 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h and -G does not mix");
1439 g_enmAction = kVBoxTpGAction_GenerateHeader;
1440 }
1441 else
1442 {
1443 /* --help or similar */
1444 RTPrintf("VirtualBox Tracepoint Generator\n"
1445 "\n"
1446 "Usage: %s [options]\n"
1447 "\n"
1448 "Options:\n", RTProcShortName());
1449 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
1450 if ((unsigned)s_aOpts[i].iShort < 128)
1451 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
1452 else
1453 RTPrintf(" %s\n", s_aOpts[i].pszLong);
1454 return RTEXITCODE_SUCCESS;
1455 }
1456 break;
1457
1458 case 'o':
1459 if (g_pszOutput)
1460 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
1461 g_pszOutput = ValueUnion.psz;
1462 break;
1463
1464 case 's':
1465 if (g_pszScript)
1466 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
1467 g_pszScript = ValueUnion.psz;
1468 break;
1469
1470 case 'v':
1471 g_cVerbosity++;
1472 break;
1473
1474 case 'V':
1475 {
1476 /* The following is assuming that svn does it's job here. */
1477 static const char s_szRev[] = "$Revision: 40569 $";
1478 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
1479 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
1480 return RTEXITCODE_SUCCESS;
1481 }
1482
1483 case VINF_GETOPT_NOT_OPTION:
1484 if (g_enmAction == kVBoxTpGAction_GenerateObject)
1485 break; /* object files, ignore them. */
1486 return RTGetOptPrintError(rc, &ValueUnion);
1487
1488
1489 /*
1490 * Out options.
1491 */
1492 case kVBoxTpGOpt_Assembler:
1493 g_pszAssembler = ValueUnion.psz;
1494 break;
1495
1496 case kVBoxTpGOpt_AssemblerFmtOpt:
1497 g_pszAssemblerFmtOpt = ValueUnion.psz;
1498 break;
1499
1500 case kVBoxTpGOpt_AssemblerFmtVal:
1501 g_pszAssemblerFmtVal = ValueUnion.psz;
1502 break;
1503
1504 case kVBoxTpGOpt_AssemblerOutputOpt:
1505 g_pszAssemblerOutputOpt = ValueUnion.psz;
1506 break;
1507
1508 case kVBoxTpGOpt_AssemblerOption:
1509 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
1510 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
1511 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
1512 g_cAssemblerOptions++;
1513 break;
1514
1515 /*
1516 * Errors and bugs.
1517 */
1518 default:
1519 return RTGetOptPrintError(rc, &ValueUnion);
1520 }
1521 }
1522
1523 /*
1524 * Check that we've got all we need.
1525 */
1526 if (g_enmAction == kVBoxTpGAction_Nothing)
1527 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h or -G)");
1528 if (!g_pszScript)
1529 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
1530 if (!g_pszOutput)
1531 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
1532
1533 return RTEXITCODE_SUCCESS;
1534}
1535
1536
1537int main(int argc, char **argv)
1538{
1539 int rc = RTR3InitExe(argc, &argv, 0);
1540 if (RT_FAILURE(rc))
1541 return 1;
1542
1543 RTEXITCODE rcExit = parseArguments(argc, argv);
1544 if (rcExit == RTEXITCODE_SUCCESS)
1545 {
1546 /*
1547 * Parse the script.
1548 */
1549 RTListInit(&g_ProviderHead);
1550 rcExit = parseScript(g_pszScript);
1551 if (rcExit == RTEXITCODE_SUCCESS)
1552 {
1553 /*
1554 * Take action.
1555 */
1556 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
1557 rcExit = generateHeader(g_pszOutput);
1558 else
1559 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
1560 }
1561 }
1562
1563 return rcExit;
1564}
1565
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