import sys, re, cStringIO, os, dis, types, struct import xam, psyco from SimpleHTTPServer import SimpleHTTPRequestHandler, test # # Adapted from SimpleHTTPServer.py. # def show_vinfos(array, d, co=None, path=[]): text = "
    " for i in range(len(array)): vi = array[i] text += "
  1. " if hasattr(co, 'co_code') and path == []: j = i - xam.LOC_LOCALS_PLUS if 0 <= j < len(co.co_varnames): text += "(%s):\t" % co.co_varnames[j] #if name is not None: # text += "%s " % name if vi is None: text += "[NULL]" else: text += "[%x] %s" % (vi.addr, vi.gettext()) if d.has_key(vi.addr): text += " (already seen above)" else: d[vi.addr] = 1 if vi.array: text += show_vinfos(vi.array, d, co, path+[i]) text += '
  2. \n' text += '
\n' return text def summary_vinfos(array, d, path=[]): text = '' indent = ' ' * len(path) for i in range(len(array)): vi = array[i] text += indent if vi is None: text += "[NULL]\n" else: text += "%d. %s" % (i, vi.getsummarytext()) if d.has_key(vi.addr): text += " (already seen above)" else: d[vi.addr] = 1 text += '\n' if vi.array: text += summary_vinfos(vi.array, d, path+[i]) return text def find4(f, s4): result = [] while 1: base = f.tell() buffer = f.read(8192) if not buffer: return result p = -1 while 1: p = buffer.find(s4, p+1) if p<0: break if not (p&3): result.append(base + p) re_codebuf = re.compile(r'[/]0x([0-9A-Fa-f]+)$') re_proxy = re.compile(r'[/]proxy(\d+)$') re_summary = re.compile(r'[/]summary(\d+)$') re_trace = re.compile(r'[/]trace0x([0-9A-Fa-f]+)$') re_traces = re.compile(r'[/]traces0x([0-9A-Fa-f]+)$') re_trlist = re.compile(r'[/]trace0x([0-9A-Fa-f]+)[-]0x([0-9A-Fa-f]+)$') ##def cache_load(filename, cache={}): ## try: ## return cache[filename] ## except KeyError: ## data = {} ## try: ## f = execfile(filename, data) ## except: ## data = None ## cache[filename] = data ## return data def cache_load(filename, codename, cache={}): try: modulecode = cache[filename] except KeyError: try: f = open(filename, 'rU') source = f.read() f.close() modulecode = compile(source, filename, 'exec') except Exception, e: print repr(source) print '*** While loading %s:' % (filename,) import traceback traceback.print_exc() return None return recfindcode(modulecode, codename) def recfindcode(code, codename): if code.co_name == codename: return code else: for c in code.co_consts: if type(c) is type(code): result = recfindcode(c, codename) if result: return result return '' class CodeBufHTTPHandler(SimpleHTTPRequestHandler): def symhtml(self, sym, addr, inbuf=None, lineaddr=None): text = xam.symtext(sym, addr, inbuf, lineaddr) if isinstance(sym, xam.CodeBuf): if addr == sym.addr: name = '' else: name = '#0x%x' % addr text = "%s" % (sym.addr, name, text) if addr == lineaddr: text += "\ttraces" % addr return text def linehtml(self, line, addr): line = "%s" % (addr, line) if addr in self.trace_addr: line = "%s" % line i = self.trace_addr.index(addr) if i == 0 and self.trace_prev is not None: line += ("\t<<<<<" % self.trace_prev) if i == len(self.trace_addr)-1 and self.trace_next is not None: line += ("\t>>>>>" % self.trace_next) return line def proxyhtml(self, proxy): return "(snapshot %s:%s)\n" % ( codebufs.index(proxy), proxy.co_name, proxy.get_next_instr()) def htmlpage(self, title, data): return ('%s\n' % title + '

%s

\n' % title # + '
\n' + data + '
\n') def bufferpage(self, codebuf): rev = {} for o, c in codebuf.reverse_lookup: if c is not codebuf: rev[c] = rev.get(c,0) + 1 if rev: data = '

Other code buffers pointing to this one:

\n' else: data = '

No other code buffer points to this one.

\n' data += '
\n' data += '
%s
\n' % codebuf.disassemble(self.symhtml, self.linehtml, self.proxyhtml) data += "
Back to the list of code objects\n" if codebuf.co_name: data = '

Code object %s from file %s, at position %s

