]> sourceware.org Git - systemtap.git/blob - dtrace.in
Merge branch 'master' of ssh://sourceware.org/git/systemtap
[systemtap.git] / dtrace.in
1 #!/usr/bin/python
2 # vim: et sta sts=4 sw=4 ts=8
3
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
8
9 # Copyright (C) 2009-2014 Red Hat Inc.
10 #
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
14 # later version.
15
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
22
23 import os
24 import sys
25 from shlex import split
26 from subprocess import call
27 from tempfile import mkstemp
28 try:
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
33 HAVE_PYP = True
34 except ImportError:
35 HAVE_PYP = False
36
37
38 # Common file creation methods for pyparsing and regexparsing
39
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")
47
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")
53
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'
67 return semaphores_def
68
69 def add_probe(self, this_provider, this_probe, args):
70 stap_str = ""
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)
74
75 if len(args) == 0:
76 stap_str += "DTRACE_PROBE ("
77 else:
78 stap_str += "DTRACE_PROBE%d (" % len(args)
79 stap_str += "%s, %s" % (this_provider, this_probe)
80 i = 0
81 while i < len(args):
82 if i != 0:
83 define_str += ", "
84 comment_str += ","
85 define_str = define_str + "arg%s" % (i + 1)
86 stap_str = stap_str + ", arg%s" % (i + 1)
87 for argi in args[i]:
88 if len(argi) > 0:
89 comment_str += " %s" % argi
90 i += 1
91 stap_str += ")"
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")
108 return probe_def
109
110 # Parse using pyparsing if it is available
111
112 class _PypProvider(_HeaderCreator):
113 def __init__(self):
114 self.ast = []
115 self.bnf = None
116 self.dtrace_statements = None
117
118 def dtrace_bnf(self):
119 self.current_probe = ""
120 if self.dtrace_statements is not None:
121 return
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()
129 type_name = ident
130 varname = ident
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)
149
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)
154
155 cplusplus_linecomment = Literal("//") + restOfLine
156 cpp_linecomment = Literal("#") + restOfLine
157
158 self.dtrace_statements.ignore(cStyleComment)
159 self.dtrace_statements.ignore(cplusplus_linecomment)
160 self.dtrace_statements.ignore(cpp_linecomment)
161
162 self.bnf = self.dtrace_statements
163
164 def semaphore_write(self, fdesc):
165 semaphores_def = ""
166 self.init_semaphores(fdesc)
167 for asti in self.ast:
168 if len(asti) == 0:
169 continue
170 elif asti[0] == "provider":
171 # list of probes
172 for prb in asti[2]:
173 semaphores_def += self.add_semaphore(asti[1], prb[1])
174 fdesc.write(semaphores_def)
175
176
177 def probe_write(self, provider, header):
178 hdr = open(header, mode='w')
179 self.init_probes(hdr)
180 self.dtrace_bnf()
181 try:
182 try:
183 self.ast = self.bnf.parseFile(provider, parseAll=True).asList()
184 except TypeError:
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)
190 else:
191 print "%s:%s:%d syntax error near:\n%s\n" % (sys.argv[0],provider,err.lineno, err.line)
192 sys.exit(1)
193
194 probes_def = ""
195 for asti in self.ast:
196 if len(asti) == 0:
197 continue
198 if asti[0] == "provider":
199 # list of probes
200 for prb in asti[2]:
201 if prb[3] == ')': # No parsed argument list
202 alist = []
203 else:
204 alist = prb[3]
205 probes_def += self.add_probe(asti[1], prb[1], alist)
206 hdr.write(probes_def)
207 hdr.close()
208
209
210 # Parse using regular expressions if pyparsing is not available
211
212 class _ReProvider(_HeaderCreator):
213 def __init__(self):
214 self.semaphores_def = "\n"
215 self.provider = []
216
217 def __semaphore_append(self, this_probe):
218 self.semaphores_def += self.add_semaphore(self.provider, this_probe)
219
220 def semaphore_write(self, fdesc):
221 self.init_semaphores(fdesc)
222 fdesc.write(self.semaphores_def)
223
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)
229 in_comment = False
230 probes_def = ""
231 while True:
232 line = fdesc.readline()
233 if line == "":
234 break
235 if line.find("/*") != -1:
236 in_comment = True
237 if line.find("*/") != -1:
238 in_comment = False
239 continue
240 if in_comment:
241 continue
242 if line.find("provider") != -1:
243 tokens = line.split()
244 have_provider = True
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(")")])
251 arg = ""
252 i = 0
253 args = []
254 self.__semaphore_append(this_probe)
255 while i < len(argstr):
256 if argstr[i:i+1] == ",":
257 args.append(arg.split())
258 arg = ""
259 else:
260 arg = arg + argstr[i]
261 i += 1
262 if len(arg) > 0:
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)
268 hdr.close()
269
270
271 def usage():
272 print "Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]"
273
274
275 def dtrace_help():
276 usage()
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."
285 sys.exit(1)
286
287
288 ########################################################################
289 # main
290 ########################################################################
291
292 def main():
293 if len(sys.argv) < 2:
294 usage()
295 return 1
296
297 global HAVE_PYP
298 i = 1
299 build_header = False
300 build_source = False
301 keep_temps = False
302 use_cpp = False
303 suffix = ""
304 filename = ""
305 s_filename = ""
306 includes = []
307 defines = []
308 while i < len(sys.argv):
309 if sys.argv[i] == "-o":
310 i += 1
311 filename = sys.argv[i]
312 elif sys.argv[i] == "-s":
313 i += 1
314 s_filename = sys.argv[i]
315 elif sys.argv[i] == "-C":
316 use_cpp = True
317 elif sys.argv[i].startswith("-D"):
318 defines.append(sys.argv[i])
319 elif sys.argv[i] == "-h":
320 build_header = True
321 suffix = ".h"
322 elif sys.argv[i].startswith("-I"):
323 includes.append(sys.argv[i])
324 elif sys.argv[i] == "-G":
325 build_source = True
326 suffix = ".o"
327 elif sys.argv[i] == "-k":
328 keep_temps = True
329 elif sys.argv[i] == "--no-pyparsing":
330 HAVE_PYP = False
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":
336 dtrace_help()
337 elif sys.argv[i][0] == "-":
338 print sys.argv[0], "invalid option", sys.argv[i]
339 usage()
340 return 1
341 i += 1
342 if not build_header and not build_source:
343 usage()
344 return 1
345
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])
350 if retcode != 0:
351 print "\"cpp includes s_filename\" failed"
352 usage()
353 return 1
354 s_filename = fname
355 if filename == "":
356 if s_filename != "":
357 (filename, ignore) = os.path.splitext(s_filename)
358 filename = os.path.basename(filename)
359 else:
360 usage()
361 return 1
362 else:
363 suffix = ""
364
365 if build_header:
366 if HAVE_PYP:
367 providers = _PypProvider()
368 else:
369 providers = _ReProvider()
370 providers.probe_write(s_filename, filename + suffix)
371 elif build_source:
372 if HAVE_PYP:
373 providers = _PypProvider()
374 else:
375 providers = _ReProvider()
376 (ignore, fname) = mkstemp(suffix=".h")
377 providers.probe_write(s_filename, fname)
378 if not keep_temps:
379 os.remove(fname)
380 else:
381 print "header: " + fname
382
383 (ignore, fname) = mkstemp(suffix=".c")
384 fdesc = open(fname, mode='w')
385 providers.semaphore_write(fdesc)
386 fdesc.close()
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)
392 if retcode != 0:
393 print "\"gcc " + fname + "\" failed"
394 usage()
395 return 1
396 if not keep_temps:
397 os.remove(fname)
398 else:
399 print "source: " + fname
400
401 if use_cpp:
402 if not keep_temps:
403 os.remove(s_filename)
404 else:
405 print "cpp: " + s_filename
406
407 return 0
408
409 if __name__ == "__main__":
410 sys.exit(main())
This page took 0.064018 seconds and 6 git commands to generate.