]> sourceware.org Git - systemtap.git/blob - dtrace.in
stapbpf PR22330 fixes :: identify format types of pe_unknown arguments
[systemtap.git] / dtrace.in
1 #!@preferred_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-2018 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, SkipTo, Word, ZeroOrMore
33 HAVE_PYP = True
34 except ImportError:
35 HAVE_PYP = False
36
37
38 # Common file creation methods for pyparsing and string pattern matching
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 + Optional(Keyword("const")))
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(Group(Literal(":") + "(" + 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 (SkipTo("provider", include=False) + 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 # ignore SkipTo token
171 if asti[0] != "provider":
172 del asti[0]
173 if asti[0] == "provider":
174 # list of probes
175 for prb in asti[2]:
176 semaphores_def += self.add_semaphore(asti[1], prb[1])
177 fdesc.write(semaphores_def)
178
179
180 def probe_write(self, provider, header):
181 hdr = open(header, mode='w')
182 self.init_probes(hdr)
183 self.dtrace_bnf()
184 try:
185 try:
186 self.ast = self.bnf.parseFile(provider, parseAll=True).asList()
187 except TypeError:
188 # pyparsing-1.5.0 does not support parseAll
189 self.ast = self.bnf.parseFile(provider).asList()
190 except ParseException:
191 err = sys.exc_info()[1]
192 if len(self.current_probe):
193 print("Warning: %s:%s:%d: syntax error near:\nprobe %s\n" % (sys.argv[0], provider, self.current_lineno, self.current_probe))
194 else:
195 print("Warning: %s:%s:%d syntax error near:\n%s\n" % (sys.argv[0], provider, err.lineno, err.line))
196 raise err
197
198 probes_def = ""
199 for asti in self.ast:
200 if len(asti) == 0:
201 continue
202 # ignore SkipTo token
203 if asti[0] != "provider":
204 del asti[0]
205 if asti[0] == "provider":
206 # list of probes
207 for prb in asti[2]:
208 if prb[3] == ')': # No parsed argument list
209 alist = []
210 else:
211 alist = prb[3]
212 probes_def += self.add_probe(asti[1], prb[1], alist)
213 hdr.write(probes_def)
214 hdr.close()
215
216
217 # Parse using regular expressions if pyparsing is not available
218
219 class _ReProvider(_HeaderCreator):
220 def __init__(self):
221 self.semaphores_def = "\n"
222 self.provider = []
223
224 def __semaphore_append(self, this_probe):
225 self.semaphores_def += self.add_semaphore(self.provider, this_probe)
226
227 def semaphore_write(self, fdesc):
228 self.init_semaphores(fdesc)
229 fdesc.write(self.semaphores_def)
230
231 def probe_write(self, provider, header):
232 have_provider = False
233 fdesc = open(provider)
234 hdr = open(header, mode='w')
235 self.init_probes(hdr)
236 in_comment = False
237 probes_def = ""
238 while True:
239 line = fdesc.readline()
240 if line == "":
241 break
242 if line.find("/*") != -1:
243 in_comment = True
244 if line.find("*/") != -1:
245 in_comment = False
246 continue
247 if in_comment:
248 continue
249 if line.find("provider") != -1:
250 tokens = line.split()
251 have_provider = True
252 self.provider = tokens[1]
253 elif have_provider and line.find("probe ") != -1:
254 while line.find(")") < 0:
255 line += fdesc.readline()
256 this_probe = line[line.find("probe ")+5:line.find("(")].strip()
257 argstr = (line[line.find("(")+1:line.find(")")])
258 arg = ""
259 i = 0
260 args = []
261 self.__semaphore_append(this_probe)
262 while i < len(argstr):
263 if argstr[i:i+1] == ",":
264 args.append(arg.split())
265 arg = ""
266 else:
267 arg = arg + argstr[i]
268 i += 1
269 if len(arg) > 0:
270 args.append(arg.split())
271 probes_def += self.add_probe(self.provider, this_probe, args)
272 elif line.find("}") != -1 and have_provider:
273 have_provider = False
274 hdr.write(probes_def)
275 hdr.close()
276
277
278 def usage():
279 print("Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]")
280
281
282 def dtrace_help():
283 usage()
284 print("Where -h builds a systemtap header file from the .d file")
285 print(" -C when used with -h, also run cpp preprocessor")
286 print(" -o specifies an explicit output file name,")
287 print(" the default for -G is file.o and -h is file.h")
288 print(" -I when running cpp pass through this -I include Path")
289 print(" -s specifies the name of the .d input file")
290 print(" -G builds a stub file.o from file.d,")
291 print(" which is required by some packages that use dtrace.")
292 sys.exit(1)
293
294
295 ########################################################################
296 # main
297 ########################################################################
298
299 def main():
300 if len(sys.argv) < 2:
301 usage()
302 return 1
303
304 global HAVE_PYP
305 i = 1
306 build_header = False
307 build_source = False
308 keep_temps = False
309 use_cpp = False
310 suffix = ""
311 filename = ""
312 s_filename = ""
313 includes = []
314 defines = []
315 ignore_options = ["-64", "-32", "-fpic", "-fPIC"]
316 ignore_options2 = ["-x"] # with parameter
317
318 while i < len(sys.argv):
319 if sys.argv[i] == "-o":
320 i += 1
321 filename = sys.argv[i]
322 elif sys.argv[i] == "-s":
323 i += 1
324 s_filename = sys.argv[i]
325 elif sys.argv[i] == "-C":
326 use_cpp = True
327 elif sys.argv[i].startswith("-D"):
328 defines.append(sys.argv[i])
329 elif sys.argv[i] == "-h":
330 build_header = True
331 suffix = ".h"
332 elif sys.argv[i].startswith("-I"):
333 includes.append(sys.argv[i])
334 elif sys.argv[i] == "-G":
335 build_source = True
336 suffix = ".o"
337 elif sys.argv[i] == "-k":
338 keep_temps = True
339 elif sys.argv[i] == "--no-pyparsing":
340 HAVE_PYP = False
341 elif sys.argv[i] == "--types":
342 print(sys.argv[0] + ": note: obsolete option --types used")
343 elif sys.argv[i] in ignore_options:
344 pass # dtrace users sometimes pass these flags
345 elif sys.argv[i] in ignore_options2:
346 i += 1
347 pass # dtrace users sometimes pass these flags
348 elif sys.argv[i] == "--help":
349 dtrace_help()
350 elif sys.argv[i][0] == "-":
351 print(sys.argv[0], "invalid option", sys.argv[i])
352 usage()
353 return 1
354 i += 1
355 if not build_header and not build_source:
356 usage()
357 return 1
358
359 if s_filename != "" and use_cpp:
360 (ignore, fname) = mkstemp(suffix=".d")
361 cpp = os.environ.get("CPP", "cpp")
362 retcode = call(split(cpp) + includes + defines + [s_filename, fname])
363 if retcode != 0:
364 print("\"cpp includes s_filename\" failed")
365 usage()
366 return 1
367 s_filename = fname
368 if filename == "":
369 if s_filename != "":
370 (filename, ignore) = os.path.splitext(s_filename)
371 filename = os.path.basename(filename)
372 else:
373 usage()
374 return 1
375 else:
376 suffix = ""
377
378 if build_header:
379 if HAVE_PYP:
380 providers = _PypProvider()
381 else:
382 providers = _ReProvider()
383 while True:
384 try:
385 providers.probe_write(s_filename, filename + suffix)
386 break;
387 # complex C declarations can fool the pyparsing grammar.
388 # we could increase the complexity of the grammar
389 # instead we fall back to string pattern matching
390 except ParseException:
391 err = sys.exc_info()[1]
392 print("Warning: Proceeding as if --no-pyparsing was given.\n")
393 providers = _ReProvider()
394 elif build_source:
395 if HAVE_PYP:
396 providers = _PypProvider()
397 else:
398 providers = _ReProvider()
399 (ignore, fname) = mkstemp(suffix=".h")
400 while True:
401 try:
402 providers.probe_write(s_filename, fname)
403 break;
404 except ParseException:
405 err = sys.exc_info()[1]
406 print("Warning: Proceeding as if --no-pyparsing was given.\n")
407 providers = _ReProvider()
408 if not keep_temps:
409 os.remove(fname)
410 else:
411 print("header: " + fname)
412
413 try: # for reproducible-builds purposes, prefer a fixed path name pattern
414 fname = filename + ".dtrace-temp.c"
415 fdesc = open(fname, mode='w')
416 except: # but that doesn't work for -o /dev/null - see rhbz1504009
417 (ignore,fname) = mkstemp(suffix=".c")
418 fdesc = open(fname, mode='w')
419 providers.semaphore_write(fdesc)
420 fdesc.close()
421 cc1 = os.environ.get("CC", "gcc")
422 cflags = "-g " + os.environ.get("CFLAGS", "").replace('\\\n', ' ').replace('\\\r',' ')
423 # sanitize any embedded \n etc. goo; PR21063
424 retcode = call(split(cc1) + defines + includes + split(cflags) +
425 ["-fPIC", "-I.", "-I@prefix@/include", "-c", fname, "-o",
426 filename + suffix], shell=False)
427 if retcode != 0:
428 print("\"gcc " + fname + "\" failed")
429 usage()
430 return 1
431 if not keep_temps:
432 os.remove(fname)
433 else:
434 print("source: " + fname)
435
436 if use_cpp:
437 if not keep_temps:
438 os.remove(s_filename)
439 else:
440 print("cpp: " + s_filename)
441
442 return 0
443
444 if __name__ == "__main__":
445 sys.exit(main())
This page took 0.054591 seconds and 5 git commands to generate.