1import sys 2import inspect 3from collections import OrderedDict 4 5 6class TracebackFancy: 7 def __init__(self, traceback): 8 self.t = traceback 9 10 def getFrame(self): 11 return FrameFancy(self.t.tb_frame) 12 13 def getLineNumber(self): 14 return self.t.tb_lineno if self.t is not None else None 15 16 def getNext(self): 17 return TracebackFancy(self.t.tb_next) 18 19 def __str__(self): 20 if self.t is None: 21 return "" 22 str_self = "%s @ %s" % (self.getFrame().getName(), self.getLineNumber()) 23 return str_self + "\n" + self.getNext().__str__() 24 25 26class ExceptionFancy: 27 def __init__(self, frame): 28 self.etraceback = frame.f_exc_traceback 29 self.etype = frame.exc_type 30 self.evalue = frame.f_exc_value 31 32 def __init__(self, tb, ty, va): 33 self.etraceback = tb 34 self.etype = ty 35 self.evalue = va 36 37 def getTraceback(self): 38 return TracebackFancy(self.etraceback) 39 40 def __nonzero__(self): 41 return ( 42 self.etraceback is not None 43 or self.etype is not None 44 or self.evalue is not None 45 ) 46 47 def getType(self): 48 return str(self.etype) 49 50 def getValue(self): 51 return self.evalue 52 53 54class CodeFancy: 55 def __init__(self, code): 56 self.c = code 57 58 def getArgCount(self): 59 return self.c.co_argcount if self.c is not None else 0 60 61 def getFilename(self): 62 return self.c.co_filename if self.c is not None else "" 63 64 def getVariables(self): 65 return self.c.co_varnames if self.c is not None else [] 66 67 def getName(self): 68 return self.c.co_name if self.c is not None else "" 69 70 def getFileName(self): 71 return self.c.co_filename if self.c is not None else "" 72 73 74class ArgsFancy: 75 def __init__(self, frame, arginfo): 76 self.f = frame 77 self.a = arginfo 78 79 def __str__(self): 80 args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs() 81 ret = "" 82 count = 0 83 size = len(args) 84 for arg in args: 85 ret = ret + ("%s = %s" % (arg, args[arg])) 86 count = count + 1 87 if count < size: 88 ret = ret + ", " 89 if varargs: 90 if size > 0: 91 ret = ret + " " 92 ret = ret + "varargs are " + str(varargs) 93 if kwargs: 94 if size > 0: 95 ret = ret + " " 96 ret = ret + "kwargs are " + str(kwargs) 97 return ret 98 99 def getNumArgs(wantVarargs=False, wantKWArgs=False): 100 args, varargs, keywords, values = self.a 101 size = len(args) 102 if varargs and wantVarargs: 103 size = size + len(self.getVarArgs()) 104 if keywords and wantKWArgs: 105 size = size + len(self.getKWArgs()) 106 return size 107 108 def getArgs(self): 109 args, _, _, values = self.a 110 argWValues = OrderedDict() 111 for arg in args: 112 argWValues[arg] = values[arg] 113 return argWValues 114 115 def getVarArgs(self): 116 _, vargs, _, _ = self.a 117 if vargs: 118 return self.f.f_locals[vargs] 119 return () 120 121 def getKWArgs(self): 122 _, _, kwargs, _ = self.a 123 if kwargs: 124 return self.f.f_locals[kwargs] 125 return {} 126 127 128class FrameFancy: 129 def __init__(self, frame): 130 self.f = frame 131 132 def getCaller(self): 133 return FrameFancy(self.f.f_back) 134 135 def getLineNumber(self): 136 return self.f.f_lineno if self.f is not None else 0 137 138 def getCodeInformation(self): 139 return CodeFancy(self.f.f_code) if self.f is not None else None 140 141 def getExceptionInfo(self): 142 return ExceptionFancy(self.f) if self.f is not None else None 143 144 def getName(self): 145 return self.getCodeInformation().getName() if self.f is not None else "" 146 147 def getFileName(self): 148 return self.getCodeInformation().getFileName() if self.f is not None else "" 149 150 def getLocals(self): 151 return self.f.f_locals if self.f is not None else {} 152 153 def getArgumentInfo(self): 154 return ( 155 ArgsFancy(self.f, inspect.getargvalues(self.f)) 156 if self.f is not None 157 else None 158 ) 159 160 161class TracerClass: 162 def callEvent(self, frame): 163 pass 164 165 def lineEvent(self, frame): 166 pass 167 168 def returnEvent(self, frame, retval): 169 pass 170 171 def exceptionEvent(self, frame, exception, value, traceback): 172 pass 173 174 def cCallEvent(self, frame, cfunct): 175 pass 176 177 def cReturnEvent(self, frame, cfunct): 178 pass 179 180 def cExceptionEvent(self, frame, cfunct): 181 pass 182 183 184tracer_impl = TracerClass() 185 186 187def the_tracer_entrypoint(frame, event, args): 188 if tracer_impl is None: 189 return None 190 if event == "call": 191 call_retval = tracer_impl.callEvent(FrameFancy(frame)) 192 if not call_retval: 193 return None 194 return the_tracer_entrypoint 195 elif event == "line": 196 line_retval = tracer_impl.lineEvent(FrameFancy(frame)) 197 if not line_retval: 198 return None 199 return the_tracer_entrypoint 200 elif event == "return": 201 tracer_impl.returnEvent(FrameFancy(frame), args) 202 elif event == "exception": 203 exty, exva, extb = args 204 exception_retval = tracer_impl.exceptionEvent( 205 FrameFancy(frame), ExceptionFancy(extb, exty, exva) 206 ) 207 if not exception_retval: 208 return None 209 return the_tracer_entrypoint 210 elif event == "c_call": 211 tracer_impl.cCallEvent(FrameFancy(frame), args) 212 elif event == "c_return": 213 tracer_impl.cReturnEvent(FrameFancy(frame), args) 214 elif event == "c_exception": 215 tracer_impl.cExceptionEvent(FrameFancy(frame), args) 216 return None 217 218 219def enable(t=None): 220 global tracer_impl 221 if t: 222 tracer_impl = t 223 sys.settrace(the_tracer_entrypoint) 224 225 226def disable(): 227 sys.settrace(None) 228 229 230class LoggingTracer: 231 def callEvent(self, frame): 232 print( 233 "call " 234 + frame.getName() 235 + " from " 236 + frame.getCaller().getName() 237 + " @ " 238 + str(frame.getCaller().getLineNumber()) 239 + " args are " 240 + str(frame.getArgumentInfo()) 241 ) 242 243 def lineEvent(self, frame): 244 print( 245 "running " 246 + frame.getName() 247 + " @ " 248 + str(frame.getLineNumber()) 249 + " locals are " 250 + str(frame.getLocals()) 251 + " in " 252 + frame.getFileName() 253 ) 254 255 def returnEvent(self, frame, retval): 256 print( 257 "return from " 258 + frame.getName() 259 + " value is " 260 + str(retval) 261 + " locals are " 262 + str(frame.getLocals()) 263 ) 264 265 def exceptionEvent(self, frame, exception): 266 print( 267 "exception %s %s raised from %s @ %s" 268 % ( 269 exception.getType(), 270 str(exception.getValue()), 271 frame.getName(), 272 frame.getLineNumber(), 273 ) 274 ) 275 print("tb: " + str(exception.getTraceback())) 276 277 278# the same functionality as LoggingTracer, but with a little more 279# lldb-specific smarts 280 281 282class LLDBAwareTracer: 283 def callEvent(self, frame): 284 if frame.getName() == "<module>": 285 return 286 if frame.getName() == "run_one_line": 287 print( 288 "call run_one_line(%s)" 289 % (frame.getArgumentInfo().getArgs()["input_string"]) 290 ) 291 return 292 if "Python.framework" in frame.getFileName(): 293 print("call into Python at " + frame.getName()) 294 return 295 if ( 296 frame.getName() == "__init__" 297 and frame.getCaller().getName() == "run_one_line" 298 and frame.getCaller().getLineNumber() == 101 299 ): 300 return False 301 strout = "call " + frame.getName() 302 if frame.getCaller().getFileName() == "": 303 strout += " from LLDB - args are " 304 args = frame.getArgumentInfo().getArgs() 305 for arg in args: 306 if arg == "dict" or arg == "internal_dict": 307 continue 308 strout = strout + ("%s = %s " % (arg, args[arg])) 309 else: 310 strout += ( 311 " from " 312 + frame.getCaller().getName() 313 + " @ " 314 + str(frame.getCaller().getLineNumber()) 315 + " args are " 316 + str(frame.getArgumentInfo()) 317 ) 318 print(strout) 319 320 def lineEvent(self, frame): 321 if frame.getName() == "<module>": 322 return 323 if frame.getName() == "run_one_line": 324 print( 325 "running run_one_line(%s) @ %s" 326 % ( 327 frame.getArgumentInfo().getArgs()["input_string"], 328 frame.getLineNumber(), 329 ) 330 ) 331 return 332 if "Python.framework" in frame.getFileName(): 333 print( 334 "running into Python at " 335 + frame.getName() 336 + " @ " 337 + str(frame.getLineNumber()) 338 ) 339 return 340 strout = ( 341 "running " 342 + frame.getName() 343 + " @ " 344 + str(frame.getLineNumber()) 345 + " locals are " 346 ) 347 if frame.getCaller().getFileName() == "": 348 locals = frame.getLocals() 349 for local in locals: 350 if local == "dict" or local == "internal_dict": 351 continue 352 strout = strout + ("%s = %s " % (local, locals[local])) 353 else: 354 strout = strout + str(frame.getLocals()) 355 strout = strout + " in " + frame.getFileName() 356 print(strout) 357 358 def returnEvent(self, frame, retval): 359 if frame.getName() == "<module>": 360 return 361 if frame.getName() == "run_one_line": 362 print( 363 "return from run_one_line(%s) return value is %s" 364 % (frame.getArgumentInfo().getArgs()["input_string"], retval) 365 ) 366 return 367 if "Python.framework" in frame.getFileName(): 368 print( 369 "return from Python at " 370 + frame.getName() 371 + " return value is " 372 + str(retval) 373 ) 374 return 375 strout = ( 376 "return from " 377 + frame.getName() 378 + " return value is " 379 + str(retval) 380 + " locals are " 381 ) 382 if frame.getCaller().getFileName() == "": 383 locals = frame.getLocals() 384 for local in locals: 385 if local == "dict" or local == "internal_dict": 386 continue 387 strout = strout + ("%s = %s " % (local, locals[local])) 388 else: 389 strout = strout + str(frame.getLocals()) 390 strout = strout + " in " + frame.getFileName() 391 print(strout) 392 393 def exceptionEvent(self, frame, exception): 394 if frame.getName() == "<module>": 395 return 396 print( 397 "exception %s %s raised from %s @ %s" 398 % ( 399 exception.getType(), 400 str(exception.getValue()), 401 frame.getName(), 402 frame.getLineNumber(), 403 ) 404 ) 405 print("tb: " + str(exception.getTraceback())) 406 407 408def f(x, y=None): 409 if x > 0: 410 return 2 + f(x - 2) 411 return 35 412 413 414def g(x): 415 return 1.134 / x 416 417 418def print_keyword_args(**kwargs): 419 # kwargs is a dict of the keyword args passed to the function 420 for key, value in kwargs.items(): 421 print("%s = %s" % (key, value)) 422 423 424def total(initial=5, *numbers, **keywords): 425 count = initial 426 for number in numbers: 427 count += number 428 for key in keywords: 429 count += keywords[key] 430 return count 431 432 433if __name__ == "__main__": 434 enable(LoggingTracer()) 435 f(5) 436 f(5, 1) 437 print_keyword_args(first_name="John", last_name="Doe") 438 total(10, 1, 2, 3, vegetables=50, fruits=100) 439 try: 440 g(0) 441 except: 442 pass 443 disable() 444