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