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-2014, 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 def copy(self): 56 """Create a new copy.""" 57 return Arguments(list(self._args)) 58 59 @staticmethod 60 def build(arg_str): 61 """Build and Arguments instance from an argument string. 62 63 Parameters 64 ---------- 65 arg_str : str 66 String describing the event arguments. 67 """ 68 res = [] 69 for arg in arg_str.split(","): 70 arg = arg.strip() 71 if arg == 'void': 72 continue 73 74 if '*' in arg: 75 arg_type, identifier = arg.rsplit('*', 1) 76 arg_type += '*' 77 identifier = identifier.strip() 78 else: 79 arg_type, identifier = arg.rsplit(None, 1) 80 81 res.append((arg_type, identifier)) 82 return Arguments(res) 83 84 def __iter__(self): 85 """Iterate over the (type, name) pairs.""" 86 return iter(self._args) 87 88 def __len__(self): 89 """Number of arguments.""" 90 return len(self._args) 91 92 def __str__(self): 93 """String suitable for declaring function arguments.""" 94 if len(self._args) == 0: 95 return "void" 96 else: 97 return ", ".join([ " ".join([t, n]) for t,n in self._args ]) 98 99 def __repr__(self): 100 """Evaluable string representation for this object.""" 101 return "Arguments(\"%s\")" % str(self) 102 103 def names(self): 104 """List of argument names.""" 105 return [ name for _, name in self._args ] 106 107 def types(self): 108 """List of argument types.""" 109 return [ type_ for type_, _ in self._args ] 110 111 112class Event(object): 113 """Event description. 114 115 Attributes 116 ---------- 117 name : str 118 The event name. 119 fmt : str 120 The event format string. 121 properties : set(str) 122 Properties of the event. 123 args : Arguments 124 The event arguments. 125 """ 126 127 _CRE = re.compile("((?P<props>.*)\s+)?(?P<name>[^(\s]+)\((?P<args>[^)]*)\)\s*(?P<fmt>\".*)?") 128 129 _VALID_PROPS = set(["disable"]) 130 131 def __init__(self, name, props, fmt, args): 132 """ 133 Parameters 134 ---------- 135 name : string 136 Event name. 137 props : list of str 138 Property names. 139 fmt : str 140 Event printing format. 141 args : Arguments 142 Event arguments. 143 """ 144 self.name = name 145 self.properties = props 146 self.fmt = fmt 147 self.args = args 148 149 unknown_props = set(self.properties) - self._VALID_PROPS 150 if len(unknown_props) > 0: 151 raise ValueError("Unknown properties: %s" 152 % ", ".join(unknown_props)) 153 154 def copy(self): 155 """Create a new copy.""" 156 return Event(self.name, list(self.properties), self.fmt, 157 self.args.copy(), self) 158 159 @staticmethod 160 def build(line_str): 161 """Build an Event instance from a string. 162 163 Parameters 164 ---------- 165 line_str : str 166 Line describing the event. 167 """ 168 m = Event._CRE.match(line_str) 169 assert m is not None 170 groups = m.groupdict('') 171 172 name = groups["name"] 173 props = groups["props"].split() 174 fmt = groups["fmt"] 175 args = Arguments.build(groups["args"]) 176 177 return Event(name, props, fmt, args) 178 179 def __repr__(self): 180 """Evaluable string representation for this object.""" 181 return "Event('%s %s(%s) %s')" % (" ".join(self.properties), 182 self.name, 183 self.args, 184 self.fmt) 185 186 QEMU_TRACE = "trace_%(name)s" 187 188 def api(self, fmt=None): 189 if fmt is None: 190 fmt = Event.QEMU_TRACE 191 return fmt % {"name": self.name} 192 193 194def _read_events(fobj): 195 res = [] 196 for line in fobj: 197 if not line.strip(): 198 continue 199 if line.lstrip().startswith('#'): 200 continue 201 res.append(Event.build(line)) 202 return res 203 204 205class TracetoolError (Exception): 206 """Exception for calls to generate.""" 207 pass 208 209 210def try_import(mod_name, attr_name=None, attr_default=None): 211 """Try to import a module and get an attribute from it. 212 213 Parameters 214 ---------- 215 mod_name : str 216 Module name. 217 attr_name : str, optional 218 Name of an attribute in the module. 219 attr_default : optional 220 Default value if the attribute does not exist in the module. 221 222 Returns 223 ------- 224 A pair indicating whether the module could be imported and the module or 225 object or attribute value. 226 """ 227 try: 228 module = __import__(mod_name, globals(), locals(), ["__package__"]) 229 if attr_name is None: 230 return True, module 231 return True, getattr(module, str(attr_name), attr_default) 232 except ImportError: 233 return False, None 234 235 236def generate(fevents, format, backends, 237 binary=None, probe_prefix=None): 238 """Generate the output for the given (format, backends) pair. 239 240 Parameters 241 ---------- 242 fevents : file 243 Event description file. 244 format : str 245 Output format name. 246 backends : list 247 Output backend names. 248 binary : str or None 249 See tracetool.backend.dtrace.BINARY. 250 probe_prefix : str or None 251 See tracetool.backend.dtrace.PROBEPREFIX. 252 """ 253 # fix strange python error (UnboundLocalError tracetool) 254 import tracetool 255 256 format = str(format) 257 if len(format) is 0: 258 raise TracetoolError("format not set") 259 if not tracetool.format.exists(format): 260 raise TracetoolError("unknown format: %s" % format) 261 262 if len(backends) is 0: 263 raise TracetoolError("no backends specified") 264 for backend in backends: 265 if not tracetool.backend.exists(backend): 266 raise TracetoolError("unknown backend: %s" % backend) 267 backend = tracetool.backend.Wrapper(backends, format) 268 269 import tracetool.backend.dtrace 270 tracetool.backend.dtrace.BINARY = binary 271 tracetool.backend.dtrace.PROBEPREFIX = probe_prefix 272 273 events = _read_events(fevents) 274 275 tracetool.format.generate(events, format, backend) 276