2 # vim: et sta sts=4 sw=4 ts=8
4 # This handles the systemtap equivalent of
5 # $(DTRACE) $(DTRACEFLAGS) -G -s $^ -o $@
6 # $(DTRACE) $(DTRACEFLAGS) -h -s $^ -o $@
7 # which is a step that builds DTrace provider and probe definitions
9 # Copyright (C) 2009-2014 Red Hat Inc.
11 # This file is part of systemtap, and is free software. You can
12 # redistribute it and/or modify it under the terms of the GNU General
13 # Public License (GPL); either version 2, or (at your option) any
16 # ignore line too long, missing docstring, method could be a function,
17 # too many public methods
18 # pylint: disable=C0301
19 # pylint: disable=C0111
20 # pylint: disable=R0201
21 # pylint: disable=R0904
25 from shlex
import split
26 from subprocess
import call
27 from tempfile
import mkstemp
29 from pyparsing
import alphas
, cStyleComment
, delimitedList
, Group
, \
30 Keyword
, lineno
, Literal
, nestedExpr
, nums
, oneOf
, OneOrMore
, \
31 Optional
, ParseException
, ParserElement
, restOfLine
, restOfLine
, \
32 Suppress
, Word
, ZeroOrMore
38 # Common file creation methods for pyparsing and regexparsing
40 class _HeaderCreator(object):
41 def init_semaphores(self
, fdesc
):
42 # dummy declaration just to make the object file non-empty
43 fdesc
.write("/* Generated by the Systemtap dtrace wrapper */\n\n")
44 fdesc
.write("static void __dtrace (void) __attribute__((unused));\n")
45 fdesc
.write("static void __dtrace (void) {}\n")
46 fdesc
.write("\n#include <sys/sdt.h>\n\n")
48 def init_probes(self
, fdesc
):
49 fdesc
.write("/* Generated by the Systemtap dtrace wrapper */\n\n")
50 fdesc
.write("\n#define _SDT_HAS_SEMAPHORES 1\n\n")
51 fdesc
.write("\n#define STAP_HAS_SEMAPHORES 1 /* deprecated */\n\n")
52 fdesc
.write("\n#include <sys/sdt.h>\n\n")
54 def add_semaphore(self
, this_provider
, this_probe
):
55 # NB: unsigned short is fixed in ABI
56 semaphores_def
= '\n#if defined STAP_SDT_V1\n'
57 semaphores_def
+= '#define %s_%s_semaphore %s_semaphore\n' % \
58 (this_provider
, this_probe
, this_probe
)
59 semaphores_def
+= '#endif\n'
60 semaphores_def
+= '#if defined STAP_SDT_V1 || defined STAP_SDT_V2 \n'
61 semaphores_def
+= "__extension__ unsigned short %s_%s_semaphore __attribute__ ((unused)) __attribute__ ((section (\".probes\")));\n" % \
62 (this_provider
, this_probe
)
63 semaphores_def
+= '#else\n'
64 semaphores_def
+= "__extension__ unsigned short %s_%s_semaphore __attribute__ ((unused)) __attribute__ ((section (\".probes\"))) __attribute__ ((visibility (\"hidden\")));\n" % \
65 (this_provider
, this_probe
)
66 semaphores_def
+= '#endif\n'
69 def add_probe(self
, this_provider
, this_probe
, args
):
71 this_probe_canon
= this_provider
.upper() + "_" + this_probe
.replace("__", "_").upper()
72 define_str
= "#define %s(" % (this_probe_canon
)
73 comment_str
= "/* %s (" % (this_probe_canon
)
76 stap_str
+= "DTRACE_PROBE ("
78 stap_str
+= "DTRACE_PROBE%d (" % len(args
)
79 stap_str
+= "%s, %s" % (this_provider
, this_probe
)
85 define_str
= define_str
+ "arg%s" % (i
+ 1)
86 stap_str
= stap_str
+ ", arg%s" % (i
+ 1)
89 comment_str
+= " %s" % argi
92 comment_str
+= " ) */"
93 define_str
+= ") \\\n"
94 probe_def
= '%s\n' % (comment_str
)
95 probe_def
+= ('#if defined STAP_SDT_V1\n')
96 probe_def
+= ('#define %s_ENABLED() __builtin_expect (%s_semaphore, 0)\n' % \
97 (this_probe_canon
, this_probe
))
98 probe_def
+= ('#define %s_%s_semaphore %s_semaphore\n' % \
99 (this_provider
, this_probe
, this_probe
))
100 probe_def
+= ('#else\n')
101 probe_def
+= ('#define %s_ENABLED() __builtin_expect (%s_%s_semaphore, 0)\n' % \
102 (this_probe_canon
, this_provider
, this_probe
))
103 probe_def
+= ('#endif\n')
104 # NB: unsigned short is fixed in ABI
105 probe_def
+= ("__extension__ extern unsigned short %s_%s_semaphore __attribute__ ((unused)) __attribute__ ((section (\".probes\")));\n" % \
106 (this_provider
, this_probe
))
107 probe_def
+= (define_str
+ stap_str
+ "\n\n")
110 # Parse using pyparsing if it is available
112 class _PypProvider(_HeaderCreator
):
116 self
.dtrace_statements
= None
118 def dtrace_bnf(self
):
119 self
.current_probe
= ""
120 if self
.dtrace_statements
is not None:
122 ParserElement
.setDefaultWhitespaceChars(' \f\r\n\t\v')
123 ident
= Word(alphas
+"_", alphas
+nums
+"_$")
124 probe_ident
= Word(alphas
+nums
+"_$")
125 semi
= Literal(";").suppress()
126 integer
= Word( nums
)
127 lbrace
= Literal("{").suppress()
128 rbrace
= Literal("}").suppress()
131 PROBE
= Keyword("probe")
132 PROVIDER
= Keyword("provider")
133 array_size
= integer | ident
134 varname_spec
= varname
+ Optional("[" + array_size
+ "]")
135 struct_decl
= Group(oneOf("struct union") + varname
+ Suppress(nestedExpr('{','}')) + semi
)
136 enum_decl
= Group("enum" + varname
+ Suppress(nestedExpr('{','}')) + semi
)
137 member_decl
= Group((Optional(oneOf("struct unsigned const")) + type_name
)
138 + Optional(Word("*"), default
="") + Optional(varname_spec
))
139 struct_typedef
= Group(Literal("typedef") + Literal("struct") + varname
140 + Suppress(nestedExpr('{','}'))) + Optional(varname
) + semi
141 typedef
= ZeroOrMore("typedef" + (member_decl
)) + semi
142 decls
= OneOrMore(struct_typedef | struct_decl | typedef | enum_decl
)
143 def memoize_probe(instring
, loc
, tokens
):
144 self
.current_probe
= tokens
[0][1]
145 self
.current_lineno
= lineno(loc
,instring
)
146 probe_decl
= Group(PROBE
+ probe_ident
+ "(" + Optional(Group(delimitedList(member_decl
))) + ")" + Optional(semi
))
147 probe_decl
.setParseAction(memoize_probe
)
148 probe_decls
= OneOrMore(probe_decl
)
150 provider_decl
= (PROVIDER
+ Optional(ident
)
151 + lbrace
+ Group(probe_decls
) + rbrace
+ Optional(semi
))
152 dtrace_statement
= Group(decls | provider_decl
)
153 self
.dtrace_statements
= ZeroOrMore(dtrace_statement
)
155 cplusplus_linecomment
= Literal("//") + restOfLine
156 cpp_linecomment
= Literal("#") + restOfLine
158 self
.dtrace_statements
.ignore(cStyleComment
)
159 self
.dtrace_statements
.ignore(cplusplus_linecomment
)
160 self
.dtrace_statements
.ignore(cpp_linecomment
)
162 self
.bnf
= self
.dtrace_statements
164 def semaphore_write(self
, fdesc
):
166 self
.init_semaphores(fdesc
)
167 for asti
in self
.ast
:
170 elif asti
[0] == "provider":
173 semaphores_def
+= self
.add_semaphore(asti
[1], prb
[1])
174 fdesc
.write(semaphores_def
)
177 def probe_write(self
, provider
, header
):
178 hdr
= open(header
, mode
='w')
179 self
.init_probes(hdr
)
183 self
.ast
= self
.bnf
.parseFile(provider
, parseAll
=True).asList()
185 # pyparsing-1.5.0 does not support parseAll
186 self
.ast
= self
.bnf
.parseFile(provider
).asList()
187 except ParseException
, err
:
188 if len(self
.current_probe
):
189 print "%s:%s:%d: syntax error near:\nprobe %s\n" % (sys
.argv
[0],provider
, self
.current_lineno
, self
.current_probe
)
191 print "%s:%s:%d syntax error near:\n%s\n" % (sys
.argv
[0],provider
,err
.lineno
, err
.line
)
195 for asti
in self
.ast
:
198 if asti
[0] == "provider":
201 if prb
[3] == ')': # No parsed argument list
205 probes_def
+= self
.add_probe(asti
[1], prb
[1], alist
)
206 hdr
.write(probes_def
)
210 # Parse using regular expressions if pyparsing is not available
212 class _ReProvider(_HeaderCreator
):
214 self
.semaphores_def
= "\n"
217 def __semaphore_append(self
, this_probe
):
218 self
.semaphores_def
+= self
.add_semaphore(self
.provider
, this_probe
)
220 def semaphore_write(self
, fdesc
):
221 self
.init_semaphores(fdesc
)
222 fdesc
.write(self
.semaphores_def
)
224 def probe_write(self
, provider
, header
):
225 have_provider
= False
226 fdesc
= open(provider
)
227 hdr
= open(header
, mode
='w')
228 self
.init_probes(hdr
)
232 line
= fdesc
.readline()
235 if line
.find("/*") != -1:
237 if line
.find("*/") != -1:
242 if line
.find("provider") != -1:
243 tokens
= line
.split()
245 self
.provider
= tokens
[1]
246 elif have_provider
and line
.find("probe ") != -1:
247 while line
.find(")") < 0:
248 line
+= fdesc
.readline()
249 this_probe
= line
[line
.find("probe ")+5:line
.find("(")].strip()
250 argstr
= (line
[line
.find("(")+1:line
.find(")")])
254 self
.__semaphore
_append
(this_probe
)
255 while i
< len(argstr
):
256 if argstr
[i
:i
+1] == ",":
257 args
.append(arg
.split())
260 arg
= arg
+ argstr
[i
]
263 args
.append(arg
.split())
264 probes_def
+= self
.add_probe(self
.provider
, this_probe
, args
)
265 elif line
.find("}") != -1 and have_provider
:
266 have_provider
= False
267 hdr
.write(probes_def
)
272 print "Usage " + sys
.argv
[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]"
277 print "Where -h builds a systemtap header file from the .d file"
278 print " -C when used with -h, also run cpp preprocessor"
279 print " -o specifies an explicit output file name,"
280 print " the default for -G is file.o and -h is file.h"
281 print " -I when running cpp pass through this -I include Path"
282 print " -s specifies the name of the .d input file"
283 print " -G builds a stub file.o from file.d,"
284 print " which is required by some packages that use dtrace."
288 ########################################################################
290 ########################################################################
293 if len(sys
.argv
) < 2:
308 while i
< len(sys
.argv
):
309 if sys
.argv
[i
] == "-o":
311 filename
= sys
.argv
[i
]
312 elif sys
.argv
[i
] == "-s":
314 s_filename
= sys
.argv
[i
]
315 elif sys
.argv
[i
] == "-C":
317 elif sys
.argv
[i
].startswith("-D"):
318 defines
.append(sys
.argv
[i
])
319 elif sys
.argv
[i
] == "-h":
322 elif sys
.argv
[i
].startswith("-I"):
323 includes
.append(sys
.argv
[i
])
324 elif sys
.argv
[i
] == "-G":
327 elif sys
.argv
[i
] == "-k":
329 elif sys
.argv
[i
] == "--no-pyparsing":
331 elif sys
.argv
[i
] == "--types":
332 print sys
.argv
[0] + ": note: obsolete option --types used"
333 elif sys
.argv
[i
] == "-64" or sys
.argv
[i
] == "-32":
334 pass # dtrace users sometimes pass these flags
335 elif sys
.argv
[i
] == "--help":
337 elif sys
.argv
[i
][0] == "-":
338 print sys
.argv
[0], "invalid option", sys
.argv
[i
]
342 if not build_header
and not build_source
:
346 if s_filename
!= "" and use_cpp
:
347 (ignore
, fname
) = mkstemp(suffix
=".d")
348 cpp
= os
.environ
.get("CPP", "cpp")
349 retcode
= call(split(cpp
) + includes
+ defines
+ [s_filename
, fname
])
351 print "\"cpp includes s_filename\" failed"
357 (filename
, ignore
) = os
.path
.splitext(s_filename
)
358 filename
= os
.path
.basename(filename
)
367 providers
= _PypProvider()
369 providers
= _ReProvider()
370 providers
.probe_write(s_filename
, filename
+ suffix
)
373 providers
= _PypProvider()
375 providers
= _ReProvider()
376 (ignore
, fname
) = mkstemp(suffix
=".h")
377 providers
.probe_write(s_filename
, fname
)
381 print "header: " + fname
383 (ignore
, fname
) = mkstemp(suffix
=".c")
384 fdesc
= open(fname
, mode
='w')
385 providers
.semaphore_write(fdesc
)
387 cc1
= os
.environ
.get("CC", "gcc")
388 cflags
= "-g " + os
.environ
.get("CFLAGS", "")
389 retcode
= call(split(cc1
) + defines
+ includes
+ split(cflags
) +
390 ["-fPIC", "-I.", "-I@prefix@/include", "-c", fname
, "-o",
391 filename
+ suffix
], shell
=False)
393 print "\"gcc " + fname
+ "\" failed"
399 print "source: " + fname
403 os
.remove(s_filename
)
405 print "cpp: " + s_filename
409 if __name__
== "__main__":