VirtualBox

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

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

RT_STR_TUPLE: Switch the length and string so it'll work with functions like RTStrAAppendN. VBoxTpG: Parser works.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 40.4 KB
Line 
1/* $Id: VBoxTpG.cpp 40557 2012-03-20 22:24:36Z 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/process.h>
34#include <iprt/stream.h>
35#include <iprt/string.h>
36#include <iprt/strcache.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 char *pszType;
88} VTGARG;
89typedef VTGARG *PVTGARG;
90
91typedef struct VTGPROBE
92{
93 RTLISTNODE ListEntry;
94 const char *pszName;
95 RTLISTANCHOR ArgHead;
96 uint32_t cArgs;
97} VTGPROBE;
98typedef VTGPROBE *PVTGPROBE;
99
100typedef struct VTGPROVIDER
101{
102 RTLISTNODE ListEntry;
103 const char *pszName;
104
105 VTGATTRS AttrSelf;
106 VTGATTRS AttrModules;
107 VTGATTRS AttrFunctions;
108 VTGATTRS AttrName;
109 VTGATTRS AttrArguments;
110
111 RTLISTANCHOR ProbeHead;
112} VTGPROVIDER;
113typedef VTGPROVIDER *PVTGPROVIDER;
114
115
116/*******************************************************************************
117* Global Variables *
118*******************************************************************************/
119/** String cache used for storing strings when parsing. */
120static RTSTRCACHE g_hStrCache = NIL_RTSTRCACHE;
121/** List of providers created by the parser. */
122static RTLISTANCHOR g_ProviderHead;
123
124/** @name Options
125 * @{ */
126static enum
127{
128 kVBoxTpGAction_Nothing,
129 kVBoxTpGAction_GenerateHeader,
130 kVBoxTpGAction_GenerateObject
131} g_enmAction = kVBoxTpGAction_Nothing;
132static uint32_t g_cBits = ARCH_BITS;
133static bool g_fApplyCpp = false;
134static uint32_t g_cVerbosity = 0;
135static const char *g_pszOutput = NULL;
136static const char *g_pszScript = NULL;
137static const char *g_pszTempAsm = NULL;
138#ifdef RT_OS_DARWIN
139static const char *g_pszAssembler = "yasm";
140static const char *g_pszAssemblerFmtOpt = "--oformat";
141static const char g_szAssemblerFmtVal32[] = "macho32";
142static const char g_szAssemblerFmtVal64[] = "macho64";
143#elif defined(RT_OS_OS2)
144static const char *pszAssembler = "nasm.exe";
145static const char *pszAssemblerFmtOpt = "-f";
146static const char g_szAssemblerFmtVal32[] = "obj";
147static const char g_szAssemblerFmtVal64[] = "elf64";
148#elif defined(RT_OS_WINDOWS)
149static const char *g_pszAssembler = "yasm.exe";
150static const char *g_pszAssemblerFmtOpt = "--oformat";
151static const char g_szAssemblerFmtVal32[] = "win32";
152static const char g_szAssemblerFmtVal64[] = "win64";
153#else
154static const char *g_pszAssembler = "yasm";
155static const char *g_pszAssemblerFmtOpt = "--oformat";
156static const char g_szAssemblerFmtVal32[] = "elf32";
157static const char g_szAssemblerFmtVal64[] = "elf64";
158#endif
159static const char *g_pszAssemblerFmtVal = RT_CONCAT(g_szAssemblerFmtVal, ARCH_BITS);
160static const char *g_pszAssemblerOutputOpt = "-o";
161static unsigned g_cAssemblerOptions = 0;
162static const char *g_apszAssemblerOptions[32];
163/** @} */
164
165
166static RTEXITCODE generateInvokeAssembler(const char *pszOutput, const char *pszTempAsm)
167{
168 RTPrintf("Todo invoke the assembler\n");
169 return RTEXITCODE_SKIPPED;
170}
171
172
173static RTEXITCODE generateFile(const char *pszOutput, const char *pszWhat,
174 RTEXITCODE (*pfnGenerator)(PSCMSTREAM))
175{
176 SCMSTREAM Strm;
177 int rc = ScmStreamInitForWriting(&Strm, NULL);
178 if (RT_FAILURE(rc))
179 return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file",
180 rc, pszWhat);
181
182 RTEXITCODE rcExit = pfnGenerator(&Strm);
183 if (RT_FAILURE(ScmStreamGetStatus(&Strm)))
184 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Stream error %Rrc generating the %s file",
185 ScmStreamGetStatus(&Strm), pszWhat);
186 if (rcExit == RTEXITCODE_SUCCESS)
187 {
188 rc = ScmStreamWriteToFile(&Strm, "%s", pszOutput);
189 if (RT_FAILURE(rc))
190 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)",
191 rc, pszOutput, pszWhat);
192 if (rcExit == RTEXITCODE_SUCCESS)
193 {
194 if (g_cVerbosity > 0)
195 RTMsgInfo("Successfully generated '%s'.", pszOutput);
196 if (g_cVerbosity > 1)
197 {
198 RTMsgInfo("================ %s - start ================", pszWhat);
199 ScmStreamRewindForReading(&Strm);
200 const char *pszLine;
201 size_t cchLine;
202 SCMEOL enmEol;
203 while ((pszLine = ScmStreamGetLine(&Strm, &cchLine, &enmEol)) != NULL)
204 RTPrintf("%.*s\n", cchLine, pszLine);
205 RTMsgInfo("================ %s - end ================", pszWhat);
206 }
207 }
208 }
209 ScmStreamDelete(&Strm);
210 return rcExit;
211}
212
213
214static RTEXITCODE generateAssembly(PSCMSTREAM pStrm)
215{
216 if (g_cVerbosity > 0)
217 RTMsgInfo("Generating assembly code...");
218
219 RTPrintf("Todo generate the assembly code\n");
220 return RTEXITCODE_SUCCESS;
221}
222
223
224static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm)
225{
226 if (!pszTempAsm)
227 {
228 size_t cch = strlen(pszTempAsm);
229 char *psz = (char *)alloca(cch + sizeof(".asm"));
230 memcpy(psz, pszOutput, cch);
231 memcpy(psz + cch, ".asm", sizeof(".asm"));
232 pszTempAsm = psz;
233 }
234
235 RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly);
236 if (rcExit == RTEXITCODE_SUCCESS)
237 rcExit = generateInvokeAssembler(pszOutput, pszTempAsm);
238 RTFileDelete(pszTempAsm);
239 return rcExit;
240}
241
242
243static RTEXITCODE generateHeaderInner(PSCMSTREAM pStrm)
244{
245 RTPrintf("Todo generate the header\n");
246 return RTEXITCODE_SUCCESS;
247}
248
249
250static RTEXITCODE generateHeader(const char *pszHeader)
251{
252 return generateFile(pszHeader, "header", generateHeaderInner);
253}
254
255/**
256 * If the given C word is at off - 1, return @c true and skip beyond it,
257 * otherwise return @c false.
258 *
259 * @retval true if the given C-word is at the current position minus one char.
260 * The stream position changes.
261 * @retval false if not. The stream position is unchanged.
262 *
263 * @param pStream The stream.
264 * @param cchWord The length of the word.
265 * @param pszWord The word.
266 */
267bool ScmStreamCMatchingWordM1(PSCMSTREAM pStream, const char *pszWord, size_t cchWord)
268{
269 /* Check stream state. */
270 AssertReturn(!pStream->fWriteOrRead, false);
271 AssertReturn(RT_SUCCESS(pStream->rc), false);
272 AssertReturn(pStream->fFullyLineated, false);
273
274 /* Sufficient chars left on the line? */
275 size_t const iLine = pStream->iLine;
276 AssertReturn(pStream->off > pStream->paLines[iLine].off, false);
277 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
278 if (cchWord > cchLeft)
279 return false;
280
281 /* Do they match? */
282 const char *psz = &pStream->pch[pStream->off - 1];
283 if (memcmp(psz, pszWord, cchWord))
284 return false;
285
286 /* Is it the end of a C word? */
287 if (cchWord < cchLeft)
288 {
289 psz += cchWord;
290 if (RT_C_IS_ALNUM(*psz) || *psz == '_')
291 return false;
292 }
293
294 /* Skip ahead. */
295 pStream->off += cchWord - 1;
296 return true;
297}
298
299/**
300 * Get's the C word starting at the current position.
301 *
302 * @returns Pointer to the word on success and the stream position advanced to
303 * the end of it.
304 * NULL on failure, stream position normally unchanged.
305 * @param pStream The stream to get the C word from.
306 * @param pcchWord Where to return the word length.
307 */
308const char *ScmStreamCGetWord(PSCMSTREAM pStream, size_t *pcchWord)
309{
310 /* Check stream state. */
311 AssertReturn(!pStream->fWriteOrRead, false);
312 AssertReturn(RT_SUCCESS(pStream->rc), false);
313 AssertReturn(pStream->fFullyLineated, false);
314
315 /* Get the number of chars left on the line and locate the current char. */
316 size_t const iLine = pStream->iLine;
317 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - pStream->off;
318 const char *psz = &pStream->pch[pStream->off];
319
320 /* Is it a leading C character. */
321 if (!RT_C_IS_ALPHA(*psz) && !*psz == '_')
322 return NULL;
323
324 /* Find the end of the word. */
325 char ch;
326 size_t off = 1;
327 while ( off < cchLeft
328 && ( (ch = psz[off]) == '_'
329 || RT_C_IS_ALNUM(ch)))
330 off++;
331
332 pStream->off += off;
333 *pcchWord = off;
334 return psz;
335}
336
337
338/**
339 * Get's the C word starting at the current position minus one.
340 *
341 * @returns Pointer to the word on success and the stream position advanced to
342 * the end of it.
343 * NULL on failure, stream position normally unchanged.
344 * @param pStream The stream to get the C word from.
345 * @param pcchWord Where to return the word length.
346 */
347const char *ScmStreamCGetWordM1(PSCMSTREAM pStream, size_t *pcchWord)
348{
349 /* Check stream state. */
350 AssertReturn(!pStream->fWriteOrRead, false);
351 AssertReturn(RT_SUCCESS(pStream->rc), false);
352 AssertReturn(pStream->fFullyLineated, false);
353
354 /* Get the number of chars left on the line and locate the current char. */
355 size_t const iLine = pStream->iLine;
356 size_t const cchLeft = pStream->paLines[iLine].cch + pStream->paLines[iLine].off - (pStream->off - 1);
357 const char *psz = &pStream->pch[pStream->off - 1];
358
359 /* Is it a leading C character. */
360 if (!RT_C_IS_ALPHA(*psz) && !*psz == '_')
361 return NULL;
362
363 /* Find the end of the word. */
364 char ch;
365 size_t off = 1;
366 while ( off < cchLeft
367 && ( (ch = psz[off]) == '_'
368 || RT_C_IS_ALNUM(ch)))
369 off++;
370
371 pStream->off += off - 1;
372 *pcchWord = off;
373 return psz;
374}
375
376
377/**
378 * Parser error with line and position.
379 *
380 * @returns RTEXITCODE_FAILURE.
381 * @param pStrm The stream.
382 * @param cb The offset from the current position to the
383 * point of failure.
384 * @param pszMsg The message to display.
385 */
386static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t cb, const char *pszMsg)
387{
388 if (cb)
389 ScmStreamSeekRelative(pStrm, -cb);
390 size_t const off = ScmStreamTell(pStrm);
391 size_t const iLine = ScmStreamTellLine(pStrm);
392 ScmStreamSeekByLine(pStrm, iLine);
393 size_t const offLine = ScmStreamTell(pStrm);
394
395 RTPrintf("%s:%d:%zd: error: %s.\n", g_pszScript, iLine + 1, off - offLine + 1, pszMsg);
396
397 size_t cchLine;
398 SCMEOL enmEof;
399 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
400 if (pszLine)
401 RTPrintf(" %.*s\n"
402 " %*s^\n",
403 cchLine, pszLine, off - offLine, "");
404 return RTEXITCODE_FAILURE;
405}
406
407
408/**
409 * Parser error with line and position.
410 *
411 * @returns RTEXITCODE_FAILURE.
412 * @param pStrm The stream.
413 * @param cb The offset from the current position to the
414 * point of failure.
415 * @param pszMsg The message to display.
416 */
417static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszMsg)
418{
419 ScmStreamSeekAbsolute(pStrm, off);
420 return parseError(pStrm, 0, pszMsg);
421}
422
423/**
424 * Handles a C++ one line comment.
425 *
426 * @returns Exit code.
427 * @param pStrm The stream.
428 */
429static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
430{
431 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
432 return RTEXITCODE_SUCCESS;
433}
434
435/**
436 * Handles a multi-line C/C++ comment.
437 *
438 * @returns Exit code.
439 * @param pStrm The stream.
440 */
441static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
442{
443 unsigned ch;
444 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
445 {
446 if (ch == '*')
447 {
448 do
449 ch = ScmStreamGetCh(pStrm);
450 while (ch == '*');
451 if (ch == '/')
452 return RTEXITCODE_SUCCESS;
453 }
454 }
455
456 parseError(pStrm, 1, "Expected end of comment, got end of file");
457 return RTEXITCODE_FAILURE;
458}
459
460
461/**
462 * Skips spaces and comments.
463 *
464 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
465 * @param pStrm The stream..
466 */
467static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
468{
469 unsigned ch;
470 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
471 {
472 if (!RT_C_IS_SPACE(ch) && ch != '/')
473 return RTEXITCODE_SUCCESS;
474 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
475 if (ch == '/')
476 {
477 ch = ScmStreamGetCh(pStrm);
478 RTEXITCODE rcExit;
479 if (ch == '*')
480 rcExit = parseMultiLineComment(pStrm);
481 else if (ch == '/')
482 rcExit = parseOneLineComment(pStrm);
483 else
484 rcExit = parseError(pStrm, 2, "Unexpected character");
485 if (rcExit != RTEXITCODE_SUCCESS)
486 return rcExit;
487 }
488 }
489
490 return parseError(pStrm, 0, "Unexpected end of file");
491}
492
493
494/**
495 * Skips spaces and comments, returning the next character.
496 *
497 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
498 * failure.
499 * @param pStrm The stream.
500 */
501static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
502{
503 unsigned ch;
504 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
505 {
506 if (!RT_C_IS_SPACE(ch) && ch != '/')
507 return ch;
508 if (ch == '/')
509 {
510 ch = ScmStreamGetCh(pStrm);
511 RTEXITCODE rcExit;
512 if (ch == '*')
513 rcExit = parseMultiLineComment(pStrm);
514 else if (ch == '/')
515 rcExit = parseOneLineComment(pStrm);
516 else
517 rcExit = parseError(pStrm, 2, "Unexpected character");
518 if (rcExit != RTEXITCODE_SUCCESS)
519 return ~(unsigned)0;
520 }
521 }
522
523 parseError(pStrm, 0, "Unexpected end of file");
524 return ~(unsigned)0;
525}
526
527
528/**
529 * Get the next non-space-non-comment character on a preprocessor line.
530 *
531 * @returns The next character. On error message and ~(unsigned)0.
532 * @param pStrm The stream.
533 */
534static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
535{
536 size_t off = ScmStreamTell(pStrm) - 1;
537 unsigned ch;
538 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
539 {
540 if (RT_C_IS_SPACE(ch))
541 {
542 if (ch == '\n' || ch == '\r')
543 {
544 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
545 break;
546 }
547 }
548 else if (ch == '\\')
549 {
550 size_t off2 = ScmStreamTell(pStrm) - 1;
551 ch = ScmStreamGetCh(pStrm);
552 if (ch == '\r')
553 ch = ScmStreamGetCh(pStrm);
554 if (ch != '\n')
555 {
556 parseErrorAbs(pStrm, off2, "Expected new line");
557 break;
558 }
559 }
560 else
561 return ch;
562 }
563 return ~(unsigned)0;
564}
565
566
567
568/**
569 * Skips spaces and comments.
570 *
571 * @returns Same as ScmStreamCGetWord
572 * @param pStrm The stream..
573 * @param pcchWord Where to return the length.
574 */
575static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
576{
577 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
578 return NULL;
579 return ScmStreamCGetWord(pStrm, pcchWord);
580}
581
582
583
584/**
585 * Parses interface stability.
586 *
587 * @returns Interface stability if parsed correctly, otherwise error message and
588 * kVTGStability_Invalid.
589 * @param pStrm The stream.
590 * @param ch The first character in the stability spec.
591 */
592static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
593{
594 switch (ch)
595 {
596 case 'E':
597 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
598 return kVTGStability_External;
599 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
600 return kVTGStability_Evolving;
601 break;
602 case 'I':
603 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
604 return kVTGStability_Internal;
605 break;
606 case 'O':
607 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
608 return kVTGStability_Obsolete;
609 break;
610 case 'P':
611 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
612 return kVTGStability_Private;
613 break;
614 case 'S':
615 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
616 return kVTGStability_Stable;
617 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
618 return kVTGStability_Standard;
619 break;
620 case 'U':
621 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
622 return kVTGStability_Unstable;
623 break;
624 }
625 parseError(pStrm, 1, "Unknown stability specifier");
626 return kVTGStability_Invalid;
627}
628
629
630/**
631 * Parses data depndency class.
632 *
633 * @returns Data dependency class if parsed correctly, otherwise error message
634 * and kVTGClass_Invalid.
635 * @param pStrm The stream.
636 * @param ch The first character in the stability spec.
637 */
638static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
639{
640 switch (ch)
641 {
642 case 'C':
643 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
644 return kVTGClass_Common;
645 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
646 return kVTGClass_Cpu;
647 break;
648 case 'G':
649 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
650 return kVTGClass_Group;
651 break;
652 case 'I':
653 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
654 return kVTGClass_Isa;
655 break;
656 case 'P':
657 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
658 return kVTGClass_Platform;
659 break;
660 case 'U':
661 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
662 return kVTGClass_Unknown;
663 break;
664 }
665 parseError(pStrm, 1, "Unknown data dependency class specifier");
666 return kVTGClass_Invalid;
667}
668
669/**
670 * Parses a pragma D attributes statement.
671 *
672 * @returns Suitable exit code, errors message already written on failure.
673 * @param pStrm The stream.
674 */
675static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
676{
677 /*
678 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
679 */
680 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
681 if (ch == ~(unsigned)0)
682 return RTEXITCODE_FAILURE;
683
684 kVTGStability enmCode = parseStability(pStrm, ch);
685 if (enmCode == kVTGStability_Invalid)
686 return RTEXITCODE_FAILURE;
687 ch = ScmStreamGetCh(pStrm);
688 if (ch != '/')
689 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
690
691 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
692 if (enmData == kVTGStability_Invalid)
693 return RTEXITCODE_FAILURE;
694 ch = ScmStreamGetCh(pStrm);
695 if (ch != '/')
696 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
697
698 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
699 if (enmDataDep == kVTGClass_Invalid)
700 return RTEXITCODE_FAILURE;
701
702 /*
703 * Expecting 'provider' followed by the name of an provider defined earlier.
704 */
705 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
706 if (ch == ~(unsigned)0)
707 return RTEXITCODE_FAILURE;
708 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
709 return parseError(pStrm, 1, "Expected 'provider'");
710
711 size_t cchName;
712 const char *pszName = parseGetNextCWord(pStrm, &cchName);
713 if (!pszName)
714 return parseError(pStrm, 1, "Expected provider name");
715
716 PVTGPROVIDER pProv;
717 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
718 {
719 if ( !strncmp(pProv->pszName, pszName, cchName)
720 && pProv->pszName[cchName] == '\0')
721 break;
722 }
723 if (!pProv)
724 return parseError(pStrm, cchName, "Provider not found");
725
726 /*
727 * Which aspect of the provider?
728 */
729 size_t cchAspect;
730 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
731 if (!pszAspect)
732 return parseError(pStrm, 1, "Expected provider aspect");
733
734 PVTGATTRS pAttrs;
735 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
736 pAttrs = &pProv->AttrSelf;
737 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
738 pAttrs = &pProv->AttrFunctions;
739 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
740 pAttrs = &pProv->AttrModules;
741 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
742 pAttrs = &pProv->AttrName;
743 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
744 pAttrs = &pProv->AttrArguments;
745 else
746 return parseError(pStrm, cchAspect, "Unknown aspect");
747
748 if (pAttrs->enmCode != kVTGStability_Invalid)
749 return parseError(pStrm, cchAspect, "You have already specified these attributes");
750
751 pAttrs->enmCode = enmCode;
752 pAttrs->enmData = enmData;
753 pAttrs->enmDataDep = enmDataDep;
754 return RTEXITCODE_SUCCESS;
755}
756
757/**
758 * Parses a D pragma statement.
759 *
760 * @returns Suitable exit code, errors message already written on failure.
761 * @param pStrm The stream.
762 */
763static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
764{
765 RTEXITCODE rcExit;
766 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
767 if (ch == ~(unsigned)0)
768 rcExit = RTEXITCODE_FAILURE;
769 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
770 {
771 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
772 if (ch == ~(unsigned)0)
773 rcExit = RTEXITCODE_FAILURE;
774 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
775 rcExit = parsePragmaDAttributes(pStrm);
776 else
777 rcExit = parseError(pStrm, 1, "Unknown pragma D");
778 }
779 else
780 rcExit = parseError(pStrm, 1, "Unknown pragma");
781 return rcExit;
782}
783
784
785/**
786 * Parses a D probe statement.
787 *
788 * @returns Suitable exit code, errors message already written on failure.
789 * @param pStrm The stream.
790 * @param pProv The provider being parsed.
791 */
792static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
793{
794 /*
795 * Next up is a name followed by an opening parenthesis.
796 */
797 size_t cchProbe;
798 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
799 if (!pszProbe)
800 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
801 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
802 if (ch != '(')
803 return parseError(pStrm, 1, "Expected '(' after the probe name");
804
805 /*
806 * Create a probe instance.
807 */
808 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
809 if (!pProbe)
810 return parseError(pStrm, 0, "Out of memory");
811 RTListInit(&pProbe->ArgHead);
812 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
813 pProbe->pszName = RTStrCacheEnterN(g_hStrCache, pszProbe, cchProbe);
814 if (!pProbe->pszName)
815 return parseError(pStrm, 0, "Out of memory");
816
817 /*
818 * Parse loop for the argument.
819 */
820 PVTGARG pArg = NULL;
821 size_t cchName = 0;
822 for (;;)
823 {
824 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
825 switch (ch)
826 {
827 case ')':
828 case ',':
829 {
830 /* commit the argument */
831 if (pArg)
832 {
833 if (!cchName)
834 return parseError(pStrm, 1, "Argument has no name");
835 char *pszName;
836 pArg->pszName = pszName = strchr(pArg->pszType, '\0') - cchName;
837 pszName[-1] = '\0';
838 pArg = NULL;
839 }
840 if (ch == ')')
841 {
842 size_t off = ScmStreamTell(pStrm);
843 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
844 if (ch != ';')
845 return parseErrorAbs(pStrm, off, "Expected ';'");
846 return RTEXITCODE_SUCCESS;
847 }
848 break;
849 }
850
851 default:
852 {
853 int rc;
854 size_t cchWord;
855 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
856 if (!pszWord)
857 return parseError(pStrm, 0, "Expected argument");
858 if (!pArg)
859 {
860 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
861 if (!pArg)
862 return parseError(pStrm, 1, "Out of memory");
863 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
864 pProbe->cArgs++;
865
866 rc = RTStrAAppendN(&pArg->pszType, pszWord, cchWord);
867 cchName = 0;
868 }
869 else
870 {
871 rc = RTStrAAppendExN(&pArg->pszType, 2, RT_STR_TUPLE(" "), pszWord, cchWord);
872 cchName = cchWord;
873 }
874 if (RT_FAILURE(rc))
875 return parseError(pStrm, 1, "Out of memory");
876 break;
877 }
878
879 case '*':
880 {
881 if (!pArg)
882 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
883 int rc = RTStrAAppend(&pArg->pszType, " *");
884 if (RT_FAILURE(rc))
885 return parseError(pStrm, 1, "Out of memory");
886 cchName = 0;
887 break;
888 }
889
890 case ~(unsigned)0:
891 return parseError(pStrm, 0, "Missing closing ')' on probe");
892 }
893 }
894}
895
896/**
897 * Parses a D provider statement.
898 *
899 * @returns Suitable exit code, errors message already written on failure.
900 * @param pStrm The stream.
901 */
902static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
903{
904 /*
905 * Next up is a name followed by a curly bracket. Ignore comments.
906 */
907 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
908 if (rcExit != RTEXITCODE_SUCCESS)
909 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
910 size_t cchName;
911 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
912 if (!pszName)
913 return parseError(pStrm, 0, "Bad provider name");
914 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
915 return parseError(pStrm, 1, "A provider name cannot end with digit");
916
917 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
918 if (ch != '{')
919 return parseError(pStrm, 1, "Expected '{' after the provider name");
920
921 /*
922 * Create a provider instance.
923 */
924 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
925 if (!pProv)
926 return parseError(pStrm, 0, "Out of memory");
927 RTListInit(&pProv->ProbeHead);
928 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
929 pProv->pszName = RTStrCacheEnterN(g_hStrCache, pszName, cchName);
930 if (!pProv->pszName)
931 return parseError(pStrm, 0, "Out of memory");
932
933 /*
934 * Parse loop.
935 */
936 for (;;)
937 {
938 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
939 switch (ch)
940 {
941 case 'p':
942 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
943 rcExit = parseProbe(pStrm, pProv);
944 else
945 rcExit = parseError(pStrm, 1, "Unexpected character");
946 break;
947
948 case '}':
949 {
950 size_t off = ScmStreamTell(pStrm);
951 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
952 if (ch == ';')
953 return RTEXITCODE_SUCCESS;
954 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
955 break;
956 }
957
958 case ~(unsigned)0:
959 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
960 break;
961
962 default:
963 rcExit = parseError(pStrm, 1, "Unexpected character");
964 break;
965 }
966 if (rcExit != RTEXITCODE_SUCCESS)
967 return rcExit;
968 }
969}
970
971
972static RTEXITCODE parseScript(const char *pszScript)
973{
974 SCMSTREAM Strm;
975 int rc = ScmStreamInitForReading(&Strm, pszScript);
976 if (RT_FAILURE(rc))
977 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
978 if (g_cVerbosity > 0)
979 RTMsgInfo("Parsing '%s'...", pszScript);
980
981 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
982 unsigned ch;
983 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
984 {
985 if (RT_C_IS_SPACE(ch))
986 continue;
987 switch (ch)
988 {
989 case '/':
990 ch = ScmStreamGetCh(&Strm);
991 if (ch == '*')
992 rcExit = parseMultiLineComment(&Strm);
993 else if (ch == '/')
994 rcExit = parseOneLineComment(&Strm);
995 else
996 rcExit = parseError(&Strm, 2, "Unexpected character");
997 break;
998
999 case 'p':
1000 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
1001 rcExit = parseProvider(&Strm);
1002 else
1003 rcExit = parseError(&Strm, 1, "Unexpected character");
1004 break;
1005
1006 case '#':
1007 {
1008 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
1009 if (ch == ~(unsigned)0)
1010 rcExit != RTEXITCODE_FAILURE;
1011 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
1012 rcExit = parsePragma(&Strm);
1013 else
1014 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
1015 break;
1016 }
1017
1018 default:
1019 rcExit = parseError(&Strm, 1, "Unexpected character");
1020 break;
1021 }
1022 if (rcExit != RTEXITCODE_SUCCESS)
1023 return rcExit;
1024 }
1025
1026 ScmStreamDelete(&Strm);
1027 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
1028 RTMsgInfo("Successfully parsed '%s'.", pszScript);
1029 return rcExit;
1030}
1031
1032
1033/**
1034 * Parses the arguments.
1035 */
1036static RTEXITCODE parseArguments(int argc, char **argv)
1037{
1038 enum
1039 {
1040 kVBoxTpGOpt_32Bit = 1000,
1041 kVBoxTpGOpt_64Bit,
1042 kVBoxTpGOpt_Assembler,
1043 kVBoxTpGOpt_AssemblerFmtOpt,
1044 kVBoxTpGOpt_AssemblerFmtVal,
1045 kVBoxTpGOpt_AssemblerOutputOpt,
1046 kVBoxTpGOpt_AssemblerOption,
1047 kVBoxTpGOpt_End
1048 };
1049
1050 static RTGETOPTDEF const s_aOpts[] =
1051 {
1052 /* dtrace w/ long options */
1053 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
1054 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
1055 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
1056 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
1057 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
1058 { "--output", 'o', RTGETOPT_REQ_STRING },
1059 { "--script", 's', RTGETOPT_REQ_STRING },
1060 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
1061 /* out stuff */
1062 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
1063 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
1064 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
1065 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
1066 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
1067 };
1068
1069 RTGETOPTUNION ValueUnion;
1070 RTGETOPTSTATE GetOptState;
1071 int rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1072 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
1073
1074 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
1075 {
1076 switch (rc)
1077 {
1078 /*
1079 * DTrace compatible options.
1080 */
1081 case kVBoxTpGOpt_32Bit:
1082 g_cBits = 32;
1083 g_pszAssemblerFmtOpt = g_szAssemblerFmtVal32;
1084 break;
1085
1086 case kVBoxTpGOpt_64Bit:
1087 g_cBits = 64;
1088 g_pszAssemblerFmtOpt = g_szAssemblerFmtVal64;
1089 break;
1090
1091 case 'C':
1092 g_fApplyCpp = true;
1093 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
1094 break;
1095
1096 case 'G':
1097 if ( g_enmAction != kVBoxTpGAction_Nothing
1098 && g_enmAction != kVBoxTpGAction_GenerateObject)
1099 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G and -h does not mix");
1100 g_enmAction = kVBoxTpGAction_GenerateObject;
1101 break;
1102
1103 case 'h':
1104 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
1105 {
1106 if ( g_enmAction != kVBoxTpGAction_Nothing
1107 && g_enmAction != kVBoxTpGAction_GenerateHeader)
1108 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h and -G does not mix");
1109 g_enmAction = kVBoxTpGAction_GenerateHeader;
1110 }
1111 else
1112 {
1113 /* --help or similar */
1114 RTPrintf("VirtualBox Tracepoint Generator\n"
1115 "\n"
1116 "Usage: %s [options]\n"
1117 "\n"
1118 "Options:\n", RTProcShortName());
1119 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
1120 if ((unsigned)s_aOpts[i].iShort < 128)
1121 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
1122 else
1123 RTPrintf(" %s\n", s_aOpts[i].pszLong);
1124 return RTEXITCODE_SUCCESS;
1125 }
1126 break;
1127
1128 case 'o':
1129 if (g_pszOutput)
1130 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
1131 g_pszOutput = ValueUnion.psz;
1132 break;
1133
1134 case 's':
1135 if (g_pszScript)
1136 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
1137 g_pszScript = ValueUnion.psz;
1138 break;
1139
1140 case 'v':
1141 g_cVerbosity++;
1142 break;
1143
1144 case 'V':
1145 {
1146 /* The following is assuming that svn does it's job here. */
1147 static const char s_szRev[] = "$Revision: 40557 $";
1148 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
1149 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
1150 return RTEXITCODE_SUCCESS;
1151 }
1152
1153 case VINF_GETOPT_NOT_OPTION:
1154 if (g_enmAction == kVBoxTpGAction_GenerateObject)
1155 break; /* object files, ignore them. */
1156 return RTGetOptPrintError(rc, &ValueUnion);
1157
1158
1159 /*
1160 * Out options.
1161 */
1162 case kVBoxTpGOpt_Assembler:
1163 g_pszAssembler = ValueUnion.psz;
1164 break;
1165
1166 case kVBoxTpGOpt_AssemblerFmtOpt:
1167 g_pszAssemblerFmtOpt = ValueUnion.psz;
1168 break;
1169
1170 case kVBoxTpGOpt_AssemblerFmtVal:
1171 g_pszAssemblerFmtVal = ValueUnion.psz;
1172 break;
1173
1174 case kVBoxTpGOpt_AssemblerOutputOpt:
1175 g_pszAssemblerOutputOpt = ValueUnion.psz;
1176 break;
1177
1178 case kVBoxTpGOpt_AssemblerOption:
1179 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
1180 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
1181 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
1182 g_cAssemblerOptions++;
1183 break;
1184
1185 /*
1186 * Errors and bugs.
1187 */
1188 default:
1189 return RTGetOptPrintError(rc, &ValueUnion);
1190 }
1191 }
1192
1193 /*
1194 * Check that we've got all we need.
1195 */
1196 if (g_enmAction == kVBoxTpGAction_Nothing)
1197 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h or -G)");
1198 if (!g_pszScript)
1199 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
1200 if (!g_pszOutput)
1201 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
1202
1203 return RTEXITCODE_SUCCESS;
1204}
1205
1206
1207int main(int argc, char **argv)
1208{
1209 int rc = RTR3InitExe(argc, &argv, 0);
1210 if (RT_FAILURE(rc))
1211 return 1;
1212
1213 RTEXITCODE rcExit = parseArguments(argc, argv);
1214 if (rcExit == RTEXITCODE_SUCCESS)
1215 {
1216 /*
1217 * Parse the script.
1218 */
1219 rc = RTStrCacheCreate(&g_hStrCache, "VBoxTpG");
1220 if (RT_SUCCESS(rc))
1221 {
1222 RTListInit(&g_ProviderHead);
1223 rcExit = parseScript(g_pszScript);
1224 if (rcExit == RTEXITCODE_SUCCESS)
1225 {
1226 /*
1227 * Take action.
1228 */
1229 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
1230 rcExit = generateHeader(g_pszOutput);
1231 else
1232 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
1233 }
1234 RTStrCacheDestroy(g_hStrCache);
1235 }
1236 }
1237
1238 return rcExit;
1239}
1240
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