VirtualBox

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

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

More of the generation code.

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