VirtualBox

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

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

SUPR0TracerFireProbe takes a VTG probe location pointer, not a dtrace ID.

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