]> sourceware.org Git - systemtap.git/blame - dtrace.in
Fixed systemtap-server.exp
[systemtap.git] / dtrace.in
CommitLineData
c1008fd0 1#!/usr/bin/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
d3adcb4a 9# Copyright (C) 2009-2014 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 23import os
398d1973 24import sys
d3adcb4a 25from shlex import split
c0d0d868 26from subprocess import call
e3c5bcd9 27from tempfile import mkstemp
d3adcb4a 28try:
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
34except ImportError:
35 HAVE_PYP = False
36
37
52cac9d8 38# Common file creation methods for pyparsing and string pattern matching
d3adcb4a
SC
39
40class _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
112class _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)
146 probe_decl = Group(PROBE + probe_ident + "(" + Optional(Group(delimitedList(member_decl))) + ")" + Optional(semi))
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()
190 except ParseException, err:
5dd56b4f 191 if len(self.current_probe):
52cac9d8 192 print "Warning: %s:%s:%d: syntax error near:\nprobe %s\n" % (sys.argv[0],provider, self.current_lineno, self.current_probe)
5dd56b4f 193 else:
52cac9d8
SC
194 print "Warning: %s:%s:%d syntax error near:\n%s\n" % (sys.argv[0],provider,err.lineno, err.line)
195 raise ParseException, err
d3adcb4a
SC
196
197 probes_def = ""
198 for asti in self.ast:
199 if len(asti) == 0:
200 continue
52cac9d8
SC
201 # ignore SkipTo token
202 if asti[0] != "provider":
203 del asti[0]
d3adcb4a
SC
204 if asti[0] == "provider":
205 # list of probes
206 for prb in asti[2]:
207 if prb[3] == ')': # No parsed argument list
208 alist = []
209 else:
210 alist = prb[3]
211 probes_def += self.add_probe(asti[1], prb[1], alist)
212 hdr.write(probes_def)
213 hdr.close()
214
215
216# Parse using regular expressions if pyparsing is not available
217
218class _ReProvider(_HeaderCreator):
50e767ff
JS
219 def __init__(self):
220 self.semaphores_def = "\n"
d3adcb4a 221 self.provider = []
50e767ff 222
d3adcb4a
SC
223 def __semaphore_append(self, this_probe):
224 self.semaphores_def += self.add_semaphore(self.provider, this_probe)
225
226 def semaphore_write(self, fdesc):
227 self.init_semaphores(fdesc)
228 fdesc.write(self.semaphores_def)
229
230 def probe_write(self, provider, header):
c1008fd0 231 have_provider = False
d3adcb4a
SC
232 fdesc = open(provider)
233 hdr = open(header, mode='w')
234 self.init_probes(hdr)
c1008fd0 235 in_comment = False
d3adcb4a
SC
236 probes_def = ""
237 while True:
238 line = fdesc.readline()
239 if line == "":
c1008fd0 240 break
d3adcb4a 241 if line.find("/*") != -1:
c1008fd0 242 in_comment = True
d3adcb4a 243 if line.find("*/") != -1:
c1008fd0
SC
244 in_comment = False
245 continue
d3adcb4a 246 if in_comment:
c1008fd0 247 continue
d3adcb4a 248 if line.find("provider") != -1:
c1008fd0
SC
249 tokens = line.split()
250 have_provider = True
251 self.provider = tokens[1]
d3adcb4a
SC
252 elif have_provider and line.find("probe ") != -1:
253 while line.find(")") < 0:
254 line += fdesc.readline()
c1008fd0 255 this_probe = line[line.find("probe ")+5:line.find("(")].strip()
d3adcb4a 256 argstr = (line[line.find("(")+1:line.find(")")])
5111fc3e 257 arg = ""
c1008fd0 258 i = 0
d3adcb4a
SC
259 args = []
260 self.__semaphore_append(this_probe)
261 while i < len(argstr):
262 if argstr[i:i+1] == ",":
263 args.append(arg.split())
5111fc3e 264 arg = ""
c1008fd0 265 else:
d3adcb4a 266 arg = arg + argstr[i]
4f988cd3 267 i += 1
e0da6e75
SC
268 if len(arg) > 0:
269 args.append(arg.split())
d3adcb4a
SC
270 probes_def += self.add_probe(self.provider, this_probe, args)
271 elif line.find("}") != -1 and have_provider:
5111fc3e 272 have_provider = False
d3adcb4a
SC
273 hdr.write(probes_def)
274 hdr.close()
5111fc3e 275
c1008fd0 276
d3adcb4a 277def usage():
a54d79ce 278 print "Usage " + sys.argv[0] + " [--help] [-h | -G] [-C [-I<Path>]] -s File.d [-o <File>]"
5111fc3e 279
d3adcb4a
SC
280
281def dtrace_help():
5111fc3e 282 usage()
a54d79ce
FCE
283 print "Where -h builds a systemtap header file from the .d file"
284 print " -C when used with -h, also run cpp preprocessor"
285 print " -o specifies an explicit output file name,"
286 print " the default for -G is file.o and -h is file.h"
287 print " -I when running cpp pass through this -I include Path"
288 print " -s specifies the name of the .d input file"
289 print " -G builds a stub file.o from file.d,"
290 print " which is required by some packages that use dtrace."
c1008fd0
SC
291 sys.exit(1)
292
4f988cd3
SC
293
294########################################################################
295# main
296########################################################################
297
a49946ac 298def main():
d3adcb4a 299 if len(sys.argv) < 2:
a49946ac 300 usage()
12aad6f0 301 return 1
c1008fd0 302
d3adcb4a 303 global HAVE_PYP
a49946ac
JS
304 i = 1
305 build_header = False
306 build_source = False
a49946ac
JS
307 keep_temps = False
308 use_cpp = False
b571f934 309 suffix = ""
a49946ac
JS
310 filename = ""
311 s_filename = ""
312 includes = []
313 defines = []
c486eff3 314 ignore_options = ["-64", "-32", "-fpic", "-fPIC"]
fd4053fa 315 ignore_options2 = ["-x"] # with parameter
c486eff3 316
d3adcb4a
SC
317 while i < len(sys.argv):
318 if sys.argv[i] == "-o":
a49946ac
JS
319 i += 1
320 filename = sys.argv[i]
d3adcb4a 321 elif sys.argv[i] == "-s":
a49946ac
JS
322 i += 1
323 s_filename = sys.argv[i]
d3adcb4a 324 elif sys.argv[i] == "-C":
a49946ac 325 use_cpp = True
d3adcb4a 326 elif sys.argv[i].startswith("-D"):
a49946ac 327 defines.append(sys.argv[i])
d3adcb4a 328 elif sys.argv[i] == "-h":
a49946ac 329 build_header = True
b571f934 330 suffix = ".h"
d3adcb4a 331 elif sys.argv[i].startswith("-I"):
a49946ac 332 includes.append(sys.argv[i])
d3adcb4a 333 elif sys.argv[i] == "-G":
a49946ac 334 build_source = True
b571f934 335 suffix = ".o"
d3adcb4a 336 elif sys.argv[i] == "-k":
a49946ac 337 keep_temps = True
d3adcb4a
SC
338 elif sys.argv[i] == "--no-pyparsing":
339 HAVE_PYP = False
340 elif sys.argv[i] == "--types":
341 print sys.argv[0] + ": note: obsolete option --types used"
c486eff3 342 elif sys.argv[i] in ignore_options:
d3adcb4a 343 pass # dtrace users sometimes pass these flags
fd4053fa
FCE
344 elif sys.argv[i] in ignore_options2:
345 i += 1
346 pass # dtrace users sometimes pass these flags
d3adcb4a
SC
347 elif sys.argv[i] == "--help":
348 dtrace_help()
349 elif sys.argv[i][0] == "-":
350 print sys.argv[0], "invalid option", sys.argv[i]
351 usage()
352 return 1
c1008fd0 353 i += 1
d3adcb4a 354 if not build_header and not build_source:
dd66ed3f 355 usage()
12aad6f0 356 return 1
a49946ac 357
d3adcb4a
SC
358 if s_filename != "" and use_cpp:
359 (ignore, fname) = mkstemp(suffix=".d")
360 cpp = os.environ.get("CPP", "cpp")
361 retcode = call(split(cpp) + includes + defines + [s_filename, fname])
362 if retcode != 0:
a49946ac
JS
363 print "\"cpp includes s_filename\" failed"
364 usage()
12aad6f0 365 return 1
d3adcb4a
SC
366 s_filename = fname
367 if filename == "":
368 if s_filename != "":
369 (filename, ignore) = os.path.splitext(s_filename)
a49946ac
JS
370 filename = os.path.basename(filename)
371 else:
372 usage()
12aad6f0 373 return 1
c1008fd0 374 else:
b571f934 375 suffix = ""
d3adcb4a
SC
376
377 if build_header:
378 if HAVE_PYP:
379 providers = _PypProvider()
380 else:
381 providers = _ReProvider()
52cac9d8
SC
382 while True:
383 try:
384 providers.probe_write(s_filename, filename + suffix)
385 break;
386 # complex C declarations can fool the pyparsing grammar.
387 # we could increase the complexity of the grammar
388 # instead we fall back to string pattern matching
389 except ParseException, err:
390 print "Warning: Proceeding as if --no-pyparsing was given.\n"
391 providers = _ReProvider()
d3adcb4a
SC
392 elif build_source:
393 if HAVE_PYP:
394 providers = _PypProvider()
a49946ac 395 else:
d3adcb4a
SC
396 providers = _ReProvider()
397 (ignore, fname) = mkstemp(suffix=".h")
52cac9d8
SC
398 while True:
399 try:
400 providers.probe_write(s_filename, fname)
401 break;
402 except ParseException, err:
403 print "Warning: Proceeding as if --no-pyparsing was given.\n"
404 providers = _ReProvider()
d3adcb4a
SC
405 if not keep_temps:
406 os.remove(fname)
407 else:
408 print "header: " + fname
4f46087f 409
d3adcb4a
SC
410 (ignore, fname) = mkstemp(suffix=".c")
411 fdesc = open(fname, mode='w')
412 providers.semaphore_write(fdesc)
413 fdesc.close()
414 cc1 = os.environ.get("CC", "gcc")
415 cflags = "-g " + os.environ.get("CFLAGS", "")
416 retcode = call(split(cc1) + defines + includes + split(cflags) +
417 ["-fPIC", "-I.", "-I@prefix@/include", "-c", fname, "-o",
b571f934 418 filename + suffix], shell=False)
d3adcb4a
SC
419 if retcode != 0:
420 print "\"gcc " + fname + "\" failed"
b571f934 421 usage()
12aad6f0 422 return 1
d3adcb4a
SC
423 if not keep_temps:
424 os.remove(fname)
bc65bf81 425 else:
d3adcb4a 426 print "source: " + fname
114bb022 427
d3adcb4a
SC
428 if use_cpp:
429 if not keep_temps:
114bb022
MW
430 os.remove(s_filename)
431 else:
432 print "cpp: " + s_filename
433
12aad6f0 434 return 0
a49946ac
JS
435
436if __name__ == "__main__":
12aad6f0 437 sys.exit(main())
This page took 0.148312 seconds and 5 git commands to generate.