VirtualBox

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

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

Make VBoxTpG work on 64-bit darwin. (Still need to check 32-bit.)

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