VirtualBox

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

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

VBoxTpG: another darwin fix.

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