1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4""" 5Machinery for generating tracing-related intermediate files. 6""" 7 8__author__ = "Lluís Vilanova <vilanova@ac.upc.edu>" 9__copyright__ = "Copyright 2012, Lluís Vilanova <vilanova@ac.upc.edu>" 10__license__ = "GPL version 2 or (at your option) any later version" 11 12__maintainer__ = "Stefan Hajnoczi" 13__email__ = "stefanha@linux.vnet.ibm.com" 14 15 16import re 17import sys 18 19import tracetool.format 20import tracetool.backend 21 22 23def error_write(*lines): 24 """Write a set of error lines.""" 25 sys.stderr.writelines("\n".join(lines) + "\n") 26 27def error(*lines): 28 """Write a set of error lines and exit.""" 29 error_write(*lines) 30 sys.exit(1) 31 32 33def out(*lines, **kwargs): 34 """Write a set of output lines. 35 36 You can use kwargs as a shorthand for mapping variables when formating all 37 the strings in lines. 38 """ 39 lines = [ l % kwargs for l in lines ] 40 sys.stdout.writelines("\n".join(lines) + "\n") 41 42 43class Arguments: 44 """Event arguments description.""" 45 46 def __init__(self, args): 47 """ 48 Parameters 49 ---------- 50 args : 51 List of (type, name) tuples. 52 """ 53 self._args = args 54 55 @staticmethod 56 def build(arg_str): 57 """Build and Arguments instance from an argument string. 58 59 Parameters 60 ---------- 61 arg_str : str 62 String describing the event arguments. 63 """ 64 res = [] 65 for arg in arg_str.split(","): 66 arg = arg.strip() 67 if arg == 'void': 68 continue 69 70 if '*' in arg: 71 arg_type, identifier = arg.rsplit('*', 1) 72 arg_type += '*' 73 identifier = identifier.strip() 74 else: 75 arg_type, identifier = arg.rsplit(None, 1) 76 77 res.append((arg_type, identifier)) 78 return Arguments(res) 79 80 def __iter__(self): 81 """Iterate over the (type, name) pairs.""" 82 return iter(self._args) 83 84 def __len__(self): 85 """Number of arguments.""" 86 return len(self._args) 87 88 def __str__(self): 89 """String suitable for declaring function arguments.""" 90 if len(self._args) == 0: 91 return "void" 92 else: 93 return ", ".join([ " ".join([t, n]) for t,n in self._args ]) 94 95 def __repr__(self): 96 """Evaluable string representation for this object.""" 97 return "Arguments(\"%s\")" % str(self) 98 99 def names(self): 100 """List of argument names.""" 101 return [ name for _, name in self._args ] 102 103 def types(self): 104 """List of argument types.""" 105 return [ type_ for type_, _ in self._args ] 106 107 108class Event(object): 109 """Event description. 110 111 Attributes 112 ---------- 113 name : str 114 The event name. 115 fmt : str 116 The event format string. 117 properties : set(str) 118 Properties of the event. 119 args : Arguments 120 The event arguments. 121 """ 122 123 _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?") 124 125 _VALID_PROPS = set(["disable"]) 126 127 def __init__(self, name, props, fmt, args): 128 """ 129 Parameters 130 ---------- 131 name : string 132 Event name. 133 props : list of str 134 Property names. 135 fmt : str 136 Event printing format. 137 args : Arguments 138 Event arguments. 139 """ 140 self.name = name 141 self.properties = props 142 self.fmt = fmt 143 self.args = args 144 145 unknown_props = set(self.properties) - self._VALID_PROPS 146 if len(unknown_props) > 0: 147 raise ValueError("Unknown properties: %s" % ", ".join(unknown_props)) 148 149 @staticmethod 150 def build(line_str): 151 """Build an Event instance from a string. 152 153 Parameters 154 ---------- 155 line_str : str 156 Line describing the event. 157 """ 158 m = Event._CRE.match(line_str) 159 assert m is not None 160 groups = m.groupdict('') 161 162 name = groups["name"] 163 props = groups["props"].split() 164 fmt = groups["fmt"] 165 args = Arguments.build(groups["args"]) 166 167 return Event(name, props, fmt, args) 168 169 def __repr__(self): 170 """Evaluable string representation for this object.""" 171 return "Event('%s %s(%s) %s')" % (" ".join(self.properties), 172 self.name, 173 self.args, 174 self.fmt) 175 176def _read_events(fobj): 177 res = [] 178 for line in fobj: 179 if not line.strip(): 180 continue 181 if line.lstrip().startswith('#'): 182 continue 183 res.append(Event.build(line)) 184 return res 185 186 187class TracetoolError (Exception): 188 """Exception for calls to generate.""" 189 pass 190 191 192def try_import(mod_name, attr_name = None, attr_default = None): 193 """Try to import a module and get an attribute from it. 194 195 Parameters 196 ---------- 197 mod_name : str 198 Module name. 199 attr_name : str, optional 200 Name of an attribute in the module. 201 attr_default : optional 202 Default value if the attribute does not exist in the module. 203 204 Returns 205 ------- 206 A pair indicating whether the module could be imported and the module or 207 object or attribute value. 208 """ 209 try: 210 module = __import__(mod_name, globals(), locals(), ["__package__"]) 211 if attr_name is None: 212 return True, module 213 return True, getattr(module, str(attr_name), attr_default) 214 except ImportError: 215 return False, None 216 217 218def generate(fevents, format, backend, 219 binary = None, probe_prefix = None): 220 """Generate the output for the given (format, backend) pair. 221 222 Parameters 223 ---------- 224 fevents : file 225 Event description file. 226 format : str 227 Output format name. 228 backend : str 229 Output backend name. 230 binary : str or None 231 See tracetool.backend.dtrace.BINARY. 232 probe_prefix : str or None 233 See tracetool.backend.dtrace.PROBEPREFIX. 234 """ 235 # fix strange python error (UnboundLocalError tracetool) 236 import tracetool 237 238 format = str(format) 239 if len(format) is 0: 240 raise TracetoolError("format not set") 241 mformat = format.replace("-", "_") 242 if not tracetool.format.exists(mformat): 243 raise TracetoolError("unknown format: %s" % format) 244 245 backend = str(backend) 246 if len(backend) is 0: 247 raise TracetoolError("backend not set") 248 mbackend = backend.replace("-", "_") 249 if not tracetool.backend.exists(mbackend): 250 raise TracetoolError("unknown backend: %s" % backend) 251 252 if not tracetool.backend.compatible(mbackend, mformat): 253 raise TracetoolError("backend '%s' not compatible with format '%s'" % 254 (backend, format)) 255 256 import tracetool.backend.dtrace 257 tracetool.backend.dtrace.BINARY = binary 258 tracetool.backend.dtrace.PROBEPREFIX = probe_prefix 259 260 events = _read_events(fevents) 261 262 if backend == "nop": 263 ( e.properies.add("disable") for e in events ) 264 265 tracetool.format.generate_begin(mformat, events) 266 tracetool.backend.generate("nop", format, 267 [ e 268 for e in events 269 if "disable" in e.properties ]) 270 tracetool.backend.generate(backend, format, 271 [ e 272 for e in events 273 if "disable" not in e.properties ]) 274 tracetool.format.generate_end(mformat, events) 275