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 += "- "
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 += '
\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'
for c in codebufs: # display them in original load order
if rev.has_key(c):
if rev[c] == 1:
extra = ''
else:
extra = '\t(%d times)' % rev[c]
data += '- %s\t(%d bytes)%s
\n' % \
(self.symhtml(c, c.addr),
len(c.data),
extra)
data += '
\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 = ['']
named = 0
proxies = 0
for codebuf in codebufs:
if codebuf.data and codebuf.co_name:
named += 1
else:
if not codebuf.data:
proxies += 1
if not all:
continue
data.append('- %s:\t%s:\t%s:\t%s\t(%d bytes)
\n' % (
codebuf.co_filename, codebuf.co_name,
codebuf.get_next_instr(),
self.symhtml(codebuf, codebuf.addr),
len(codebuf.data)))
data.append('
\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 = ['']
for p in plist:
data.append("- 0x%x\n" % (p, p))
data.append('
')
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),
'']
f = open(tracefilename, 'rb')
f.seek(start)
prevname = None
for p in range(start, end, 4):
addr, = struct.unpack('L', f.read(4))
s = self.try_hard_to_name(addr)
if s == prevname:
continue
data.append("- 0x%x: %s\n" % (p,p,s))
prevname = s
data.append('
')
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)