]>
Commit | Line | Data |
---|---|---|
e5d623b1 | 1 | #!@preferred_python@ |
bc65bf81 | 2 | # vim: et sta sts=4 sw=4 ts=8 |
c1008fd0 SC |
3 | |
4 | # This handles the systemtap equivalent of | |
d294a649 | 5 | # $(DTRACE) $(DTRACEFLAGS) -G -s $^ -o $@ |
c1008fd0 | 6 | # $(DTRACE) $(DTRACEFLAGS) -h -s $^ -o $@ |
d294a649 SC |
7 | # which is a step that builds DTrace provider and probe definitions |
8 | ||
80ad5b9a | 9 | # Copyright (C) 2009-2017 Red Hat Inc. |
c1008fd0 SC |
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 | ||
d3adcb4a SC |
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 | ||
398d1973 | 23 | import os |
398d1973 | 24 | import sys |
d3adcb4a | 25 | from shlex import split |
c0d0d868 | 26 | from subprocess import call |
e3c5bcd9 | 27 | from tempfile import mkstemp |
d3adcb4a | 28 | try: |
5dd56b4f SC |
29 | from pyparsing import alphas, cStyleComment, delimitedList, Group, \ |
30 | Keyword, lineno, Literal, nestedExpr, nums, oneOf, OneOrMore, \ | |
31 | Optional, ParseException, ParserElement, restOfLine, restOfLine, \ | |
52cac9d8 | 32 | Suppress, SkipTo, Word, ZeroOrMore |
d3adcb4a SC |
33 | HAVE_PYP = True |
34 | except ImportError: | |
35 | HAVE_PYP = False | |
36 | ||
37 | ||
52cac9d8 | 38 | # Common file creation methods for pyparsing and string pattern matching |
d3adcb4a SC |
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") | |
c1008fd0 | 53 | |
d3adcb4a SC |
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): | |
5dd56b4f | 119 | self.current_probe = "" |
d3adcb4a SC |
120 | if self.dtrace_statements is not None: |
121 | return | |
122 | ParserElement.setDefaultWhitespaceChars(' \f\r\n\t\v') | |
123 | ident = Word(alphas+"_", alphas+nums+"_$") | |
5dd56b4f | 124 | probe_ident = Word(alphas+nums+"_$") |
d3adcb4a SC |
125 | semi = Literal(";").suppress() |
126 | integer = Word( nums ) | |
127 | lbrace = Literal("{").suppress() | |
128 | rbrace = Literal("}").suppress() | |
129 | type_name = ident | |
130 | varname = ident | |
5dd56b4f SC |
131 | PROBE = Keyword("probe") |
132 | PROVIDER = Keyword("provider") | |
d3adcb4a | 133 | array_size = integer | ident |
d3adcb4a SC |
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) | |
480ec383 | 137 | member_decl = Group((Optional(oneOf("struct unsigned const")) + type_name) |
d3adcb4a SC |
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) | |
5dd56b4f SC |
143 | def memoize_probe(instring, loc, tokens): |
144 | self.current_probe = tokens[0][1] | |
145 | self.current_lineno = lineno(loc,instring) | |
80ad5b9a | 146 | probe_decl = Group(PROBE + probe_ident + "(" + Optional(Group(delimitedList(member_decl))) + ")" + Optional(Group(Literal(":") + "(" + Optional(Group(delimitedList(member_decl))) + ")")) + Optional(semi)) |
5dd56b4f | 147 | probe_decl.setParseAction(memoize_probe) |
d3adcb4a SC |
148 | probe_decls = OneOrMore(probe_decl) |
149 | ||
5dd56b4f | 150 | provider_decl = (PROVIDER + Optional(ident) |
d3adcb4a | 151 | + lbrace + Group(probe_decls) + rbrace + Optional(semi)) |
52cac9d8 | 152 | dtrace_statement = Group (SkipTo("provider", include=False) + provider_decl) |
d3adcb4a SC |
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 | |
52cac9d8 SC |
170 | # ignore SkipTo token |
171 | if asti[0] != "provider": | |
172 | del asti[0] | |
173 | if asti[0] == "provider": | |
d3adcb4a SC |
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() | |
35771676 FCE |
190 | except ParseException: |
191 | err = sys.exc_info()[1] | |
5dd56b4f | 192 | if len(self.current_probe): |
d576100c | 193 | print("Warning: %s:%s:%d: syntax error near:\nprobe %s\n" % (sys.argv[0], provider, self.current_lineno, self.current_probe)) |
5dd56b4f | 194 | else: |
d576100c | 195 | print("Warning: %s:%s:%d syntax error near:\n%s\n" % (sys.argv[0], provider, err.lineno, err.line)) |
35771676 | 196 | raise err |
d3adcb4a SC |
197 | |
198 | probes_def = "" | |
199 | for asti in self.ast: | |
200 | if len(asti) == 0: | |
201 | continue | |
52cac9d8 SC |
202 | # ignore SkipTo token |
203 | if asti[0] != "provider": | |
204 | del asti[0] | |
d3adcb4a SC |
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): | |
50e767ff JS |
220 | def __init__(self): |
221 | self.semaphores_def = "\n" | |
d3adcb4a | 222 | self.provider = [] |
50e767ff | 223 | |
d3adcb4a SC |
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): | |
c1008fd0 | 232 | have_provider = False |
d3adcb4a SC |
233 | fdesc = open(provider) |
234 | hdr = open(header, mode='w') | |
235 | self.init_probes(hdr) | |
c1008fd0 | 236 | in_comment = False |
d3adcb4a SC |
237 | probes_def = "" |
238 | while True: | |
239 | line = fdesc.readline() | |
240 | if line == "": | |
c1008fd0 | 241 | break |
d3adcb4a | 242 | if line.find("/*") != -1: |
c1008fd0 | 243 | in_comment = True |
d3adcb4a | 244 | if line.find("*/") != -1: |
c1008fd0 SC |
245 | in_comment = False |
246 | continue | |
d3adcb4a | 247 | if in_comment: |
c1008fd0 | 248 | continue |
d3adcb4a | 249 | if line.find("provider") != -1: |
c1008fd0 SC |
250 | tokens = line.split() |
251 | have_provider = True | |
252 | self.provider = tokens[1] | |
d3adcb4a SC |
253 | elif have_provider and line.find("probe ") != -1: |
254 | while line.find(")") < 0: | |
255 | line += fdesc.readline() | |
c1008fd0 | 256 | this_probe = line[line.find("probe ")+5:line.find("(")].strip() |
d3adcb4a | 257 | argstr = (line[line.find("(")+1:line.find(")")]) |
5111fc3e | 258 | arg = "" |
c1008fd0 | 259 | i = 0 |
d3adcb4a SC |
260 | args = [] |
261 | self.__semaphore_append(this_probe) | |
262 | while i < len(argstr): | |
263 | if argstr[i:i+1] == ",": | |
264 | args.append(arg.split()) | |
5111fc3e | 265 | arg = "" |
c1008fd0 | 266 | else: |
d3adcb4a | 267 | arg = arg + argstr[i] |
4f988cd3 | 268 | i += 1 |
e0da6e75 SC |
269 | if len(arg) > 0: |
270 | args.append(arg.split()) | |
d3adcb4a SC |
271 | probes_def += self.add_probe(self.provider, this_probe, args) |
272 | elif line.find("}") != -1 and have_provider: | |
5111fc3e | 273 | have_provider = False |
d3adcb4a SC |
274 | hdr.write(probes_def) |
275 | hdr.close() | |
5111fc3e | 276 | |
c1008fd0 | 277 | |
d3adcb4a | 278 | def usage(): |
d576100c | 279 | print("Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]") |
5111fc3e | 280 | |
d3adcb4a SC |
281 | |
282 | def dtrace_help(): | |
5111fc3e | 283 | usage() |
d576100c NS |
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.") | |
c1008fd0 SC |
292 | sys.exit(1) |
293 | ||
4f988cd3 SC |
294 | |
295 | ######################################################################## | |
296 | # main | |
297 | ######################################################################## | |
298 | ||
a49946ac | 299 | def main(): |
d3adcb4a | 300 | if len(sys.argv) < 2: |
a49946ac | 301 | usage() |
12aad6f0 | 302 | return 1 |
c1008fd0 | 303 | |
d3adcb4a | 304 | global HAVE_PYP |
a49946ac JS |
305 | i = 1 |
306 | build_header = False | |
307 | build_source = False | |
a49946ac JS |
308 | keep_temps = False |
309 | use_cpp = False | |
b571f934 | 310 | suffix = "" |
a49946ac JS |
311 | filename = "" |
312 | s_filename = "" | |
313 | includes = [] | |
314 | defines = [] | |
c486eff3 | 315 | ignore_options = ["-64", "-32", "-fpic", "-fPIC"] |
fd4053fa | 316 | ignore_options2 = ["-x"] # with parameter |
c486eff3 | 317 | |
d3adcb4a SC |
318 | while i < len(sys.argv): |
319 | if sys.argv[i] == "-o": | |
a49946ac JS |
320 | i += 1 |
321 | filename = sys.argv[i] | |
d3adcb4a | 322 | elif sys.argv[i] == "-s": |
a49946ac JS |
323 | i += 1 |
324 | s_filename = sys.argv[i] | |
d3adcb4a | 325 | elif sys.argv[i] == "-C": |
a49946ac | 326 | use_cpp = True |
d3adcb4a | 327 | elif sys.argv[i].startswith("-D"): |
a49946ac | 328 | defines.append(sys.argv[i]) |
d3adcb4a | 329 | elif sys.argv[i] == "-h": |
a49946ac | 330 | build_header = True |
b571f934 | 331 | suffix = ".h" |
d3adcb4a | 332 | elif sys.argv[i].startswith("-I"): |
a49946ac | 333 | includes.append(sys.argv[i]) |
d3adcb4a | 334 | elif sys.argv[i] == "-G": |
a49946ac | 335 | build_source = True |
b571f934 | 336 | suffix = ".o" |
d3adcb4a | 337 | elif sys.argv[i] == "-k": |
a49946ac | 338 | keep_temps = True |
d3adcb4a SC |
339 | elif sys.argv[i] == "--no-pyparsing": |
340 | HAVE_PYP = False | |
341 | elif sys.argv[i] == "--types": | |
d576100c | 342 | print(sys.argv[0] + ": note: obsolete option --types used") |
c486eff3 | 343 | elif sys.argv[i] in ignore_options: |
d3adcb4a | 344 | pass # dtrace users sometimes pass these flags |
fd4053fa FCE |
345 | elif sys.argv[i] in ignore_options2: |
346 | i += 1 | |
347 | pass # dtrace users sometimes pass these flags | |
d3adcb4a SC |
348 | elif sys.argv[i] == "--help": |
349 | dtrace_help() | |
350 | elif sys.argv[i][0] == "-": | |
d576100c | 351 | print(sys.argv[0], "invalid option", sys.argv[i]) |
d3adcb4a SC |
352 | usage() |
353 | return 1 | |
c1008fd0 | 354 | i += 1 |
d3adcb4a | 355 | if not build_header and not build_source: |
dd66ed3f | 356 | usage() |
12aad6f0 | 357 | return 1 |
a49946ac | 358 | |
d3adcb4a SC |
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: | |
d576100c | 364 | print("\"cpp includes s_filename\" failed") |
a49946ac | 365 | usage() |
12aad6f0 | 366 | return 1 |
d3adcb4a SC |
367 | s_filename = fname |
368 | if filename == "": | |
369 | if s_filename != "": | |
370 | (filename, ignore) = os.path.splitext(s_filename) | |
a49946ac JS |
371 | filename = os.path.basename(filename) |
372 | else: | |
373 | usage() | |
12aad6f0 | 374 | return 1 |
c1008fd0 | 375 | else: |
b571f934 | 376 | suffix = "" |
d3adcb4a SC |
377 | |
378 | if build_header: | |
379 | if HAVE_PYP: | |
380 | providers = _PypProvider() | |
381 | else: | |
382 | providers = _ReProvider() | |
52cac9d8 SC |
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 | |
35771676 FCE |
390 | except ParseException: |
391 | err = sys.exc_info()[1] | |
d576100c | 392 | print("Warning: Proceeding as if --no-pyparsing was given.\n") |
52cac9d8 | 393 | providers = _ReProvider() |
d3adcb4a SC |
394 | elif build_source: |
395 | if HAVE_PYP: | |
396 | providers = _PypProvider() | |
a49946ac | 397 | else: |
d3adcb4a SC |
398 | providers = _ReProvider() |
399 | (ignore, fname) = mkstemp(suffix=".h") | |
52cac9d8 SC |
400 | while True: |
401 | try: | |
402 | providers.probe_write(s_filename, fname) | |
403 | break; | |
35771676 FCE |
404 | except ParseException: |
405 | err = sys.exc_info()[1] | |
d576100c | 406 | print("Warning: Proceeding as if --no-pyparsing was given.\n") |
52cac9d8 | 407 | providers = _ReProvider() |
d3adcb4a SC |
408 | if not keep_temps: |
409 | os.remove(fname) | |
410 | else: | |
d576100c | 411 | print("header: " + fname) |
4f46087f | 412 | |
d3adcb4a SC |
413 | (ignore, fname) = mkstemp(suffix=".c") |
414 | fdesc = open(fname, mode='w') | |
415 | providers.semaphore_write(fdesc) | |
416 | fdesc.close() | |
417 | cc1 = os.environ.get("CC", "gcc") | |
100bca3f FCE |
418 | cflags = "-g " + os.environ.get("CFLAGS", "").replace('\n', ' ').replace('\r',' ').replace('\\',' ') |
419 | # sanitize any embedded \ etc. goo; PR21063 | |
d3adcb4a SC |
420 | retcode = call(split(cc1) + defines + includes + split(cflags) + |
421 | ["-fPIC", "-I.", "-I@prefix@/include", "-c", fname, "-o", | |
b571f934 | 422 | filename + suffix], shell=False) |
d3adcb4a | 423 | if retcode != 0: |
d576100c | 424 | print("\"gcc " + fname + "\" failed") |
b571f934 | 425 | usage() |
12aad6f0 | 426 | return 1 |
d3adcb4a SC |
427 | if not keep_temps: |
428 | os.remove(fname) | |
bc65bf81 | 429 | else: |
d576100c | 430 | print("source: " + fname) |
114bb022 | 431 | |
d3adcb4a SC |
432 | if use_cpp: |
433 | if not keep_temps: | |
114bb022 MW |
434 | os.remove(s_filename) |
435 | else: | |
d576100c | 436 | print("cpp: " + s_filename) |
114bb022 | 437 | |
12aad6f0 | 438 | return 0 |
a49946ac JS |
439 | |
440 | if __name__ == "__main__": | |
12aad6f0 | 441 | sys.exit(main()) |