%s' % ( codebuf.co_name, codebuf.co_filename, codebuf.get_next_instr(), data) return data def try_hard_to_name(self, addr): def result(codebuf): return '%s:%s:%s' % (codebuf.co_filename, codebuf.co_name, codebuf.get_next_instr()) codebuf = xam.codeat(addr) if codebuf is not None: codemap = codebuf.codemap proxylist = [] for lineaddr in range(addr, codebuf.addr-1, -1): if codemap.has_key(lineaddr): for proxy in codemap[lineaddr]: if proxy.co_name: return result(proxy) if codebuf.co_name: return result(codebuf) else: return '?' def send_head(self): global codebufs # CT self.trace_prev = None self.trace_next = None self.trace_addr = () if self.path == '/' or self.path == '/all': all = self.path == '/all' if all: title = 'List of ALL code objects' else: title = 'List of all named code objects' data = ['\n') data.append('
%d named buffers; ' % named + '%d buffers in total, ' % len(codebufs) + 'including %d proxies' % proxies) data = ''.join(data) return self.donepage(title, data) match = re_codebuf.match(self.path) if match: addr = long(match.group(1), 16) codebuf = xam.codeat(addr) if not codebuf: self.send_error(404, "No code buffer at this address") return None if codebuf.addr != addr: self.trace_addr = [addr] title = '%s code buffer at 0x%x' % (codebuf.mode.capitalize(), codebuf.addr) data = self.bufferpage(codebuf) return self.donepage(title, data) match = re_trace.match(self.path) if match: tracepos = int(match.group(1), 16) f = open(tracefilename, 'rb') try: def traceat(p, f=f): f.seek(p) data = f.read(4) if len(data) == 4: addr, = struct.unpack('L', data) return addr else: raise IOError addr = traceat(tracepos) codebuf = xam.codeat(addr) if not codebuf: self.send_error(404, "No code buffer at 0x%x" % addr) return None start = codebuf.addr end = start + len(codebuf.data) while tracepos > 0: addr1 = traceat(tracepos-4) if not (start <= addr1 < addr): break tracepos -= 4 addr = addr1 self.trace_prev = tracepos-4 self.trace_addr = [] while 1: self.trace_addr.append(addr) addr1 = traceat(tracepos+4) if not (addr < addr1 < end): break tracepos += 4 addr = addr1 self.trace_next = tracepos+4 finally: f.close() title = '%s code buffer at 0x%x' % (codebuf.mode.capitalize(), codebuf.addr) data = self.bufferpage(codebuf) return self.donepage(title, data) match = re_traces.match(self.path) if match: traceaddr = long(match.group(1), 16) f = open(tracefilename, 'rb') plist = find4(f, struct.pack('L', traceaddr)) f.close() title = 'Traces through 0x%x' % traceaddr data = ['') data = ''.join(data) return self.donepage(title, data) match = re_trlist.match(self.path) if match: start = int(match.group(1), 16) end = int(match.group(2), 16) title = 'Traces timed 0x%x to 0x%x' % (start, end) data = ["

<<<<

\n" % (start-(end-start), start), '') data.append( "

>>>>

\n" % (end, end+(end-start))) f.close() data = ''.join(data) return self.donepage(title, data) match = re_proxy.match(self.path) if match: title = 'Snapshot' n = int(match.group(1)) proxy = codebufs[n] for n1 in xrange(n-1, -1, -1): pprev = codebufs[n1] if (pprev.nextinstr == proxy.nextinstr and pprev.co_name == proxy.co_name and pprev.co_filename == proxy.co_filename): pprev = n1 break else: pprev = None for n1 in xrange(n+1, len(codebufs)): pnext = codebufs[n1] if (pnext.nextinstr == proxy.nextinstr and pnext.co_name == proxy.co_name and pnext.co_filename == proxy.co_filename): pnext = n1 break else: pnext = None filename = os.path.join(DIRECTORY, proxy.co_filename) co = cache_load(filename, proxy.co_name) data = '

PsycoObject structure at this point:' data += ' ' * 20 data += '[' data += '  summary  ' % n if pprev is not None or pnext is not None: if pprev is not None: data += '  <<< previous  ' % pprev if pnext is not None: data += '  next >>>  ' % pnext data += ']' data += '

\n' data += show_vinfos(proxy.vlocals, {}, co) data += '

Disassembly of %s:%s:%s:

\n' % ( proxy.co_filename, proxy.co_name, proxy.get_next_instr()) if co is None: #moduledata is None: txt = "(exception while loading the file '%s')\n" % ( filename) else: if not hasattr(co, 'co_code'): txt = "(no function object '%s' in file '%s')\n" % ( proxy.co_name, filename) else: txt = cStringIO.StringIO() oldstdout = sys.stdout try: sys.stdout = txt dis.disassemble(co, proxy.get_next_instr()) finally: sys.stdout = oldstdout txt = txt.getvalue() data += '
%s
\n' % txt data += "
Back\n" % proxy.addr return self.donepage(title, data) match = re_summary.match(self.path) if match: n = int(match.group(1)) proxy = codebufs[n] data = summary_vinfos(proxy.vlocals, {}) f = cStringIO.StringIO(data) self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() return f if self.path == '/checkall': for codebuf in codebufs: codebuf.cache_text f = cStringIO.StringIO('done') self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() return f ## CT: simple reload feature if self.path == "/reload": codebufs = xam.readdump(FILENAME) self.path = "/all" return self.send_head() self.send_error(404, "Invalid path") return None def donepage(self, title, data): f = cStringIO.StringIO(self.htmlpage(title, data)) self.send_response(200) self.send_header("Content-type", "text/html") self.end_headers() return f if __name__ == '__main__': if len(sys.argv) <= 1: print "Usage: python httpxam.py " print " psyco.dump and any .py files containing code objects" print " are loaded from the ." sys.exit(1) DIRECTORY = sys.argv[1] del sys.argv[1] filename = os.path.join(DIRECTORY, 'psyco.dump') if not os.path.isfile(filename) and os.path.isfile(DIRECTORY): filename = DIRECTORY DIRECTORY = os.path.dirname(DIRECTORY) tracefilename = os.path.join(DIRECTORY, 'psyco.trace') codebufs = xam.readdump(filename) FILENAME = filename # CT hack test(CodeBufHTTPHandler)