VirtualBox

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

Last change on this file since 43712 was 43055, checked in by vboxsync, 12 years ago

VBoxTpG.cpp: Fix for 32-bit mac KEXTs.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 94.7 KB
Line 
1/* $Id: VBoxTpG.cpp 43055 2012-08-28 23:50:20Z 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#include <iprt/uuid.h>
40
41#include "scmstream.h"
42
43
44/*******************************************************************************
45* Structures and Typedefs *
46*******************************************************************************/
47
48typedef struct VTGATTRS
49{
50 kVTGStability enmCode;
51 kVTGStability enmData;
52 kVTGClass enmDataDep;
53} VTGATTRS;
54typedef VTGATTRS *PVTGATTRS;
55
56
57typedef struct VTGARG
58{
59 RTLISTNODE ListEntry;
60 /** The argument name. (heap) */
61 char *pszName;
62 /** The type presented to the tracer (in string table). */
63 const char *pszTracerType;
64 /** The argument type used in the probe method in that context. (heap) */
65 char *pszCtxType;
66 /** Argument passing format string. First and only argument is the name.
67 * (const string) */
68 const char *pszArgPassingFmt;
69 /** The type flags. */
70 uint32_t fType;
71} VTGARG;
72typedef VTGARG *PVTGARG;
73
74typedef struct VTGPROBE
75{
76 RTLISTNODE ListEntry;
77 char *pszMangledName;
78 const char *pszUnmangledName;
79 RTLISTANCHOR ArgHead;
80 uint32_t cArgs;
81 bool fHaveLargeArgs;
82 uint32_t offArgList;
83 uint32_t iProbe;
84} VTGPROBE;
85typedef VTGPROBE *PVTGPROBE;
86
87typedef struct VTGPROVIDER
88{
89 RTLISTNODE ListEntry;
90 const char *pszName;
91
92 uint16_t iFirstProbe;
93 uint16_t cProbes;
94
95 VTGATTRS AttrSelf;
96 VTGATTRS AttrModules;
97 VTGATTRS AttrFunctions;
98 VTGATTRS AttrName;
99 VTGATTRS AttrArguments;
100
101 RTLISTANCHOR ProbeHead;
102} VTGPROVIDER;
103typedef VTGPROVIDER *PVTGPROVIDER;
104
105/**
106 * A string table string.
107 */
108typedef struct VTGSTRING
109{
110 /** The string space core. */
111 RTSTRSPACECORE Core;
112 /** The string table offset. */
113 uint32_t offStrTab;
114 /** The actual string. */
115 char szString[1];
116} VTGSTRING;
117typedef VTGSTRING *PVTGSTRING;
118
119
120/*******************************************************************************
121* Global Variables *
122*******************************************************************************/
123/** The string space organizing the string table strings. Each node is a VTGSTRING. */
124static RTSTRSPACE g_StrSpace = NULL;
125/** Used by the string table enumerator to set VTGSTRING::offStrTab. */
126static uint32_t g_offStrTab;
127/** List of providers created by the parser. */
128static RTLISTANCHOR g_ProviderHead;
129/** The number of type errors. */
130static uint32_t g_cTypeErrors = 0;
131
132
133/** @name Options
134 * @{ */
135static enum
136{
137 kVBoxTpGAction_Nothing,
138 kVBoxTpGAction_GenerateHeader,
139 kVBoxTpGAction_GenerateWrapperHeader,
140 kVBoxTpGAction_GenerateObject
141} g_enmAction = kVBoxTpGAction_Nothing;
142static uint32_t g_cBits = HC_ARCH_BITS;
143static uint32_t g_cHostBits = HC_ARCH_BITS;
144static uint32_t g_fTypeContext = VTG_TYPE_CTX_R0;
145static const char *g_pszContextDefine = "IN_RING0";
146static const char *g_pszContextDefine2 = NULL;
147static bool g_fApplyCpp = false;
148static uint32_t g_cVerbosity = 0;
149static const char *g_pszOutput = NULL;
150static const char *g_pszScript = NULL;
151static const char *g_pszTempAsm = NULL;
152#ifdef RT_OS_DARWIN
153static const char *g_pszAssembler = "yasm";
154static const char *g_pszAssemblerFmtOpt = "-f";
155static const char g_szAssemblerFmtVal32[] = "macho32";
156static const char g_szAssemblerFmtVal64[] = "macho64";
157static const char g_szAssemblerOsDef[] = "RT_OS_DARWIN";
158#elif defined(RT_OS_OS2)
159static const char *pszAssembler = "nasm.exe";
160static const char *pszAssemblerFmtOpt = "-f";
161static const char g_szAssemblerFmtVal32[] = "obj";
162static const char g_szAssemblerFmtVal64[] = "elf64";
163static const char g_szAssemblerOsDef[] = "RT_OS_OS2";
164#elif defined(RT_OS_WINDOWS)
165static const char *g_pszAssembler = "yasm.exe";
166static const char *g_pszAssemblerFmtOpt = "-f";
167static const char g_szAssemblerFmtVal32[] = "win32";
168static const char g_szAssemblerFmtVal64[] = "win64";
169static const char g_szAssemblerOsDef[] = "RT_OS_WINDOWS";
170#else
171static const char *g_pszAssembler = "yasm";
172static const char *g_pszAssemblerFmtOpt = "-f";
173static const char g_szAssemblerFmtVal32[] = "elf32";
174static const char g_szAssemblerFmtVal64[] = "elf64";
175# ifdef RT_OS_FREEBSD
176static const char g_szAssemblerOsDef[] = "RT_OS_FREEBSD";
177# elif defined(RT_OS_NETBSD)
178static const char g_szAssemblerOsDef[] = "RT_OS_NETBSD";
179# elif defined(RT_OS_OPENBSD)
180static const char g_szAssemblerOsDef[] = "RT_OS_OPENBSD";
181# elif defined(RT_OS_LINUX)
182static const char g_szAssemblerOsDef[] = "RT_OS_LINUX";
183# elif defined(RT_OS_SOLARIS)
184static const char g_szAssemblerOsDef[] = "RT_OS_SOLARIS";
185# else
186# error "Port me!"
187# endif
188#endif
189static const char *g_pszAssemblerFmtVal = RT_CONCAT(g_szAssemblerFmtVal, HC_ARCH_BITS);
190static const char *g_pszAssemblerDefOpt = "-D";
191static const char *g_pszAssemblerIncOpt = "-I";
192static char g_szAssemblerIncVal[RTPATH_MAX];
193static const char *g_pszAssemblerIncVal = __FILE__ "/../../../include/";
194static const char *g_pszAssemblerOutputOpt = "-o";
195static unsigned g_cAssemblerOptions = 0;
196static const char *g_apszAssemblerOptions[32];
197static const char *g_pszProbeFnName = "SUPR0TracerFireProbe";
198static bool g_fProbeFnImported = true;
199static bool g_fPic = false;
200/** @} */
201
202
203
204
205/**
206 * Inserts a string into the string table, reusing any matching existing string
207 * if possible.
208 *
209 * @returns Read only string.
210 * @param pch The string to insert (need not be terminated).
211 * @param cch The length of the string.
212 */
213static const char *strtabInsertN(const char *pch, size_t cch)
214{
215 PVTGSTRING pStr = (PVTGSTRING)RTStrSpaceGetN(&g_StrSpace, pch, cch);
216 if (pStr)
217 return pStr->szString;
218
219 /*
220 * Create a new entry.
221 */
222 pStr = (PVTGSTRING)RTMemAlloc(RT_OFFSETOF(VTGSTRING, szString[cch + 1]));
223 if (!pStr)
224 return NULL;
225
226 pStr->Core.pszString = pStr->szString;
227 memcpy(pStr->szString, pch, cch);
228 pStr->szString[cch] = '\0';
229 pStr->offStrTab = UINT32_MAX;
230
231 bool fRc = RTStrSpaceInsert(&g_StrSpace, &pStr->Core);
232 Assert(fRc); NOREF(fRc);
233 return pStr->szString;
234}
235
236
237/**
238 * Retrieves the string table offset of the given string table string.
239 *
240 * @returns String table offset.
241 * @param pszStrTabString The string table string.
242 */
243static uint32_t strtabGetOff(const char *pszStrTabString)
244{
245 PVTGSTRING pStr = RT_FROM_MEMBER(pszStrTabString, VTGSTRING, szString[0]);
246 Assert(pStr->Core.pszString == pszStrTabString);
247 return pStr->offStrTab;
248}
249
250
251/**
252 * Invokes the assembler.
253 *
254 * @returns Exit code.
255 * @param pszOutput The output file.
256 * @param pszTempAsm The source file.
257 */
258static RTEXITCODE generateInvokeAssembler(const char *pszOutput, const char *pszTempAsm)
259{
260 const char *apszArgs[64];
261 unsigned iArg = 0;
262
263 apszArgs[iArg++] = g_pszAssembler;
264 apszArgs[iArg++] = g_pszAssemblerFmtOpt;
265 apszArgs[iArg++] = g_pszAssemblerFmtVal;
266 apszArgs[iArg++] = g_pszAssemblerDefOpt;
267 if (!strcmp(g_pszAssemblerFmtVal, "macho32") || !strcmp(g_pszAssemblerFmtVal, "macho64"))
268 apszArgs[iArg++] = "ASM_FORMAT_MACHO";
269 else if (!strcmp(g_pszAssemblerFmtVal, "obj") || !strcmp(g_pszAssemblerFmtVal, "omf"))
270 apszArgs[iArg++] = "ASM_FORMAT_OMF";
271 else if ( !strcmp(g_pszAssemblerFmtVal, "win32")
272 || !strcmp(g_pszAssemblerFmtVal, "win64")
273 || !strcmp(g_pszAssemblerFmtVal, "pe32")
274 || !strcmp(g_pszAssemblerFmtVal, "pe64")
275 || !strcmp(g_pszAssemblerFmtVal, "pe") )
276 apszArgs[iArg++] = "ASM_FORMAT_PE";
277 else if ( !strcmp(g_pszAssemblerFmtVal, "elf32")
278 || !strcmp(g_pszAssemblerFmtVal, "elf64")
279 || !strcmp(g_pszAssemblerFmtVal, "elf"))
280 apszArgs[iArg++] = "ASM_FORMAT_ELF";
281 else
282 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unknown assembler format '%s'", g_pszAssemblerFmtVal);
283 apszArgs[iArg++] = g_pszAssemblerDefOpt;
284 if (g_cBits == 32)
285 apszArgs[iArg++] = "ARCH_BITS=32";
286 else
287 apszArgs[iArg++] = "ARCH_BITS=64";
288 apszArgs[iArg++] = g_pszAssemblerDefOpt;
289 if (g_cHostBits == 32)
290 apszArgs[iArg++] = "HC_ARCH_BITS=32";
291 else
292 apszArgs[iArg++] = "HC_ARCH_BITS=64";
293 apszArgs[iArg++] = g_pszAssemblerDefOpt;
294 if (g_cBits == 32)
295 apszArgs[iArg++] = "RT_ARCH_X86";
296 else
297 apszArgs[iArg++] = "RT_ARCH_AMD64";
298 apszArgs[iArg++] = g_pszAssemblerDefOpt;
299 apszArgs[iArg++] = g_pszContextDefine;
300 if (g_pszContextDefine2)
301 {
302 apszArgs[iArg++] = g_pszAssemblerDefOpt;
303 apszArgs[iArg++] = g_pszContextDefine2;
304 }
305 if (g_szAssemblerOsDef[0])
306 {
307 apszArgs[iArg++] = g_pszAssemblerDefOpt;
308 apszArgs[iArg++] = g_szAssemblerOsDef;
309 }
310 apszArgs[iArg++] = g_pszAssemblerIncOpt;
311 apszArgs[iArg++] = g_pszAssemblerIncVal;
312 apszArgs[iArg++] = g_pszAssemblerOutputOpt;
313 apszArgs[iArg++] = pszOutput;
314 for (unsigned i = 0; i < g_cAssemblerOptions; i++)
315 apszArgs[iArg++] = g_apszAssemblerOptions[i];
316 apszArgs[iArg++] = pszTempAsm;
317 apszArgs[iArg] = NULL;
318 Assert(iArg <= RT_ELEMENTS(apszArgs));
319
320 if (g_cVerbosity > 1)
321 {
322 RTMsgInfo("Starting assmbler '%s' with arguments:\n", g_pszAssembler);
323 for (unsigned i = 0; i < iArg; i++)
324 RTMsgInfo(" #%02u: '%s'\n", i, apszArgs[i]);
325 }
326
327 RTPROCESS hProc;
328 int rc = RTProcCreate(apszArgs[0], apszArgs, RTENV_DEFAULT, RTPROC_FLAGS_SEARCH_PATH, &hProc);
329 if (RT_FAILURE(rc))
330 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to start '%s' (assembler): %Rrc", apszArgs[0], rc);
331
332 RTPROCSTATUS Status;
333 rc = RTProcWait(hProc, RTPROCWAIT_FLAGS_BLOCK, &Status);
334 if (RT_FAILURE(rc))
335 {
336 RTProcTerminate(hProc);
337 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcWait failed: %Rrc", rc);
338 }
339 if (Status.enmReason == RTPROCEXITREASON_SIGNAL)
340 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: signal %d", Status.iStatus);
341 if (Status.enmReason != RTPROCEXITREASON_NORMAL)
342 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The assembler failed: abend");
343 if (Status.iStatus != 0)
344 return RTMsgErrorExit((RTEXITCODE)Status.iStatus, "The assembler failed: exit code %d", Status.iStatus);
345
346 return RTEXITCODE_SUCCESS;
347}
348
349
350/**
351 * Worker that does the boring bits when generating a file.
352 *
353 * @returns Exit code.
354 * @param pszOutput The name of the output file.
355 * @param pszWhat What kind of file it is.
356 * @param pfnGenerator The callback function that provides the contents
357 * of the file.
358 */
359static RTEXITCODE generateFile(const char *pszOutput, const char *pszWhat,
360 RTEXITCODE (*pfnGenerator)(PSCMSTREAM))
361{
362 SCMSTREAM Strm;
363 int rc = ScmStreamInitForWriting(&Strm, NULL);
364 if (RT_FAILURE(rc))
365 return RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamInitForWriting returned %Rrc when generating the %s file",
366 rc, pszWhat);
367
368 RTEXITCODE rcExit = pfnGenerator(&Strm);
369 if (RT_FAILURE(ScmStreamGetStatus(&Strm)))
370 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Stream error %Rrc generating the %s file",
371 ScmStreamGetStatus(&Strm), pszWhat);
372 if (rcExit == RTEXITCODE_SUCCESS)
373 {
374 rc = ScmStreamWriteToFile(&Strm, "%s", pszOutput);
375 if (RT_FAILURE(rc))
376 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "ScmStreamWriteToFile returned %Rrc when writing '%s' (%s)",
377 rc, pszOutput, pszWhat);
378 if (rcExit == RTEXITCODE_SUCCESS)
379 {
380 if (g_cVerbosity > 0)
381 RTMsgInfo("Successfully generated '%s'.", pszOutput);
382 if (g_cVerbosity > 1)
383 {
384 RTMsgInfo("================ %s - start ================", pszWhat);
385 ScmStreamRewindForReading(&Strm);
386 const char *pszLine;
387 size_t cchLine;
388 SCMEOL enmEol;
389 while ((pszLine = ScmStreamGetLine(&Strm, &cchLine, &enmEol)) != NULL)
390 RTPrintf("%.*s\n", cchLine, pszLine);
391 RTMsgInfo("================ %s - end ================", pszWhat);
392 }
393 }
394 }
395 ScmStreamDelete(&Strm);
396 return rcExit;
397}
398
399
400/**
401 * @callback_method_impl{FNRTSTRSPACECALLBACK, Writes the string table strings.}
402 */
403static DECLCALLBACK(int) generateAssemblyStrTabCallback(PRTSTRSPACECORE pStr, void *pvUser)
404{
405 PVTGSTRING pVtgStr = (PVTGSTRING)pStr;
406 PSCMSTREAM pStrm = (PSCMSTREAM)pvUser;
407
408 pVtgStr->offStrTab = g_offStrTab;
409 g_offStrTab += (uint32_t)pVtgStr->Core.cchString + 1;
410
411 ScmStreamPrintf(pStrm,
412 " db '%s', 0 ; off=%u len=%zu\n",
413 pVtgStr->szString, pVtgStr->offStrTab, pVtgStr->Core.cchString);
414 return VINF_SUCCESS;
415}
416
417
418/**
419 * Generate assembly source that can be turned into an object file.
420 *
421 * (This is a generateFile callback.)
422 *
423 * @returns Exit code.
424 * @param pStrm The output stream.
425 */
426static RTEXITCODE generateAssembly(PSCMSTREAM pStrm)
427{
428 PVTGPROVIDER pProvider;
429 PVTGPROBE pProbe;
430 PVTGARG pArg;
431
432
433 if (g_cVerbosity > 0)
434 RTMsgInfo("Generating assembly code...");
435
436 /*
437 * Write the file header.
438 */
439 ScmStreamPrintf(pStrm,
440 "; $Id: VBoxTpG.cpp 43055 2012-08-28 23:50:20Z vboxsync $ \n"
441 ";; @file\n"
442 "; Automatically generated from %s. Do NOT edit!\n"
443 ";\n"
444 "\n"
445 "%%include \"iprt/asmdefs.mac\"\n"
446 "\n"
447 "\n"
448 ";"
449 "; We put all the data in a dedicated section / segment.\n"
450 ";\n"
451 "; In order to find the probe location specifiers, we do the necessary\n"
452 "; trickery here, ASSUMING that this object comes in first in the link\n"
453 "; editing process.\n"
454 ";\n"
455 "%%ifdef ASM_FORMAT_OMF\n"
456 " %%macro VTG_GLOBAL 2\n"
457 " global NAME(%%1)\n"
458 " NAME(%%1):\n"
459 " %%endmacro\n"
460 " segment VTG.Obj public CLASS=VTG align=4096 use32\n"
461 "\n"
462 "%%elifdef ASM_FORMAT_MACHO\n"
463 " %%macro VTG_GLOBAL 2\n"
464 " global NAME(%%1)\n"
465 " NAME(%%1):\n"
466 " %%endmacro\n"
467 " %%ifdef IN_RING3\n"
468 " %%define VTG_NEW_MACHO_LINKER\n"
469 " %%elif ARCH_BITS == 64\n"
470 " %%define VTG_NEW_MACHO_LINKER\n"
471 " %%elifdef IN_RING0_AGNOSTIC\n"
472 " %%define VTG_NEW_MACHO_LINKER\n"
473 " %%endif\n"
474 " %%ifdef VTG_NEW_MACHO_LINKER\n"
475 " ; Section order hack!\n"
476 " ; With the ld64-97.17 linker there was a problem with it determining the section\n"
477 " ; order based on symbol references. The references to the start and end of the\n"
478 " ; __VTGPrLc section forced it in front of __VTGObj, we want __VTGObj first.\n"
479 " extern section$start$__VTG$__VTGObj\n"
480 " extern section$end$__VTG$__VTGObj\n"
481 " %%else\n"
482 " ; Creating 32-bit kext of the type MH_OBJECT. No fancy section end/start symbols handy.\n"
483 " [section __VTG __VTGObj align=16]\n"
484 "VTG_GLOBAL g_aVTGObj_LinkerPleaseNoticeMe, data\n"
485 " [section __VTG __VTGPrLc.Begin align=16]\n"
486 "VTG_GLOBAL g_aVTGPrLc, data\n"
487 " [section __VTG __VTGPrLc align=16]\n"
488 "VTG_GLOBAL g_aVTGPrLc_LinkerPleaseNoticeMe, data\n"
489 " [section __VTG __VTGPrLc.End align=16]\n"
490 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
491 " %%endif\n"
492 " [section __VTG __VTGObj]\n"
493 "\n"
494 "%%elifdef ASM_FORMAT_PE\n"
495 " %%macro VTG_GLOBAL 2\n"
496 " global NAME(%%1)\n"
497 " NAME(%%1):\n"
498 " %%endmacro\n"
499 " [section VTGPrLc.Begin data align=64]\n"
500 /*" times 16 db 0xcc\n"*/
501 "VTG_GLOBAL g_aVTGPrLc, data\n"
502 " [section VTGPrLc.Data data align=4]\n"
503 " [section VTGPrLc.End data align=4]\n"
504 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
505 /*" times 16 db 0xcc\n"*/
506 " [section VTGObj data align=32]\n"
507 "\n"
508 "%%elifdef ASM_FORMAT_ELF\n"
509 " %%macro VTG_GLOBAL 2\n"
510 " global NAME(%%1):%%2 hidden\n"
511 " NAME(%%1):\n"
512 " %%endmacro\n"
513 " [section .VTGData progbits alloc noexec write align=4096]\n"
514 " [section .VTGPrLc.Begin progbits alloc noexec write align=32]\n"
515 " dd 0,0,0,0, 0,0,0,0\n"
516 "VTG_GLOBAL g_aVTGPrLc, data\n"
517 " [section .VTGPrLc progbits alloc noexec write align=1]\n"
518 " [section .VTGPrLc.End progbits alloc noexec write align=1]\n"
519 "VTG_GLOBAL g_aVTGPrLc_End, data\n"
520 " dd 0,0,0,0, 0,0,0,0\n"
521 " [section .VTGData]\n"
522 "\n"
523 "%%else\n"
524 " %%error \"ASM_FORMAT_XXX is not defined\"\n"
525 "%%endif\n"
526 "\n"
527 "\n"
528 "VTG_GLOBAL g_VTGObjHeader, data\n"
529 " ;0 1 2 3\n"
530 " ;012345678901234567890123456789012\n"
531 " db 'VTG Object Header v1.5', 0, 0\n"
532 " dd %u\n"
533 " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_VTGObjHeader)\n"
534 " dd NAME(g_achVTGStringTable) - NAME(g_VTGObjHeader)\n"
535 " dd NAME(g_achVTGStringTable_End) - NAME(g_achVTGStringTable)\n"
536 " dd NAME(g_aVTGArgLists) - NAME(g_VTGObjHeader)\n"
537 " dd NAME(g_aVTGArgLists_End) - NAME(g_aVTGArgLists)\n"
538 " dd NAME(g_aVTGProbes) - NAME(g_VTGObjHeader)\n"
539 " dd NAME(g_aVTGProbes_End) - NAME(g_aVTGProbes)\n"
540 " dd NAME(g_aVTGProviders) - NAME(g_VTGObjHeader)\n"
541 " dd NAME(g_aVTGProviders_End) - NAME(g_aVTGProviders)\n"
542 " dd NAME(g_acVTGProbeEnabled) - NAME(g_VTGObjHeader)\n"
543 " dd NAME(g_acVTGProbeEnabled_End) - NAME(g_acVTGProbeEnabled)\n"
544 " dd 0\n"
545 " dd 0\n"
546 "%%ifdef VTG_NEW_MACHO_LINKER\n"
547 " extern section$start$__VTG$__VTGPrLc\n"
548 " RTCCPTR_DEF section$start$__VTG$__VTGPrLc\n"
549 " %%if ARCH_BITS == 32\n"
550 " dd 0\n"
551 " %%endif\n"
552 " extern section$end$__VTG$__VTGPrLc\n"
553 " RTCCPTR_DEF section$end$__VTG$__VTGPrLc\n"
554 " %%if ARCH_BITS == 32\n"
555 " dd 0\n"
556 " %%endif\n"
557 "%%else\n"
558 " RTCCPTR_DEF NAME(g_aVTGPrLc)\n"
559 " %%if ARCH_BITS == 32\n"
560 " dd 0\n"
561 " %%endif\n"
562 " RTCCPTR_DEF NAME(g_aVTGPrLc_End)\n"
563 " %%if ARCH_BITS == 32\n"
564 " dd 0\n"
565 " %%endif\n"
566 "%%endif\n"
567 ,
568 g_pszScript, g_cBits);
569 RTUUID Uuid;
570 int rc = RTUuidCreate(&Uuid);
571 if (RT_FAILURE(rc))
572 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTUuidCreate failed: %Rrc", rc);
573 ScmStreamPrintf(pStrm,
574 " dd 0%08xh, 0%08xh, 0%08xh, 0%08xh\n"
575 "%%ifdef VTG_NEW_MACHO_LINKER\n"
576 " RTCCPTR_DEF section$start$__VTG$__VTGObj\n"
577 " %%if ARCH_BITS == 32\n"
578 " dd 0\n"
579 " %%endif\n"
580 "%%else\n"
581 " dd 0, 0\n"
582 "%%endif\n"
583 " dd 0, 0\n"
584 , Uuid.au32[0], Uuid.au32[1], Uuid.au32[2], Uuid.au32[3]);
585
586 /*
587 * Dump the string table before we start using the strings.
588 */
589 ScmStreamPrintf(pStrm,
590 "\n"
591 ";\n"
592 "; The string table.\n"
593 ";\n"
594 "VTG_GLOBAL g_achVTGStringTable, data\n");
595 g_offStrTab = 0;
596 RTStrSpaceEnumerate(&g_StrSpace, generateAssemblyStrTabCallback, pStrm);
597 ScmStreamPrintf(pStrm,
598 "VTG_GLOBAL g_achVTGStringTable_End, data\n");
599
600 /*
601 * Write out the argument lists before we use them.
602 */
603 ScmStreamPrintf(pStrm,
604 "\n"
605 ";\n"
606 "; The argument lists.\n"
607 ";\n"
608 "ALIGNDATA(16)\n"
609 "VTG_GLOBAL g_aVTGArgLists, data\n");
610 uint32_t off = 0;
611 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
612 {
613 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
614 {
615 if (pProbe->offArgList != UINT32_MAX)
616 continue;
617
618 /* Write it. */
619 pProbe->offArgList = off;
620 ScmStreamPrintf(pStrm,
621 " ; off=%u\n"
622 " db %2u ; Argument count\n"
623 " db %u ; fHaveLargeArgs\n"
624 " db 0, 0 ; Reserved\n"
625 , off, pProbe->cArgs, (int)pProbe->fHaveLargeArgs);
626 off += 4;
627 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
628 {
629 ScmStreamPrintf(pStrm,
630 " dd %8u ; type '%s' (name '%s')\n"
631 " dd 0%08xh ; type flags\n",
632 strtabGetOff(pArg->pszTracerType), pArg->pszTracerType, pArg->pszName,
633 pArg->fType);
634 off += 8;
635 }
636
637 /* Look for matching argument lists (lazy bird walks the whole list). */
638 PVTGPROVIDER pProv2;
639 RTListForEach(&g_ProviderHead, pProv2, VTGPROVIDER, ListEntry)
640 {
641 PVTGPROBE pProbe2;
642 RTListForEach(&pProvider->ProbeHead, pProbe2, VTGPROBE, ListEntry)
643 {
644 if (pProbe2->offArgList != UINT32_MAX)
645 continue;
646 if (pProbe2->cArgs != pProbe->cArgs)
647 continue;
648
649 PVTGARG pArg2;
650 pArg = RTListNodeGetNext(&pProbe->ArgHead, VTGARG, ListEntry);
651 pArg2 = RTListNodeGetNext(&pProbe2->ArgHead, VTGARG, ListEntry);
652 int32_t cArgs = pProbe->cArgs;
653 while ( cArgs-- > 0
654 && pArg2->pszTracerType == pArg->pszTracerType
655 && pArg2->fType == pArg->fType)
656 {
657 pArg = RTListNodeGetNext(&pArg->ListEntry, VTGARG, ListEntry);
658 pArg2 = RTListNodeGetNext(&pArg2->ListEntry, VTGARG, ListEntry);
659 }
660 if (cArgs >= 0)
661 continue;
662 pProbe2->offArgList = pProbe->offArgList;
663 }
664 }
665 }
666 }
667 ScmStreamPrintf(pStrm,
668 "VTG_GLOBAL g_aVTGArgLists_End, data\n");
669
670
671 /*
672 * Probe definitions.
673 */
674 ScmStreamPrintf(pStrm,
675 "\n"
676 ";\n"
677 "; Prob definitions.\n"
678 ";\n"
679 "ALIGNDATA(16)\n"
680 "VTG_GLOBAL g_aVTGProbes, data\n"
681 "\n");
682 uint32_t iProvider = 0;
683 uint32_t iProbe = 0;
684 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
685 {
686 pProvider->iFirstProbe = iProbe;
687 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
688 {
689 ScmStreamPrintf(pStrm,
690 "VTG_GLOBAL g_VTGProbeData_%s_%s, data ; idx=#%4u\n"
691 " dd %6u ; offName\n"
692 " dd %6u ; offArgList\n"
693 " dw (NAME(g_cVTGProbeEnabled_%s_%s) - NAME(g_acVTGProbeEnabled)) / 4 ; idxEnabled\n"
694 " dw %6u ; idxProvider\n"
695 " dd NAME(g_VTGObjHeader) - NAME(g_VTGProbeData_%s_%s) ; offObjHdr\n"
696 ,
697 pProvider->pszName, pProbe->pszMangledName, iProbe,
698 strtabGetOff(pProbe->pszUnmangledName),
699 pProbe->offArgList,
700 pProvider->pszName, pProbe->pszMangledName,
701 iProvider,
702 pProvider->pszName, pProbe->pszMangledName
703 );
704 pProbe->iProbe = iProbe;
705 iProbe++;
706 }
707 pProvider->cProbes = iProbe - pProvider->iFirstProbe;
708 iProvider++;
709 }
710 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProbes_End, data\n");
711
712 /*
713 * The provider data.
714 */
715 ScmStreamPrintf(pStrm,
716 "\n"
717 ";\n"
718 "; Provider data.\n"
719 ";\n"
720 "ALIGNDATA(16)\n"
721 "VTG_GLOBAL g_aVTGProviders, data\n");
722 iProvider = 0;
723 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
724 {
725 ScmStreamPrintf(pStrm,
726 " ; idx=#%4u - %s\n"
727 " dd %6u ; name\n"
728 " dw %6u ; index of first probe\n"
729 " dw %6u ; count of probes\n"
730 " db %d, %d, %d ; AttrSelf\n"
731 " db %d, %d, %d ; AttrModules\n"
732 " db %d, %d, %d ; AttrFunctions\n"
733 " db %d, %d, %d ; AttrName\n"
734 " db %d, %d, %d ; AttrArguments\n"
735 " db 0 ; reserved\n"
736 ,
737 iProvider, pProvider->pszName,
738 strtabGetOff(pProvider->pszName),
739 pProvider->iFirstProbe,
740 pProvider->cProbes,
741 pProvider->AttrSelf.enmCode, pProvider->AttrSelf.enmData, pProvider->AttrSelf.enmDataDep,
742 pProvider->AttrModules.enmCode, pProvider->AttrModules.enmData, pProvider->AttrModules.enmDataDep,
743 pProvider->AttrFunctions.enmCode, pProvider->AttrFunctions.enmData, pProvider->AttrFunctions.enmDataDep,
744 pProvider->AttrName.enmCode, pProvider->AttrName.enmData, pProvider->AttrName.enmDataDep,
745 pProvider->AttrArguments.enmCode, pProvider->AttrArguments.enmData, pProvider->AttrArguments.enmDataDep);
746 iProvider++;
747 }
748 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_aVTGProviders_End, data\n");
749
750 /*
751 * Declare the probe enable flags.
752 *
753 * These must be placed at the end so they'll end up adjacent to the probe
754 * locations. This is important for reducing the amount of memory we need
755 * to lock down for user mode modules.
756 */
757 ScmStreamPrintf(pStrm,
758 ";\n"
759 "; Probe enabled flags.\n"
760 ";\n"
761 "ALIGNDATA(16)\n"
762 "VTG_GLOBAL g_acVTGProbeEnabled, data\n"
763 );
764 uint32_t cProbes = 0;
765 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
766 {
767 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
768 {
769 ScmStreamPrintf(pStrm,
770 "VTG_GLOBAL g_cVTGProbeEnabled_%s_%s, data\n"
771 " dd 0\n",
772 pProvider->pszName, pProbe->pszMangledName);
773 cProbes++;
774 }
775 }
776 ScmStreamPrintf(pStrm, "VTG_GLOBAL g_acVTGProbeEnabled_End, data\n");
777 if (cProbes >= _32K)
778 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Too many probes: %u (max %u)", cProbes, _32K - 1);
779
780
781 /*
782 * Emit code for the stub functions.
783 */
784 bool const fWin64 = g_cBits == 64 && (!strcmp(g_pszAssemblerFmtVal, "win64") || !strcmp(g_pszAssemblerFmtVal, "pe64"));
785 bool const fMachO64 = g_cBits == 64 && !strcmp(g_pszAssemblerFmtVal, "macho64");
786 bool const fMachO32 = g_cBits == 32 && !strcmp(g_pszAssemblerFmtVal, "macho32");
787 ScmStreamPrintf(pStrm,
788 "\n"
789 ";\n"
790 "; Prob stubs.\n"
791 ";\n"
792 "BEGINCODE\n"
793 "extern %sNAME(%s)\n",
794 g_fProbeFnImported ? "IMP" : "",
795 g_pszProbeFnName);
796 if (fMachO64 && g_fProbeFnImported && !g_fPic)
797 ScmStreamPrintf(pStrm,
798 "g_pfnVtgProbeFn:\n"
799 " dq NAME(%s)\n",
800 g_pszProbeFnName);
801
802 RTListForEach(&g_ProviderHead, pProvider, VTGPROVIDER, ListEntry)
803 {
804 RTListForEach(&pProvider->ProbeHead, pProbe, VTGPROBE, ListEntry)
805 {
806 ScmStreamPrintf(pStrm,
807 "\n"
808 "VTG_GLOBAL VTGProbeStub_%s_%s, function; (VBOXTPGPROBELOC pVTGProbeLoc",
809 pProvider->pszName, pProbe->pszMangledName);
810 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
811 {
812 ScmStreamPrintf(pStrm, ", %s %s", pArg->pszTracerType, pArg->pszName);
813 }
814 ScmStreamPrintf(pStrm,
815 ");\n");
816
817 /*
818 * Check if the probe in question is enabled.
819 */
820 if (g_cBits == 32)
821 ScmStreamPrintf(pStrm,
822 " mov eax, [esp + 4]\n"
823 " test byte [eax+3], 0x80 ; fEnabled == true?\n"
824 " jz .return ; jump on false\n");
825 else if (fWin64)
826 ScmStreamPrintf(pStrm,
827 " test byte [rcx+3], 0x80 ; fEnabled == true?\n"
828 " jz .return ; jump on false\n");
829 else
830 ScmStreamPrintf(pStrm,
831 " test byte [rdi+3], 0x80 ; fEnabled == true?\n"
832 " jz .return ; jump on false\n");
833
834 /*
835 * Jump to the fire-probe function.
836 */
837 if (g_cBits == 32)
838 ScmStreamPrintf(pStrm, g_fPic ?
839 " jmp %s wrt ..plt\n"
840 : g_fProbeFnImported ?
841 " mov ecx, IMP2(%s)\n"
842 " jmp ecx\n"
843 :
844 " jmp NAME(%s)\n"
845 , g_pszProbeFnName);
846 else if (fWin64)
847 ScmStreamPrintf(pStrm, g_fProbeFnImported ?
848 " mov rax, IMP2(%s)\n"
849 " jmp rax\n"
850 :
851 " jmp NAME(%s)\n"
852 , g_pszProbeFnName);
853 else if (fMachO64 && g_fProbeFnImported)
854 ScmStreamPrintf(pStrm,
855 " jmp [g_pfnVtgProbeFn wrt rip]\n");
856 else
857 ScmStreamPrintf(pStrm, g_fPic ?
858 " jmp [rel %s wrt ..got]\n"
859 : g_fProbeFnImported ?
860 " lea rax, [IMP2(%s)]\n"
861 " jmp rax\n"
862 :
863 " jmp NAME(%s)\n"
864 , g_pszProbeFnName);
865
866 ScmStreamPrintf(pStrm,
867 ".return:\n"
868 " ret ; The probe was disabled, return\n"
869 "\n");
870 }
871 }
872
873 return RTEXITCODE_SUCCESS;
874}
875
876
877static RTEXITCODE generateObject(const char *pszOutput, const char *pszTempAsm)
878{
879 if (!pszTempAsm)
880 {
881 size_t cch = strlen(pszOutput);
882 char *psz = (char *)alloca(cch + sizeof(".asm"));
883 memcpy(psz, pszOutput, cch);
884 memcpy(psz + cch, ".asm", sizeof(".asm"));
885 pszTempAsm = psz;
886 }
887
888 RTEXITCODE rcExit = generateFile(pszTempAsm, "assembly", generateAssembly);
889 if (rcExit == RTEXITCODE_SUCCESS)
890 rcExit = generateInvokeAssembler(pszOutput, pszTempAsm);
891 RTFileDelete(pszTempAsm);
892 return rcExit;
893}
894
895
896static RTEXITCODE generateProbeDefineName(char *pszBuf, size_t cbBuf, const char *pszProvider, const char *pszProbe)
897{
898 size_t cbMax = strlen(pszProvider) + 1 + strlen(pszProbe) + 1;
899 if (cbMax > cbBuf || cbMax > 80)
900 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Probe '%s' in provider '%s' ends up with a too long defined\n", pszProbe, pszProvider);
901
902 while (*pszProvider)
903 *pszBuf++ = RT_C_TO_UPPER(*pszProvider++);
904
905 *pszBuf++ = '_';
906
907 while (*pszProbe)
908 {
909 if (pszProbe[0] == '_' && pszProbe[1] == '_')
910 pszProbe++;
911 *pszBuf++ = RT_C_TO_UPPER(*pszProbe++);
912 }
913
914 *pszBuf = '\0';
915 return RTEXITCODE_SUCCESS;
916}
917
918
919/**
920 * Called via generateFile to generate the header file.
921 *
922 * @returns Exit code status.
923 * @param pStrm The output stream.
924 */
925static RTEXITCODE generateHeader(PSCMSTREAM pStrm)
926{
927 /*
928 * Calc the double inclusion blocker define and then write the file header.
929 */
930 char szTmp[4096];
931 const char *pszName = RTPathFilename(g_pszScript);
932 size_t cchName = strlen(pszName);
933 if (cchName >= sizeof(szTmp) - 64)
934 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
935 szTmp[0] = '_';
936 szTmp[1] = '_';
937 szTmp[2] = '_';
938 memcpy(&szTmp[3], pszName, cchName);
939 szTmp[3 + cchName + 0] = '_';
940 szTmp[3 + cchName + 1] = '_';
941 szTmp[3 + cchName + 2] = '_';
942 szTmp[3 + cchName + 3] = '\0';
943 char *psz = &szTmp[3];
944 while (*psz)
945 {
946 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
947 *psz = '_';
948 psz++;
949 }
950
951 ScmStreamPrintf(pStrm,
952 "/* $Id: VBoxTpG.cpp 43055 2012-08-28 23:50:20Z vboxsync $ */\n"
953 "/** @file\n"
954 " * Automatically generated from %s. Do NOT edit!\n"
955 " */\n"
956 "\n"
957 "#ifndef %s\n"
958 "#define %s\n"
959 "\n"
960 "#include <VBox/VBoxTpG.h>\n"
961 "\n"
962 "#ifndef %s\n"
963 "# error \"Expected '%s' to be defined\"\n"
964 "#endif\n"
965 "\n"
966 "RT_C_DECLS_BEGIN\n"
967 "\n"
968 "#ifdef VBOX_WITH_DTRACE\n"
969 "\n"
970 "# ifdef _MSC_VER\n"
971 "# pragma data_seg(VTG_LOC_SECT)\n"
972 "# pragma data_seg()\n"
973 "# endif\n"
974 "\n"
975 ,
976 g_pszScript,
977 szTmp,
978 szTmp,
979 g_pszContextDefine,
980 g_pszContextDefine);
981
982 /*
983 * Declare data, code and macros for each probe.
984 */
985 PVTGPROVIDER pProv;
986 PVTGPROBE pProbe;
987 PVTGARG pArg;
988 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
989 {
990 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
991 {
992 PVTGARG const pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
993
994 ScmStreamPrintf(pStrm,
995 "extern uint32_t g_cVTGProbeEnabled_%s_%s;\n"
996 "extern VTGDESCPROBE g_VTGProbeData_%s_%s;\n"
997 "DECLASM(void) VTGProbeStub_%s_%s(PVTGPROBELOC",
998 pProv->pszName, pProbe->pszMangledName,
999 pProv->pszName, pProbe->pszMangledName,
1000 pProv->pszName, pProbe->pszMangledName);
1001 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1002 {
1003 ScmStreamPrintf(pStrm, ", %s", pArg->pszCtxType);
1004 }
1005 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1006 ScmStreamPrintf(pStrm,
1007 ");\n"
1008 "# define %s_ENABLED() \\\n"
1009 " (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \n"
1010 "# define %s("
1011 , szTmp,
1012 pProv->pszName, pProbe->pszMangledName,
1013 szTmp);
1014 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1015 {
1016 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1017 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1018 else
1019 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1020 }
1021 ScmStreamPrintf(pStrm,
1022 ") \\\n"
1023 " do { \\\n"
1024 " if (RT_UNLIKELY(g_cVTGProbeEnabled_%s_%s)) \\\n"
1025 " { \\\n"
1026 " VTG_DECL_VTGPROBELOC(s_VTGProbeLoc) = \\\n"
1027 " { __LINE__, 0, 0, __FUNCTION__, &g_VTGProbeData_%s_%s }; \\\n"
1028 " VTGProbeStub_%s_%s(&s_VTGProbeLoc",
1029 pProv->pszName, pProbe->pszMangledName,
1030 pProv->pszName, pProbe->pszMangledName,
1031 pProv->pszName, pProbe->pszMangledName);
1032 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1033 {
1034 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName);
1035 }
1036 ScmStreamPrintf(pStrm,
1037 "); \\\n"
1038 " } \\\n"
1039 " { \\\n" );
1040 uint32_t iArg = 0;
1041 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1042 {
1043 if ((pArg->fType & (VTG_TYPE_FIXED_SIZED | VTG_TYPE_AUTO_CONV_PTR)) == VTG_TYPE_FIXED_SIZED)
1044 ScmStreamPrintf(pStrm,
1045 " AssertCompile(sizeof(%s) == %u); \\\n"
1046 " AssertCompile(sizeof(%s) <= %u); \\\n",
1047 pArg->pszTracerType, pArg->fType & VTG_TYPE_SIZE_MASK,
1048 pArg->pszName, pArg->fType & VTG_TYPE_SIZE_MASK);
1049 else if (pArg->fType & (VTG_TYPE_POINTER | VTG_TYPE_HC_ARCH_SIZED))
1050 ScmStreamPrintf(pStrm,
1051 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n"
1052 " AssertCompile(sizeof(%s) <= sizeof(uintptr_t)); \\\n",
1053 pArg->pszName,
1054 pArg->pszTracerType);
1055 iArg++;
1056 }
1057 ScmStreamPrintf(pStrm,
1058 " } \\\n"
1059 " } while (0)\n"
1060 "\n");
1061 }
1062 }
1063
1064 ScmStreamPrintf(pStrm,
1065 "\n"
1066 "#else\n"
1067 "\n");
1068 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1069 {
1070 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1071 {
1072 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1073 ScmStreamPrintf(pStrm,
1074 "# define %s_ENABLED() (false)\n"
1075 "# define %s("
1076 , szTmp, szTmp);
1077 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1078 {
1079 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1080 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1081 else
1082 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1083 }
1084 ScmStreamPrintf(pStrm,
1085 ") do { } while (0)\n");
1086 }
1087 }
1088
1089 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1090 "#endif\n"
1091 "\n"
1092 "RT_C_DECLS_END\n"
1093 "#endif\n"));
1094 return RTEXITCODE_SUCCESS;
1095}
1096
1097
1098/**
1099 * Called via generateFile to generate the wrapper header file.
1100 *
1101 * @returns Exit code status.
1102 * @param pStrm The output stream.
1103 */
1104static RTEXITCODE generateWrapperHeader(PSCMSTREAM pStrm)
1105{
1106 /*
1107 * Calc the double inclusion blocker define and then write the file header.
1108 */
1109 char szTmp[4096];
1110 const char *pszName = RTPathFilename(g_pszScript);
1111 size_t cchName = strlen(pszName);
1112 if (cchName >= sizeof(szTmp) - 64)
1113 return RTMsgErrorExit(RTEXITCODE_FAILURE, "File name is too long '%s'", pszName);
1114 szTmp[0] = '_';
1115 szTmp[1] = '_';
1116 szTmp[2] = '_';
1117 memcpy(&szTmp[3], pszName, cchName);
1118 strcpy(&szTmp[3 + cchName ], "___WRAPPER___");
1119 char *psz = &szTmp[3];
1120 while (*psz)
1121 {
1122 if (!RT_C_IS_ALNUM(*psz) && *psz != '_')
1123 *psz = '_';
1124 psz++;
1125 }
1126
1127 ScmStreamPrintf(pStrm,
1128 "/* $Id: VBoxTpG.cpp 43055 2012-08-28 23:50:20Z vboxsync $ */\n"
1129 "/** @file\n"
1130 " * Automatically generated from %s. Do NOT edit!\n"
1131 " */\n"
1132 "\n"
1133 "#ifndef %s\n"
1134 "#define %s\n"
1135 "\n"
1136 "#include <VBox/VBoxTpG.h>\n"
1137 "\n"
1138 "#ifndef %s\n"
1139 "# error \"Expected '%s' to be defined\"\n"
1140 "#endif\n"
1141 "\n"
1142 "#ifdef VBOX_WITH_DTRACE\n"
1143 "\n"
1144 ,
1145 g_pszScript,
1146 szTmp,
1147 szTmp,
1148 g_pszContextDefine,
1149 g_pszContextDefine);
1150
1151 /*
1152 * Declare macros for each probe.
1153 */
1154 PVTGPROVIDER pProv;
1155 PVTGPROBE pProbe;
1156 PVTGARG pArg;
1157 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1158 {
1159 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1160 {
1161 PVTGARG const pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
1162
1163 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1164 ScmStreamPrintf(pStrm,
1165 "# define %s("
1166 , szTmp);
1167 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1168 {
1169 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1170 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1171 else
1172 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1173 }
1174 ScmStreamPrintf(pStrm,
1175 ") \\\n"
1176 " do { \\\n"
1177 " if (RT_UNLIKELY(%s_ENABLED())) \\\n"
1178 " { \\\n"
1179 " %s_ORIGINAL("
1180 , szTmp, szTmp);
1181 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1182 {
1183 const char *pszFmt = pArg->pszArgPassingFmt;
1184 if (pArg->fType & VTG_TYPE_AUTO_CONV_PTR)
1185 {
1186 /* Casting is required. ASSUMES sizeof(RTR0PTR) == sizeof(RTR3PTR) - safe! */
1187 pszFmt += sizeof(", ") - 1;
1188 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1189 ScmStreamPrintf(pStrm, "(%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1190 else
1191 ScmStreamPrintf(pStrm, ", (%s)%M", pArg->pszTracerType, pszFmt, pArg->pszName);
1192 }
1193 else
1194 {
1195 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1196 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt + sizeof(", ") - 1, pArg->pszName);
1197 else
1198 ScmStreamPrintf(pStrm, pArg->pszArgPassingFmt, pArg->pszName);
1199 }
1200 }
1201 ScmStreamPrintf(pStrm,
1202 "); \\\n"
1203 " } \\\n"
1204 " } while (0)\n"
1205 "\n");
1206 }
1207 }
1208
1209 ScmStreamPrintf(pStrm,
1210 "\n"
1211 "#else\n"
1212 "\n");
1213 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1214 {
1215 RTListForEach(&pProv->ProbeHead, pProbe, VTGPROBE, ListEntry)
1216 {
1217 generateProbeDefineName(szTmp, sizeof(szTmp), pProv->pszName, pProbe->pszMangledName);
1218 ScmStreamPrintf(pStrm,
1219 "# define %s("
1220 , szTmp);
1221 RTListForEach(&pProbe->ArgHead, pArg, VTGARG, ListEntry)
1222 {
1223 if (RTListNodeIsFirst(&pProbe->ArgHead, &pArg->ListEntry))
1224 ScmStreamPrintf(pStrm, "%s", pArg->pszName);
1225 else
1226 ScmStreamPrintf(pStrm, ", %s", pArg->pszName);
1227 }
1228 ScmStreamPrintf(pStrm,
1229 ") do { } while (0)\n");
1230 }
1231 }
1232
1233 ScmStreamWrite(pStrm, RT_STR_TUPLE("\n"
1234 "#endif\n"
1235 "\n"
1236 "#endif\n"));
1237 return RTEXITCODE_SUCCESS;
1238}
1239
1240
1241/**
1242 * Parser error with line and position.
1243 *
1244 * @returns RTEXITCODE_FAILURE.
1245 * @param pStrm The stream.
1246 * @param cb The offset from the current position to the
1247 * point of failure.
1248 * @param pszMsg The message to display.
1249 */
1250static RTEXITCODE parseError(PSCMSTREAM pStrm, size_t cb, const char *pszMsg)
1251{
1252 if (cb)
1253 ScmStreamSeekRelative(pStrm, -(ssize_t)cb);
1254 size_t const off = ScmStreamTell(pStrm);
1255 size_t const iLine = ScmStreamTellLine(pStrm);
1256 ScmStreamSeekByLine(pStrm, iLine);
1257 size_t const offLine = ScmStreamTell(pStrm);
1258
1259 RTPrintf("%s:%d:%zd: error: %s.\n", g_pszScript, iLine + 1, off - offLine + 1, pszMsg);
1260
1261 size_t cchLine;
1262 SCMEOL enmEof;
1263 const char *pszLine = ScmStreamGetLineByNo(pStrm, iLine, &cchLine, &enmEof);
1264 if (pszLine)
1265 RTPrintf(" %.*s\n"
1266 " %*s^\n",
1267 cchLine, pszLine, off - offLine, "");
1268 return RTEXITCODE_FAILURE;
1269}
1270
1271
1272/**
1273 * Parser error with line and position.
1274 *
1275 * @returns RTEXITCODE_FAILURE.
1276 * @param pStrm The stream.
1277 * @param cb The offset from the current position to the
1278 * point of failure.
1279 * @param pszMsg The message to display.
1280 */
1281static RTEXITCODE parseErrorAbs(PSCMSTREAM pStrm, size_t off, const char *pszMsg)
1282{
1283 ScmStreamSeekAbsolute(pStrm, off);
1284 return parseError(pStrm, 0, pszMsg);
1285}
1286
1287/**
1288 * Handles a C++ one line comment.
1289 *
1290 * @returns Exit code.
1291 * @param pStrm The stream.
1292 */
1293static RTEXITCODE parseOneLineComment(PSCMSTREAM pStrm)
1294{
1295 ScmStreamSeekByLine(pStrm, ScmStreamTellLine(pStrm) + 1);
1296 return RTEXITCODE_SUCCESS;
1297}
1298
1299/**
1300 * Handles a multi-line C/C++ comment.
1301 *
1302 * @returns Exit code.
1303 * @param pStrm The stream.
1304 */
1305static RTEXITCODE parseMultiLineComment(PSCMSTREAM pStrm)
1306{
1307 unsigned ch;
1308 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1309 {
1310 if (ch == '*')
1311 {
1312 do
1313 ch = ScmStreamGetCh(pStrm);
1314 while (ch == '*');
1315 if (ch == '/')
1316 return RTEXITCODE_SUCCESS;
1317 }
1318 }
1319
1320 parseError(pStrm, 1, "Expected end of comment, got end of file");
1321 return RTEXITCODE_FAILURE;
1322}
1323
1324
1325/**
1326 * Skips spaces and comments.
1327 *
1328 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE.
1329 * @param pStrm The stream..
1330 */
1331static RTEXITCODE parseSkipSpacesAndComments(PSCMSTREAM pStrm)
1332{
1333 unsigned ch;
1334 while ((ch = ScmStreamPeekCh(pStrm)) != ~(unsigned)0)
1335 {
1336 if (!RT_C_IS_SPACE(ch) && ch != '/')
1337 return RTEXITCODE_SUCCESS;
1338 unsigned ch2 = ScmStreamGetCh(pStrm); AssertBreak(ch == ch2); NOREF(ch2);
1339 if (ch == '/')
1340 {
1341 ch = ScmStreamGetCh(pStrm);
1342 RTEXITCODE rcExit;
1343 if (ch == '*')
1344 rcExit = parseMultiLineComment(pStrm);
1345 else if (ch == '/')
1346 rcExit = parseOneLineComment(pStrm);
1347 else
1348 rcExit = parseError(pStrm, 2, "Unexpected character");
1349 if (rcExit != RTEXITCODE_SUCCESS)
1350 return rcExit;
1351 }
1352 }
1353
1354 return parseError(pStrm, 0, "Unexpected end of file");
1355}
1356
1357
1358/**
1359 * Skips spaces and comments, returning the next character.
1360 *
1361 * @returns Next non-space-non-comment character. ~(unsigned)0 on EOF or
1362 * failure.
1363 * @param pStrm The stream.
1364 */
1365static unsigned parseGetNextNonSpaceNonCommentCh(PSCMSTREAM pStrm)
1366{
1367 unsigned ch;
1368 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1369 {
1370 if (!RT_C_IS_SPACE(ch) && ch != '/')
1371 return ch;
1372 if (ch == '/')
1373 {
1374 ch = ScmStreamGetCh(pStrm);
1375 RTEXITCODE rcExit;
1376 if (ch == '*')
1377 rcExit = parseMultiLineComment(pStrm);
1378 else if (ch == '/')
1379 rcExit = parseOneLineComment(pStrm);
1380 else
1381 rcExit = parseError(pStrm, 2, "Unexpected character");
1382 if (rcExit != RTEXITCODE_SUCCESS)
1383 return ~(unsigned)0;
1384 }
1385 }
1386
1387 parseError(pStrm, 0, "Unexpected end of file");
1388 return ~(unsigned)0;
1389}
1390
1391
1392/**
1393 * Get the next non-space-non-comment character on a preprocessor line.
1394 *
1395 * @returns The next character. On error message and ~(unsigned)0.
1396 * @param pStrm The stream.
1397 */
1398static unsigned parseGetNextNonSpaceNonCommentChOnPpLine(PSCMSTREAM pStrm)
1399{
1400 size_t off = ScmStreamTell(pStrm) - 1;
1401 unsigned ch;
1402 while ((ch = ScmStreamGetCh(pStrm)) != ~(unsigned)0)
1403 {
1404 if (RT_C_IS_SPACE(ch))
1405 {
1406 if (ch == '\n' || ch == '\r')
1407 {
1408 parseErrorAbs(pStrm, off, "Invalid preprocessor statement");
1409 break;
1410 }
1411 }
1412 else if (ch == '\\')
1413 {
1414 size_t off2 = ScmStreamTell(pStrm) - 1;
1415 ch = ScmStreamGetCh(pStrm);
1416 if (ch == '\r')
1417 ch = ScmStreamGetCh(pStrm);
1418 if (ch != '\n')
1419 {
1420 parseErrorAbs(pStrm, off2, "Expected new line");
1421 break;
1422 }
1423 }
1424 else
1425 return ch;
1426 }
1427 return ~(unsigned)0;
1428}
1429
1430
1431
1432/**
1433 * Skips spaces and comments.
1434 *
1435 * @returns Same as ScmStreamCGetWord
1436 * @param pStrm The stream..
1437 * @param pcchWord Where to return the length.
1438 */
1439static const char *parseGetNextCWord(PSCMSTREAM pStrm, size_t *pcchWord)
1440{
1441 if (parseSkipSpacesAndComments(pStrm) != RTEXITCODE_SUCCESS)
1442 return NULL;
1443 return ScmStreamCGetWord(pStrm, pcchWord);
1444}
1445
1446
1447
1448/**
1449 * Parses interface stability.
1450 *
1451 * @returns Interface stability if parsed correctly, otherwise error message and
1452 * kVTGStability_Invalid.
1453 * @param pStrm The stream.
1454 * @param ch The first character in the stability spec.
1455 */
1456static kVTGStability parseStability(PSCMSTREAM pStrm, unsigned ch)
1457{
1458 switch (ch)
1459 {
1460 case 'E':
1461 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("External")))
1462 return kVTGStability_External;
1463 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Evolving")))
1464 return kVTGStability_Evolving;
1465 break;
1466 case 'I':
1467 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Internal")))
1468 return kVTGStability_Internal;
1469 break;
1470 case 'O':
1471 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Obsolete")))
1472 return kVTGStability_Obsolete;
1473 break;
1474 case 'P':
1475 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Private")))
1476 return kVTGStability_Private;
1477 break;
1478 case 'S':
1479 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Stable")))
1480 return kVTGStability_Stable;
1481 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Standard")))
1482 return kVTGStability_Standard;
1483 break;
1484 case 'U':
1485 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unstable")))
1486 return kVTGStability_Unstable;
1487 break;
1488 }
1489 parseError(pStrm, 1, "Unknown stability specifier");
1490 return kVTGStability_Invalid;
1491}
1492
1493
1494/**
1495 * Parses data depndency class.
1496 *
1497 * @returns Data dependency class if parsed correctly, otherwise error message
1498 * and kVTGClass_Invalid.
1499 * @param pStrm The stream.
1500 * @param ch The first character in the stability spec.
1501 */
1502static kVTGClass parseDataDepClass(PSCMSTREAM pStrm, unsigned ch)
1503{
1504 switch (ch)
1505 {
1506 case 'C':
1507 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Common")))
1508 return kVTGClass_Common;
1509 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Cpu")))
1510 return kVTGClass_Cpu;
1511 break;
1512 case 'G':
1513 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Group")))
1514 return kVTGClass_Group;
1515 break;
1516 case 'I':
1517 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Isa")))
1518 return kVTGClass_Isa;
1519 break;
1520 case 'P':
1521 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Platform")))
1522 return kVTGClass_Platform;
1523 break;
1524 case 'U':
1525 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("Unknown")))
1526 return kVTGClass_Unknown;
1527 break;
1528 }
1529 parseError(pStrm, 1, "Unknown data dependency class specifier");
1530 return kVTGClass_Invalid;
1531}
1532
1533/**
1534 * Parses a pragma D attributes statement.
1535 *
1536 * @returns Suitable exit code, errors message already written on failure.
1537 * @param pStrm The stream.
1538 */
1539static RTEXITCODE parsePragmaDAttributes(PSCMSTREAM pStrm)
1540{
1541 /*
1542 * "CodeStability/DataStability/DataDepClass" - no spaces allowed.
1543 */
1544 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1545 if (ch == ~(unsigned)0)
1546 return RTEXITCODE_FAILURE;
1547
1548 kVTGStability enmCode = parseStability(pStrm, ch);
1549 if (enmCode == kVTGStability_Invalid)
1550 return RTEXITCODE_FAILURE;
1551 ch = ScmStreamGetCh(pStrm);
1552 if (ch != '/')
1553 return parseError(pStrm, 1, "Expected '/' following the code stability specifier");
1554
1555 kVTGStability enmData = parseStability(pStrm, ScmStreamGetCh(pStrm));
1556 if (enmData == kVTGStability_Invalid)
1557 return RTEXITCODE_FAILURE;
1558 ch = ScmStreamGetCh(pStrm);
1559 if (ch != '/')
1560 return parseError(pStrm, 1, "Expected '/' following the data stability specifier");
1561
1562 kVTGClass enmDataDep = parseDataDepClass(pStrm, ScmStreamGetCh(pStrm));
1563 if (enmDataDep == kVTGClass_Invalid)
1564 return RTEXITCODE_FAILURE;
1565
1566 /*
1567 * Expecting 'provider' followed by the name of an provider defined earlier.
1568 */
1569 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1570 if (ch == ~(unsigned)0)
1571 return RTEXITCODE_FAILURE;
1572 if (ch != 'p' || !ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("provider")))
1573 return parseError(pStrm, 1, "Expected 'provider'");
1574
1575 size_t cchName;
1576 const char *pszName = parseGetNextCWord(pStrm, &cchName);
1577 if (!pszName)
1578 return parseError(pStrm, 1, "Expected provider name");
1579
1580 PVTGPROVIDER pProv;
1581 RTListForEach(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry)
1582 {
1583 if ( !strncmp(pProv->pszName, pszName, cchName)
1584 && pProv->pszName[cchName] == '\0')
1585 break;
1586 }
1587 if (RTListNodeIsDummy(&g_ProviderHead, pProv, VTGPROVIDER, ListEntry))
1588 return parseError(pStrm, cchName, "Provider not found");
1589
1590 /*
1591 * Which aspect of the provider?
1592 */
1593 size_t cchAspect;
1594 const char *pszAspect = parseGetNextCWord(pStrm, &cchAspect);
1595 if (!pszAspect)
1596 return parseError(pStrm, 1, "Expected provider aspect");
1597
1598 PVTGATTRS pAttrs;
1599 if (cchAspect == 8 && !memcmp(pszAspect, "provider", 8))
1600 pAttrs = &pProv->AttrSelf;
1601 else if (cchAspect == 8 && !memcmp(pszAspect, "function", 8))
1602 pAttrs = &pProv->AttrFunctions;
1603 else if (cchAspect == 6 && !memcmp(pszAspect, "module", 6))
1604 pAttrs = &pProv->AttrModules;
1605 else if (cchAspect == 4 && !memcmp(pszAspect, "name", 4))
1606 pAttrs = &pProv->AttrName;
1607 else if (cchAspect == 4 && !memcmp(pszAspect, "args", 4))
1608 pAttrs = &pProv->AttrArguments;
1609 else
1610 return parseError(pStrm, cchAspect, "Unknown aspect");
1611
1612 if (pAttrs->enmCode != kVTGStability_Invalid)
1613 return parseError(pStrm, cchAspect, "You have already specified these attributes");
1614
1615 pAttrs->enmCode = enmCode;
1616 pAttrs->enmData = enmData;
1617 pAttrs->enmDataDep = enmDataDep;
1618 return RTEXITCODE_SUCCESS;
1619}
1620
1621/**
1622 * Parses a D pragma statement.
1623 *
1624 * @returns Suitable exit code, errors message already written on failure.
1625 * @param pStrm The stream.
1626 */
1627static RTEXITCODE parsePragma(PSCMSTREAM pStrm)
1628{
1629 RTEXITCODE rcExit;
1630 unsigned ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1631 if (ch == ~(unsigned)0)
1632 rcExit = RTEXITCODE_FAILURE;
1633 else if (ch == 'D' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("D")))
1634 {
1635 ch = parseGetNextNonSpaceNonCommentChOnPpLine(pStrm);
1636 if (ch == ~(unsigned)0)
1637 rcExit = RTEXITCODE_FAILURE;
1638 else if (ch == 'a' && ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("attributes")))
1639 rcExit = parsePragmaDAttributes(pStrm);
1640 else
1641 rcExit = parseError(pStrm, 1, "Unknown pragma D");
1642 }
1643 else
1644 rcExit = parseError(pStrm, 1, "Unknown pragma");
1645 return rcExit;
1646}
1647
1648
1649/**
1650 * Classifies the given type expression.
1651 *
1652 * @return Type flags.
1653 * @param pszType The type expression.
1654 */
1655static uint32_t parseTypeExpression(const char *pszType)
1656{
1657 size_t cchType = strlen(pszType);
1658#define MY_STRMATCH(a_sz) (cchType == sizeof(a_sz) - 1 && !memcmp(a_sz, pszType, sizeof(a_sz) - 1))
1659
1660 /*
1661 * Try detect pointers.
1662 */
1663 if (pszType[cchType - 1] == '*') return VTG_TYPE_POINTER;
1664 if (pszType[cchType - 1] == '&')
1665 {
1666 RTMsgWarning("Please avoid using references like '%s' for probe arguments!", pszType);
1667 return VTG_TYPE_POINTER;
1668 }
1669
1670 /*
1671 * Standard integer types and IPRT variants.
1672 * It's important that we catch all types larger than 32-bit here or we'll
1673 * screw up the probe argument handling.
1674 */
1675 if (MY_STRMATCH("int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1676 if (MY_STRMATCH("uintptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_UNSIGNED;
1677 if (MY_STRMATCH("intptr_t")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_SIGNED;
1678
1679 //if (MY_STRMATCH("uint128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint128_t) | VTG_TYPE_UNSIGNED;
1680 if (MY_STRMATCH("uint64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1681 if (MY_STRMATCH("uint32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1682 if (MY_STRMATCH("uint16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1683 if (MY_STRMATCH("uint8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(uint8_t) | VTG_TYPE_UNSIGNED;
1684
1685 //if (MY_STRMATCH("int128_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int128_t) | VTG_TYPE_SIGNED;
1686 if (MY_STRMATCH("int64_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int64_t) | VTG_TYPE_SIGNED;
1687 if (MY_STRMATCH("int32_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int32_t) | VTG_TYPE_SIGNED;
1688 if (MY_STRMATCH("int16_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int16_t) | VTG_TYPE_SIGNED;
1689 if (MY_STRMATCH("int8_t")) return VTG_TYPE_FIXED_SIZED | sizeof(int8_t) | VTG_TYPE_SIGNED;
1690
1691 if (MY_STRMATCH("RTUINT64U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint64_t) | VTG_TYPE_UNSIGNED;
1692 if (MY_STRMATCH("RTUINT32U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint32_t) | VTG_TYPE_UNSIGNED;
1693 if (MY_STRMATCH("RTUINT16U")) return VTG_TYPE_FIXED_SIZED | sizeof(uint16_t) | VTG_TYPE_UNSIGNED;
1694
1695 if (MY_STRMATCH("RTMSINTERVAL")) return VTG_TYPE_FIXED_SIZED | sizeof(RTMSINTERVAL) | VTG_TYPE_UNSIGNED;
1696 if (MY_STRMATCH("RTTIMESPEC")) return VTG_TYPE_FIXED_SIZED | sizeof(RTTIMESPEC) | VTG_TYPE_SIGNED;
1697 if (MY_STRMATCH("RTPROCESS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTPROCESS) | VTG_TYPE_UNSIGNED;
1698 if (MY_STRMATCH("RTHCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTHCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS;
1699
1700 if (MY_STRMATCH("RTR3PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3;
1701 if (MY_STRMATCH("RTR0PTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0;
1702 if (MY_STRMATCH("RTRCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC;
1703 if (MY_STRMATCH("RTHCPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0;
1704
1705 if (MY_STRMATCH("RTR3UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1706 if (MY_STRMATCH("RTR0UINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1707 if (MY_STRMATCH("RTRCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1708 if (MY_STRMATCH("RTHCUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1709
1710 if (MY_STRMATCH("RTR3INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_SIGNED;
1711 if (MY_STRMATCH("RTR0INTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1712 if (MY_STRMATCH("RTRCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1713 if (MY_STRMATCH("RTHCINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_SIGNED;
1714
1715 if (MY_STRMATCH("RTUINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_UNSIGNED;
1716 if (MY_STRMATCH("RTINTPTR")) return VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_CTX_RC | VTG_TYPE_SIGNED;
1717
1718 if (MY_STRMATCH("RTHCUINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_CTX_R0 | VTG_TYPE_UNSIGNED;
1719 if (MY_STRMATCH("RTR3UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1720 if (MY_STRMATCH("RTR0UINTREG")) return VTG_TYPE_HC_ARCH_SIZED | VTG_TYPE_CTX_R3 | VTG_TYPE_UNSIGNED;
1721
1722 if (MY_STRMATCH("RTGCUINTREG")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTREG) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1723 if (MY_STRMATCH("RTGCPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR) | VTG_TYPE_UNSIGNED | VTG_TYPE_CTX_GST;
1724 if (MY_STRMATCH("RTGCINTPTR")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCUINTPTR) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1725 if (MY_STRMATCH("RTGCPTR32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR32) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1726 if (MY_STRMATCH("RTGCPTR64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPTR64) | VTG_TYPE_SIGNED | VTG_TYPE_CTX_GST;
1727 if (MY_STRMATCH("RTGCPHYS")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1728 if (MY_STRMATCH("RTGCPHYS32")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS32) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1729 if (MY_STRMATCH("RTGCPHYS64")) return VTG_TYPE_FIXED_SIZED | sizeof(RTGCPHYS64) | VTG_TYPE_UNSIGNED | VTG_TYPE_PHYS | VTG_TYPE_CTX_GST;
1730
1731 /*
1732 * The special VBox types.
1733 */
1734 if (MY_STRMATCH("PVM")) return VTG_TYPE_POINTER;
1735 if (MY_STRMATCH("PVMCPU")) return VTG_TYPE_POINTER;
1736 if (MY_STRMATCH("PCPUMCTX")) return VTG_TYPE_POINTER;
1737
1738 /*
1739 * Preaching time.
1740 */
1741 if ( MY_STRMATCH("unsigned long")
1742 || MY_STRMATCH("unsigned long long")
1743 || MY_STRMATCH("signed long")
1744 || MY_STRMATCH("signed long long")
1745 || MY_STRMATCH("long")
1746 || MY_STRMATCH("long long")
1747 || MY_STRMATCH("char")
1748 || MY_STRMATCH("signed char")
1749 || MY_STRMATCH("unsigned char")
1750 || MY_STRMATCH("double")
1751 || MY_STRMATCH("long double")
1752 || MY_STRMATCH("float")
1753 )
1754 {
1755 RTMsgError("Please do NOT use the type '%s' for probe arguments!", pszType);
1756 g_cTypeErrors++;
1757 return 0;
1758 }
1759
1760 if ( MY_STRMATCH("unsigned")
1761 || MY_STRMATCH("signed")
1762 || MY_STRMATCH("signed int")
1763 || MY_STRMATCH("unsigned int")
1764 || MY_STRMATCH("short")
1765 || MY_STRMATCH("signed short")
1766 || MY_STRMATCH("unsigned short")
1767 )
1768 RTMsgWarning("Please avoid using the type '%s' for probe arguments!", pszType);
1769 if (MY_STRMATCH("unsigned")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1770 if (MY_STRMATCH("unsigned int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_UNSIGNED;
1771 if (MY_STRMATCH("signed")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1772 if (MY_STRMATCH("signed int")) return VTG_TYPE_FIXED_SIZED | sizeof(int) | VTG_TYPE_SIGNED;
1773 if (MY_STRMATCH("short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1774 if (MY_STRMATCH("signed short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_SIGNED;
1775 if (MY_STRMATCH("unsigned short")) return VTG_TYPE_FIXED_SIZED | sizeof(short) | VTG_TYPE_UNSIGNED;
1776
1777 /*
1778 * What we haven't caught by now is either unknown to us or wrong.
1779 */
1780 if (pszType[0] == 'P')
1781 {
1782 RTMsgError("Type '%s' looks like a pointer typedef, please do NOT use those "
1783 "but rather the non-pointer typedef or struct with '*'",
1784 pszType);
1785 g_cTypeErrors++;
1786 return VTG_TYPE_POINTER;
1787 }
1788
1789 RTMsgError("Don't know '%s' - please change or fix VBoxTpG", pszType);
1790 g_cTypeErrors++;
1791
1792#undef MY_STRCMP
1793 return 0;
1794}
1795
1796
1797/**
1798 * Initializes the members of an argument.
1799 *
1800 * @returns RTEXITCODE_SUCCESS or RTEXITCODE_FAILURE+msg.
1801 * @param pProbe The probe.
1802 * @param pArg The argument.
1803 * @param pStrm The input stream (for errors).
1804 * @param pchType The type.
1805 * @param cchType The type length.
1806 * @param pchName The name.
1807 * @param cchName The name length.
1808 */
1809static RTEXITCODE parseInitArgument(PVTGPROBE pProbe, PVTGARG pArg, PSCMSTREAM pStrm,
1810 char *pchType, size_t cchType, char *pchName, size_t cchName)
1811{
1812 Assert(!pArg->pszName); Assert(!pArg->pszTracerType); Assert(!pArg->pszCtxType); Assert(!pArg->fType);
1813
1814 pArg->pszArgPassingFmt = ", %s";
1815 pArg->pszName = RTStrDupN(pchName, cchName);
1816 pArg->pszTracerType = strtabInsertN(pchType, cchType);
1817 if (!pArg->pszTracerType || !pArg->pszName)
1818 return parseError(pStrm, 1, "Out of memory");
1819 pArg->fType = parseTypeExpression(pArg->pszTracerType);
1820
1821 if ( (pArg->fType & VTG_TYPE_POINTER)
1822 && !(g_fTypeContext & VTG_TYPE_CTX_R0) )
1823 {
1824 pArg->fType &= ~VTG_TYPE_POINTER;
1825 if ( !strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM")
1826 || !strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU")
1827 || !strcmp(pArg->pszTracerType, "struct CPUMCTX *") || !strcmp(pArg->pszTracerType, "PCPUMCTX")
1828 )
1829 {
1830 pArg->fType |= VTG_TYPE_CTX_POINTER | VTG_TYPE_CTX_R0
1831 | VTG_TYPE_FIXED_SIZED | (g_cHostBits / 8)
1832 | VTG_TYPE_AUTO_CONV_PTR;
1833 pArg->pszCtxType = RTStrDup("RTR0PTR");
1834
1835 if (!strcmp(pArg->pszTracerType, "struct VM *") || !strcmp(pArg->pszTracerType, "PVM"))
1836 pArg->pszArgPassingFmt = ", VTG_VM_TO_R0(%s)";
1837 else if (!strcmp(pArg->pszTracerType, "struct VMCPU *") || !strcmp(pArg->pszTracerType, "PVMCPU"))
1838 pArg->pszArgPassingFmt = ", VTG_VMCPU_TO_R0(%s)";
1839 else
1840 {
1841 PVTGARG pFirstArg = RTListGetFirst(&pProbe->ArgHead, VTGARG, ListEntry);
1842 if ( !pFirstArg
1843 || pFirstArg == pArg
1844 || strcmp(pFirstArg->pszName, "a_pVCpu")
1845 || ( strcmp(pFirstArg->pszTracerType, "struct VMCPU *")
1846 && strcmp(pFirstArg->pszTracerType, "PVMCPU *")) )
1847 return parseError(pStrm, 1, "The automatic ring-0 pointer conversion requires 'a_pVCpu' with type 'struct VMCPU *' as the first argument");
1848
1849 if (!strcmp(pArg->pszTracerType, "struct CPUMCTX *")|| !strcmp(pArg->pszTracerType, "PCPUMCTX"))
1850 pArg->pszArgPassingFmt = ", VTG_CPUMCTX_TO_R0(a_pVCpu, %s)";
1851 else
1852 pArg->pszArgPassingFmt = ", VBoxTpG-Is-Buggy!!";
1853 }
1854 }
1855 else
1856 {
1857 pArg->fType |= VTG_TYPE_CTX_POINTER | g_fTypeContext | VTG_TYPE_FIXED_SIZED | (g_cBits / 8);
1858 pArg->pszCtxType = RTStrDupN(pchType, cchType);
1859 }
1860 }
1861 else
1862 pArg->pszCtxType = RTStrDupN(pchType, cchType);
1863 if (!pArg->pszCtxType)
1864 return parseError(pStrm, 1, "Out of memory");
1865
1866 return RTEXITCODE_SUCCESS;
1867}
1868
1869
1870/**
1871 * Unmangles the probe name.
1872 *
1873 * This involves translating double underscore to dash.
1874 *
1875 * @returns Pointer to the unmangled name in the string table.
1876 * @param pszMangled The mangled name.
1877 */
1878static const char *parseUnmangleProbeName(const char *pszMangled)
1879{
1880 size_t cchMangled = strlen(pszMangled);
1881 char *pszTmp = (char *)alloca(cchMangled + 2);
1882 const char *pszSrc = pszMangled;
1883 char *pszDst = pszTmp;
1884
1885 while (*pszSrc)
1886 {
1887 if (pszSrc[0] == '_' && pszSrc[1] == '_' && pszSrc[2] != '_')
1888 {
1889 *pszDst++ = '-';
1890 pszSrc += 2;
1891 }
1892 else
1893 *pszDst++ = *pszSrc++;
1894 }
1895 *pszDst = '\0';
1896
1897 return strtabInsertN(pszTmp, pszDst - pszTmp);
1898}
1899
1900
1901/**
1902 * Parses a D probe statement.
1903 *
1904 * @returns Suitable exit code, errors message already written on failure.
1905 * @param pStrm The stream.
1906 * @param pProv The provider being parsed.
1907 */
1908static RTEXITCODE parseProbe(PSCMSTREAM pStrm, PVTGPROVIDER pProv)
1909{
1910 /*
1911 * Next up is a name followed by an opening parenthesis.
1912 */
1913 size_t cchProbe;
1914 const char *pszProbe = parseGetNextCWord(pStrm, &cchProbe);
1915 if (!pszProbe)
1916 return parseError(pStrm, 1, "Expected a probe name starting with an alphabetical character");
1917 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1918 if (ch != '(')
1919 return parseError(pStrm, 1, "Expected '(' after the probe name");
1920
1921 /*
1922 * Create a probe instance.
1923 */
1924 PVTGPROBE pProbe = (PVTGPROBE)RTMemAllocZ(sizeof(*pProbe));
1925 if (!pProbe)
1926 return parseError(pStrm, 0, "Out of memory");
1927 RTListInit(&pProbe->ArgHead);
1928 RTListAppend(&pProv->ProbeHead, &pProbe->ListEntry);
1929 pProbe->offArgList = UINT32_MAX;
1930 pProbe->pszMangledName = RTStrDupN(pszProbe, cchProbe);
1931 if (!pProbe->pszMangledName)
1932 return parseError(pStrm, 0, "Out of memory");
1933 pProbe->pszUnmangledName = parseUnmangleProbeName(pProbe->pszMangledName);
1934 if (!pProbe->pszUnmangledName)
1935 return parseError(pStrm, 0, "Out of memory");
1936
1937 /*
1938 * Parse loop for the argument.
1939 */
1940 PVTGARG pArg = NULL;
1941 size_t cchName = 0;
1942 size_t cchArg = 0;
1943 char szArg[4096];
1944 for (;;)
1945 {
1946 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1947 switch (ch)
1948 {
1949 case ')':
1950 case ',':
1951 {
1952 /* commit the argument */
1953 if (pArg)
1954 {
1955 if (!cchName)
1956 return parseError(pStrm, 1, "Argument has no name");
1957 if (cchArg - cchName - 1 >= 128)
1958 return parseError(pStrm, 1, "Argument type too long");
1959 RTEXITCODE rcExit = parseInitArgument(pProbe, pArg, pStrm,
1960 szArg, cchArg - cchName - 1,
1961 &szArg[cchArg - cchName], cchName);
1962 if (rcExit != RTEXITCODE_SUCCESS)
1963 return rcExit;
1964 if (VTG_TYPE_IS_LARGE(pArg->fType))
1965 pProbe->fHaveLargeArgs = true;
1966 pArg = NULL;
1967 cchName = cchArg = 0;
1968 }
1969 if (ch == ')')
1970 {
1971 size_t off = ScmStreamTell(pStrm);
1972 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
1973 if (ch != ';')
1974 return parseErrorAbs(pStrm, off, "Expected ';'");
1975 return RTEXITCODE_SUCCESS;
1976 }
1977 break;
1978 }
1979
1980 default:
1981 {
1982 size_t cchWord;
1983 const char *pszWord = ScmStreamCGetWordM1(pStrm, &cchWord);
1984 if (!pszWord)
1985 return parseError(pStrm, 0, "Expected argument");
1986 if (!pArg)
1987 {
1988 pArg = (PVTGARG)RTMemAllocZ(sizeof(*pArg));
1989 if (!pArg)
1990 return parseError(pStrm, 1, "Out of memory");
1991 RTListAppend(&pProbe->ArgHead, &pArg->ListEntry);
1992 pProbe->cArgs++;
1993
1994 if (cchWord + 1 > sizeof(szArg))
1995 return parseError(pStrm, 1, "Too long parameter declaration");
1996 memcpy(szArg, pszWord, cchWord);
1997 szArg[cchWord] = '\0';
1998 cchArg = cchWord;
1999 cchName = 0;
2000 }
2001 else
2002 {
2003 if (cchArg + 1 + cchWord + 1 > sizeof(szArg))
2004 return parseError(pStrm, 1, "Too long parameter declaration");
2005
2006 szArg[cchArg++] = ' ';
2007 memcpy(&szArg[cchArg], pszWord, cchWord);
2008 cchArg += cchWord;
2009 szArg[cchArg] = '\0';
2010 cchName = cchWord;
2011 }
2012 break;
2013 }
2014
2015 case '*':
2016 {
2017 if (!pArg)
2018 return parseError(pStrm, 1, "A parameter type does not start with an asterix");
2019 if (cchArg + sizeof(" *") >= sizeof(szArg))
2020 return parseError(pStrm, 1, "Too long parameter declaration");
2021 szArg[cchArg++] = ' ';
2022 szArg[cchArg++] = '*';
2023 szArg[cchArg ] = '\0';
2024 cchName = 0;
2025 break;
2026 }
2027
2028 case ~(unsigned)0:
2029 return parseError(pStrm, 0, "Missing closing ')' on probe");
2030 }
2031 }
2032}
2033
2034/**
2035 * Parses a D provider statement.
2036 *
2037 * @returns Suitable exit code, errors message already written on failure.
2038 * @param pStrm The stream.
2039 */
2040static RTEXITCODE parseProvider(PSCMSTREAM pStrm)
2041{
2042 /*
2043 * Next up is a name followed by a curly bracket. Ignore comments.
2044 */
2045 RTEXITCODE rcExit = parseSkipSpacesAndComments(pStrm);
2046 if (rcExit != RTEXITCODE_SUCCESS)
2047 return parseError(pStrm, 1, "Expected a provider name starting with an alphabetical character");
2048 size_t cchName;
2049 const char *pszName = ScmStreamCGetWord(pStrm, &cchName);
2050 if (!pszName)
2051 return parseError(pStrm, 0, "Bad provider name");
2052 if (RT_C_IS_DIGIT(pszName[cchName - 1]))
2053 return parseError(pStrm, 1, "A provider name cannot end with digit");
2054
2055 unsigned ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2056 if (ch != '{')
2057 return parseError(pStrm, 1, "Expected '{' after the provider name");
2058
2059 /*
2060 * Create a provider instance.
2061 */
2062 PVTGPROVIDER pProv = (PVTGPROVIDER)RTMemAllocZ(sizeof(*pProv));
2063 if (!pProv)
2064 return parseError(pStrm, 0, "Out of memory");
2065 RTListInit(&pProv->ProbeHead);
2066 RTListAppend(&g_ProviderHead, &pProv->ListEntry);
2067 pProv->pszName = strtabInsertN(pszName, cchName);
2068 if (!pProv->pszName)
2069 return parseError(pStrm, 0, "Out of memory");
2070
2071 /*
2072 * Parse loop.
2073 */
2074 for (;;)
2075 {
2076 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2077 switch (ch)
2078 {
2079 case 'p':
2080 if (ScmStreamCMatchingWordM1(pStrm, RT_STR_TUPLE("probe")))
2081 rcExit = parseProbe(pStrm, pProv);
2082 else
2083 rcExit = parseError(pStrm, 1, "Unexpected character");
2084 break;
2085
2086 case '}':
2087 {
2088 size_t off = ScmStreamTell(pStrm);
2089 ch = parseGetNextNonSpaceNonCommentCh(pStrm);
2090 if (ch == ';')
2091 return RTEXITCODE_SUCCESS;
2092 rcExit = parseErrorAbs(pStrm, off, "Expected ';'");
2093 break;
2094 }
2095
2096 case ~(unsigned)0:
2097 rcExit = parseError(pStrm, 0, "Missing closing '}' on provider");
2098 break;
2099
2100 default:
2101 rcExit = parseError(pStrm, 1, "Unexpected character");
2102 break;
2103 }
2104 if (rcExit != RTEXITCODE_SUCCESS)
2105 return rcExit;
2106 }
2107}
2108
2109
2110static RTEXITCODE parseScript(const char *pszScript)
2111{
2112 SCMSTREAM Strm;
2113 int rc = ScmStreamInitForReading(&Strm, pszScript);
2114 if (RT_FAILURE(rc))
2115 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open & read '%s' into memory: %Rrc", pszScript, rc);
2116 if (g_cVerbosity > 0)
2117 RTMsgInfo("Parsing '%s'...", pszScript);
2118
2119 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
2120 unsigned ch;
2121 while ((ch = ScmStreamGetCh(&Strm)) != ~(unsigned)0)
2122 {
2123 if (RT_C_IS_SPACE(ch))
2124 continue;
2125 switch (ch)
2126 {
2127 case '/':
2128 ch = ScmStreamGetCh(&Strm);
2129 if (ch == '*')
2130 rcExit = parseMultiLineComment(&Strm);
2131 else if (ch == '/')
2132 rcExit = parseOneLineComment(&Strm);
2133 else
2134 rcExit = parseError(&Strm, 2, "Unexpected character");
2135 break;
2136
2137 case 'p':
2138 if (ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("provider")))
2139 rcExit = parseProvider(&Strm);
2140 else
2141 rcExit = parseError(&Strm, 1, "Unexpected character");
2142 break;
2143
2144 case '#':
2145 {
2146 ch = parseGetNextNonSpaceNonCommentChOnPpLine(&Strm);
2147 if (ch == ~(unsigned)0)
2148 rcExit = RTEXITCODE_FAILURE;
2149 else if (ch == 'p' && ScmStreamCMatchingWordM1(&Strm, RT_STR_TUPLE("pragma")))
2150 rcExit = parsePragma(&Strm);
2151 else
2152 rcExit = parseError(&Strm, 1, "Unsupported preprocessor directive");
2153 break;
2154 }
2155
2156 default:
2157 rcExit = parseError(&Strm, 1, "Unexpected character");
2158 break;
2159 }
2160 if (rcExit != RTEXITCODE_SUCCESS)
2161 return rcExit;
2162 }
2163
2164 ScmStreamDelete(&Strm);
2165 if (g_cVerbosity > 0 && rcExit == RTEXITCODE_SUCCESS)
2166 RTMsgInfo("Successfully parsed '%s'.", pszScript);
2167 return rcExit;
2168}
2169
2170
2171/**
2172 * Parses the arguments.
2173 */
2174static RTEXITCODE parseArguments(int argc, char **argv)
2175{
2176 /*
2177 * Set / Adjust defaults.
2178 */
2179 int rc = RTPathAbs(g_pszAssemblerIncVal, g_szAssemblerIncVal, sizeof(g_szAssemblerIncVal) - 1);
2180 if (RT_FAILURE(rc))
2181 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathAbs failed: %Rrc", rc);
2182 strcat(g_szAssemblerIncVal, "/");
2183 g_pszAssemblerIncVal = g_szAssemblerIncVal;
2184
2185 /*
2186 * Option config.
2187 */
2188 enum
2189 {
2190 kVBoxTpGOpt_32Bit = 1000,
2191 kVBoxTpGOpt_64Bit,
2192 kVBoxTpGOpt_GenerateWrapperHeader,
2193 kVBoxTpGOpt_Assembler,
2194 kVBoxTpGOpt_AssemblerFmtOpt,
2195 kVBoxTpGOpt_AssemblerFmtVal,
2196 kVBoxTpGOpt_AssemblerOutputOpt,
2197 kVBoxTpGOpt_AssemblerOption,
2198 kVBoxTpGOpt_Pic,
2199 kVBoxTpGOpt_ProbeFnName,
2200 kVBoxTpGOpt_ProbeFnImported,
2201 kVBoxTpGOpt_ProbeFnNotImported,
2202 kVBoxTpGOpt_Host32Bit,
2203 kVBoxTpGOpt_Host64Bit,
2204 kVBoxTpGOpt_RawModeContext,
2205 kVBoxTpGOpt_Ring0Context,
2206 kVBoxTpGOpt_Ring0ContextAgnostic,
2207 kVBoxTpGOpt_Ring3Context,
2208 kVBoxTpGOpt_End
2209 };
2210
2211 static RTGETOPTDEF const s_aOpts[] =
2212 {
2213 /* dtrace w/ long options */
2214 { "-32", kVBoxTpGOpt_32Bit, RTGETOPT_REQ_NOTHING },
2215 { "-64", kVBoxTpGOpt_64Bit, RTGETOPT_REQ_NOTHING },
2216 { "--apply-cpp", 'C', RTGETOPT_REQ_NOTHING },
2217 { "--generate-obj", 'G', RTGETOPT_REQ_NOTHING },
2218 { "--generate-header", 'h', RTGETOPT_REQ_NOTHING },
2219 { "--output", 'o', RTGETOPT_REQ_STRING },
2220 { "--script", 's', RTGETOPT_REQ_STRING },
2221 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
2222 /* our stuff */
2223 { "--generate-wrapper-header", kVBoxTpGOpt_GenerateWrapperHeader, RTGETOPT_REQ_NOTHING },
2224 { "--assembler", kVBoxTpGOpt_Assembler, RTGETOPT_REQ_STRING },
2225 { "--assembler-fmt-opt", kVBoxTpGOpt_AssemblerFmtOpt, RTGETOPT_REQ_STRING },
2226 { "--assembler-fmt-val", kVBoxTpGOpt_AssemblerFmtVal, RTGETOPT_REQ_STRING },
2227 { "--assembler-output-opt", kVBoxTpGOpt_AssemblerOutputOpt, RTGETOPT_REQ_STRING },
2228 { "--assembler-option", kVBoxTpGOpt_AssemblerOption, RTGETOPT_REQ_STRING },
2229 { "--pic", kVBoxTpGOpt_Pic, RTGETOPT_REQ_NOTHING },
2230 { "--probe-fn-name", kVBoxTpGOpt_ProbeFnName, RTGETOPT_REQ_STRING },
2231 { "--probe-fn-imported", kVBoxTpGOpt_ProbeFnImported, RTGETOPT_REQ_NOTHING },
2232 { "--probe-fn-not-imported", kVBoxTpGOpt_ProbeFnNotImported, RTGETOPT_REQ_NOTHING },
2233 { "--host-32-bit", kVBoxTpGOpt_Host32Bit, RTGETOPT_REQ_NOTHING },
2234 { "--host-64-bit", kVBoxTpGOpt_Host64Bit, RTGETOPT_REQ_NOTHING },
2235 { "--raw-mode-context", kVBoxTpGOpt_RawModeContext, RTGETOPT_REQ_NOTHING },
2236 { "--ring-0-context", kVBoxTpGOpt_Ring0Context, RTGETOPT_REQ_NOTHING },
2237 { "--ring-0-context-agnostic", kVBoxTpGOpt_Ring0ContextAgnostic, RTGETOPT_REQ_NOTHING },
2238 { "--ring-3-context", kVBoxTpGOpt_Ring3Context, RTGETOPT_REQ_NOTHING },
2239 /** @todo We're missing a bunch of assembler options! */
2240 };
2241
2242 RTGETOPTUNION ValueUnion;
2243 RTGETOPTSTATE GetOptState;
2244 rc = RTGetOptInit(&GetOptState, argc, argv, &s_aOpts[0], RT_ELEMENTS(s_aOpts), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
2245 AssertReleaseRCReturn(rc, RTEXITCODE_FAILURE);
2246
2247 /*
2248 * Process the options.
2249 */
2250 while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
2251 {
2252 switch (rc)
2253 {
2254 /*
2255 * DTrace compatible options.
2256 */
2257 case kVBoxTpGOpt_32Bit:
2258 g_cHostBits = g_cBits = 32;
2259 g_pszAssemblerFmtVal = g_szAssemblerFmtVal32;
2260 break;
2261
2262 case kVBoxTpGOpt_64Bit:
2263 g_cHostBits = g_cBits = 64;
2264 g_pszAssemblerFmtVal = g_szAssemblerFmtVal64;
2265 break;
2266
2267 case 'C':
2268 g_fApplyCpp = true;
2269 RTMsgWarning("Ignoring the -C option - no preprocessing of the D script will be performed");
2270 break;
2271
2272 case 'G':
2273 if ( g_enmAction != kVBoxTpGAction_Nothing
2274 && g_enmAction != kVBoxTpGAction_GenerateObject)
2275 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-G does not mix with -h or --generate-wrapper-header");
2276 g_enmAction = kVBoxTpGAction_GenerateObject;
2277 break;
2278
2279 case 'h':
2280 if (!strcmp(GetOptState.pDef->pszLong, "--generate-header"))
2281 {
2282 if ( g_enmAction != kVBoxTpGAction_Nothing
2283 && g_enmAction != kVBoxTpGAction_GenerateHeader)
2284 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "-h does not mix with -G or --generate-wrapper-header");
2285 g_enmAction = kVBoxTpGAction_GenerateHeader;
2286 }
2287 else
2288 {
2289 /* --help or similar */
2290 RTPrintf("VirtualBox Tracepoint Generator\n"
2291 "\n"
2292 "Usage: %s [options]\n"
2293 "\n"
2294 "Options:\n", RTProcShortName());
2295 for (size_t i = 0; i < RT_ELEMENTS(s_aOpts); i++)
2296 if ((unsigned)s_aOpts[i].iShort < 128)
2297 RTPrintf(" -%c,%s\n", s_aOpts[i].iShort, s_aOpts[i].pszLong);
2298 else
2299 RTPrintf(" %s\n", s_aOpts[i].pszLong);
2300 return RTEXITCODE_SUCCESS;
2301 }
2302 break;
2303
2304 case 'o':
2305 if (g_pszOutput)
2306 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Output file is already set to '%s'", g_pszOutput);
2307 g_pszOutput = ValueUnion.psz;
2308 break;
2309
2310 case 's':
2311 if (g_pszScript)
2312 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Script file is already set to '%s'", g_pszScript);
2313 g_pszScript = ValueUnion.psz;
2314 break;
2315
2316 case 'v':
2317 g_cVerbosity++;
2318 break;
2319
2320 case 'V':
2321 {
2322 /* The following is assuming that svn does it's job here. */
2323 static const char s_szRev[] = "$Revision: 43055 $";
2324 const char *psz = RTStrStripL(strchr(s_szRev, ' '));
2325 RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
2326 return RTEXITCODE_SUCCESS;
2327 }
2328
2329 case VINF_GETOPT_NOT_OPTION:
2330 if (g_enmAction == kVBoxTpGAction_GenerateObject)
2331 break; /* object files, ignore them. */
2332 return RTGetOptPrintError(rc, &ValueUnion);
2333
2334
2335 /*
2336 * Our options.
2337 */
2338 case kVBoxTpGOpt_GenerateWrapperHeader:
2339 if ( g_enmAction != kVBoxTpGAction_Nothing
2340 && g_enmAction != kVBoxTpGAction_GenerateWrapperHeader)
2341 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "--generate-wrapper-header does not mix with -h or -G");
2342 g_enmAction = kVBoxTpGAction_GenerateWrapperHeader;
2343 break;
2344
2345 case kVBoxTpGOpt_Assembler:
2346 g_pszAssembler = ValueUnion.psz;
2347 break;
2348
2349 case kVBoxTpGOpt_AssemblerFmtOpt:
2350 g_pszAssemblerFmtOpt = ValueUnion.psz;
2351 break;
2352
2353 case kVBoxTpGOpt_AssemblerFmtVal:
2354 g_pszAssemblerFmtVal = ValueUnion.psz;
2355 break;
2356
2357 case kVBoxTpGOpt_AssemblerOutputOpt:
2358 g_pszAssemblerOutputOpt = ValueUnion.psz;
2359 break;
2360
2361 case kVBoxTpGOpt_AssemblerOption:
2362 if (g_cAssemblerOptions >= RT_ELEMENTS(g_apszAssemblerOptions))
2363 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many assembly options (max %u)", RT_ELEMENTS(g_apszAssemblerOptions));
2364 g_apszAssemblerOptions[g_cAssemblerOptions] = ValueUnion.psz;
2365 g_cAssemblerOptions++;
2366 break;
2367
2368 case kVBoxTpGOpt_Pic:
2369 g_fPic = true;
2370 break;
2371
2372 case kVBoxTpGOpt_ProbeFnName:
2373 g_pszProbeFnName = ValueUnion.psz;
2374 break;
2375
2376 case kVBoxTpGOpt_ProbeFnImported:
2377 g_fProbeFnImported = true;
2378 break;
2379
2380 case kVBoxTpGOpt_ProbeFnNotImported:
2381 g_fProbeFnImported = false;
2382 break;
2383
2384 case kVBoxTpGOpt_Host32Bit:
2385 g_cHostBits = 32;
2386 break;
2387
2388 case kVBoxTpGOpt_Host64Bit:
2389 g_cHostBits = 64;
2390 break;
2391
2392 case kVBoxTpGOpt_RawModeContext:
2393 g_fTypeContext = VTG_TYPE_CTX_RC;
2394 g_pszContextDefine = "IN_RC";
2395 g_pszContextDefine2 = NULL;
2396 break;
2397
2398 case kVBoxTpGOpt_Ring0Context:
2399 g_fTypeContext = VTG_TYPE_CTX_R0;
2400 g_pszContextDefine = "IN_RING0";
2401 g_pszContextDefine2 = NULL;
2402 break;
2403
2404 case kVBoxTpGOpt_Ring0ContextAgnostic:
2405 g_fTypeContext = VTG_TYPE_CTX_R0;
2406 g_pszContextDefine = "IN_RING0_AGNOSTIC";
2407 g_pszContextDefine2 = "IN_RING0";
2408 break;
2409
2410 case kVBoxTpGOpt_Ring3Context:
2411 g_fTypeContext = VTG_TYPE_CTX_R3;
2412 g_pszContextDefine = "IN_RING3";
2413 g_pszContextDefine2 = NULL;
2414 break;
2415
2416
2417 /*
2418 * Errors and bugs.
2419 */
2420 default:
2421 return RTGetOptPrintError(rc, &ValueUnion);
2422 }
2423 }
2424
2425 /*
2426 * Check that we've got all we need.
2427 */
2428 if (g_enmAction == kVBoxTpGAction_Nothing)
2429 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No action specified (-h, -G or --generate-wrapper-header)");
2430 if (!g_pszScript)
2431 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No script file specified (-s)");
2432 if (!g_pszOutput)
2433 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No output file specified (-o)");
2434
2435 return RTEXITCODE_SUCCESS;
2436}
2437
2438
2439int main(int argc, char **argv)
2440{
2441 int rc = RTR3InitExe(argc, &argv, 0);
2442 if (RT_FAILURE(rc))
2443 return 1;
2444
2445 RTEXITCODE rcExit = parseArguments(argc, argv);
2446 if (rcExit == RTEXITCODE_SUCCESS)
2447 {
2448 /*
2449 * Parse the script.
2450 */
2451 RTListInit(&g_ProviderHead);
2452 rcExit = parseScript(g_pszScript);
2453 if (rcExit == RTEXITCODE_SUCCESS)
2454 {
2455 /*
2456 * Take action.
2457 */
2458 if (g_enmAction == kVBoxTpGAction_GenerateHeader)
2459 rcExit = generateFile(g_pszOutput, "header", generateHeader);
2460 else if (g_enmAction == kVBoxTpGAction_GenerateWrapperHeader)
2461 rcExit = generateFile(g_pszOutput, "wrapper header", generateWrapperHeader);
2462 else
2463 rcExit = generateObject(g_pszOutput, g_pszTempAsm);
2464 }
2465 }
2466
2467 if (rcExit == RTEXITCODE_SUCCESS && g_cTypeErrors > 0)
2468 rcExit = RTEXITCODE_FAILURE;
2469 return rcExit;
2470}
2471
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