added strace support

This commit is contained in:
George Hotz 2014-07-22 21:18:44 -07:00
parent cf0aa670f6
commit 3a04cb16a9
13 changed files with 1733 additions and 147 deletions

View File

@ -38,7 +38,7 @@ if __name__ == '__main__':
else:
print "**** running "+program.program
if is_qira_running or os.fork() == 0: # cute?
os.execvp(program.qirabinary, [program.qirabinary, "-D", "/dev/null", "-d", "in_asm",
os.execvp(program.qirabinary, [program.qirabinary, "-strace", "-D", "/dev/null", "-d", "in_asm",
"-singlestep", program.program]+program.args)
if not is_qira_running:

View File

@ -1,141 +0,0 @@
#!/usr/bin/env python
import qira_log
import qira_trace
import socket
import threading
import time
import sys
import os
import fcntl
import signal
import argparse
program = None
run_id = 0
@socketio.on('getclnum', namespace='/qira')
def getclnum(forknum, clnum, types, limit):
if forknum not in program.traces:
return
trace = program.traces[forknum]
if clnum == None or types == None or limit == None:
return
ret = []
for t in types:
key = (clnum, t)
for c in trace.pydb_clnum[key]:
c = c.copy()
c['address'] = ghex(c['address'])
c['data'] = ghex(c['data'])
ret.append(c)
if len(ret) >= limit:
break
if len(ret) >= limit:
break
emit('clnum', ret)
@socketio.on('getchanges', namespace='/qira')
def getchanges(forknum, address, typ):
if address == None or typ == None:
return
if forknum != -1 and forknum not in program.traces:
return
address = int(address)
if forknum == -1:
ret = {}
for forknum in program.traces:
ret[forknum] = program.traces[forknum].pydb_addr[(address, typ)]
emit('changes', {'type': typ, 'clnums': ret})
else:
emit('changes', {'type': typ, 'clnums': {forknum: program.traces[forknum].pydb_addr[(address, typ)]}})
@socketio.on('getinstructions', namespace='/qira')
def getinstructions(forknum, clstart, clend):
if forknum not in program.traces:
return
trace = program.traces[forknum]
if clstart == None or clend == None:
return
ret = []
pydb_clnum = trace.pydb_clnum
for i in range(clstart, clend):
key = (i, 'I')
if key in pydb_clnum:
rret = pydb_clnum[key][0]
if rret['address'] in program.instructions:
rret['instruction'] = program.instructions[rret['address']]
ret.append(rret)
emit('instructions', ret)
@socketio.on('getmemory', namespace='/qira')
def getmemory(forknum, clnum, address, ln):
if forknum not in program.traces:
return
trace = program.traces[forknum]
if clnum == None or address == None or ln == None:
return
address = int(address)
dat = trace.mem.fetch(clnum, address, ln)
ret = {'address': address, 'len': ln, 'dat': dat}
emit('memory', ret)
@socketio.on('getregisters', namespace='/qira')
def getregisters(forknum, clnum):
if forknum not in program.traces:
return
trace = program.traces[forknum]
#print "getregisters",clnum
if clnum == None:
return
# register names shouldn't be here
# though i'm not really sure where a better place is, qemu has this information
ret = []
REGS = program.tregs[0]
REGSIZE = program.tregs[1]
for i in range(0, len(REGS)):
if i*REGSIZE in trace.regs.daddr:
rret = {"name": REGS[i], "address": i*REGSIZE, "value": ghex(trace.regs.daddr[i*REGSIZE].fetch(clnum)), "size": REGSIZE, "regactions": ""}
# this +1 is an ugly hack
if (clnum+1) in trace.pydb_addr[(i*REGSIZE, 'R')]:
rret['regactions'] = "regread"
if (clnum+1) in trace.pydb_addr[(i*REGSIZE, 'W')]:
if "regread" == rret['regactions']:
rret['regactions'] = "regreadwrite"
else:
rret['regactions'] = "regwrite"
ret.append(rret)
emit('registers', ret)
def run_middleware():
global program
print "starting QIRA middleware"
# run loop run
# read in all the traces
while 1:
time.sleep(0.2)
did_update = False
for i in os.listdir("/tmp/qira_logs/"):
if "_" in i:
continue
i = int(i)
if i not in program.traces:
#print "C create trace",qiradb.new_trace("/tmp/qira_logs/"+str(i), i, program.tregs[1], len(program.tregs[0]))
qira_trace.Trace(program, i)
for tn in program.traces:
if program.traces[tn].poll():
did_update = True
if did_update:
# push to all connected websockets
socketio.emit('pmaps', program.pmaps, namespace='/qira')
# this must happen last
socketio.emit('maxclnum', program.get_maxclnum(), namespace='/qira')

View File

@ -53,7 +53,7 @@ def start_bindserver(program, myss, parent_id, start_cl, loop = False):
except:
pass
# fingerprint here
os.execvp(program.qirabinary, [program.qirabinary, "-D", "/dev/null", "-d", "in_asm",
os.execvp(program.qirabinary, [program.qirabinary, "-strace", "-D", "/dev/null", "-d", "in_asm",
"-qirachild", "%d %d %d" % (parent_id, start_cl, run_id), "-singlestep",
program.program]+program.args)

View File

@ -3,7 +3,7 @@ import qira_socat
import time
QIRA_PORT = 3002
LIMIT = 100
LIMIT = 1000
from flask import Flask, Response
from flask.ext.socketio import SocketIO, emit
@ -189,7 +189,30 @@ def getregisters(forknum, clnum):
emit('registers', ret)
@socketio.on('getstrace', namespace='/qira')
def get_strace(forknum):
try:
f = open("/tmp/qira_logs/"+str(int(forknum))+"_strace").read()
except:
return "no strace"
ret = []
for ff in f.split("\n"):
if ff == '':
continue
ff = ff.split(" ")
clnum = int(ff[0])
if clnum == 0:
# filter the boring syscalls
continue
pid = int(ff[1])
sc = " ".join(ff[2:])
ret.append({"clnum": clnum, "pid":pid, "sc": sc})
emit('strace', ret)
# ***** generic webserver stuff *****
@app.route('/', defaults={'path': 'index.html'})
@app.route('/<path:path>')

View File

@ -18,6 +18,7 @@ if [ ! -d qemu/qemu-latest ]; then
mv disas.c disas.c.bak
mv linux-user/qemu.h linux-user/qemu.h.bak
mv linux-user/main.c linux-user/main.c.bak
mv linux-user/strace.c linux-user/strace.c.bak
cd ../../
sudo apt-get build-dep qemu
@ -28,6 +29,7 @@ ln -sf ../../qemu_mods/tci.c tci.c
ln -sf ../../qemu_mods/disas.c disas.c
ln -sf ../../../qemu_mods/qemu.h linux-user/qemu.h
ln -sf ../../../qemu_mods/main.c linux-user/main.c
ln -sf ../../../qemu_mods/strace.c linux-user/strace.c
#./configure --target-list=i386-linux-user,arm-linux-user,x86_64-linux-user,sparc-linux-user,sparc32plus-linux-user --enable-tcg-interpreter --enable-debug-tcg --cpu=unknown
./configure --target-list=i386-linux-user,x86_64-linux-user,arm-linux-user --enable-tcg-interpreter --enable-debug-tcg --cpu=unknown
make -j32

1625
qemu_mods/strace.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -487,11 +487,17 @@ uint32_t GLOBAL_start_clnum = 1;
int GLOBAL_parent_id = -1, GLOBAL_id = -1;
FILE *GLOBAL_asm_file = NULL;
FILE *GLOBAL_strace_file = NULL;
// should be 0ed on startup
#define PENDING_CHANGES_MAX_ADDR 0x100
struct change GLOBAL_pending_changes[PENDING_CHANGES_MAX_ADDR/4];
uint32_t get_current_clnum(void);
uint32_t get_current_clnum(void) {
return GLOBAL_logstate->changelist_number;
}
void resize_change_buffer(size_t size) {
if(ftruncate(GLOBAL_qira_log_fd, size)) {
perror("ftruncate");
@ -508,6 +514,9 @@ void init_QIRA(CPUArchState *env, int id) {
GLOBAL_CPUArchState = env; // unused
char fn[PATH_MAX];
sprintf(fn, "/tmp/qira_logs/%d_strace", id);
GLOBAL_strace_file = fopen(fn, "w");
sprintf(fn, "/tmp/qira_logs/%d", id);
// unlink it first
@ -784,7 +793,6 @@ int GLOBAL_last_was_syscall = 0;
uint32_t GLOBAL_last_fork_change = -1;
target_long last_pc = 0;
void write_out_base(CPUArchState *env, int id);
void write_out_base(CPUArchState *env, int id) {
CPUState *cpu = ENV_GET_CPU(env);

BIN
tests/confound/confound Executable file

Binary file not shown.

View File

@ -55,5 +55,5 @@ window.onkeydown = function(e) {
};
// don't pull the window
window.onmousewheel = function() { return false; }
//window.onmousewheel = function() { return false; }

26
web/client/strace.js Normal file
View File

@ -0,0 +1,26 @@
stream = io.connect("http://localhost:3002/qira");
stream.on('strace', function(msg) {
p(msg);
$('#strace')[0].innerHTML = "";
UI.insert(UI.renderWithData(Template.strace, {strace: msg}), $('#strace')[0]);
});
Template.strace.ischange = function() {
var clnum = Session.get("clnum");
if (this.clnum == clnum) return 'highlight';
else return '';
}
Deps.autorun(function() {
var forknum = Session.get("forknum");
var maxclnum = Session.get("max_clnum");
stream.emit("getstrace", forknum);
});
Template.strace.events({
'click .change': function() {
Session.set('clnum', this.clnum);
},
});

View File

@ -177,3 +177,10 @@ body {
background: linear-gradient(to right, #666644, #EEEE44);
}
#strace {
font-family: monospace;
height: 150px;
overflow-y: scroll;
padding: 10px;
}

View File

@ -23,6 +23,9 @@
<div class="panelthing" id="hexeditor">
{{> memviewer}}
</div>
<div class="panelthing" id="strace">
{{> strace}}
</div>
</div>
</body>
@ -35,6 +38,15 @@
<input spellcheck="false" id="control_daddr" class="control datamemory" value="{{daddr}}" />
</template>
<template name="strace">
{{#each strace}}
<div class="syscall">
<div class="change {{ischange}}">{{clnum}}</div>
{{sc}}
</div>
{{/each}}
</template>
<template name="idump">
{{#each instructions}}
<div class="instruction">

View File

@ -14,7 +14,10 @@ UI.body.contentParts.push(UI.Component.extend({render: (function() {
}, "\n", Spacebars.include(self.lookupTemplate("datachanges")), "\n"), "\n", HTML.DIV({
"class": "panelthing",
id: "hexeditor"
}, "\n", Spacebars.include(self.lookupTemplate("memviewer")), "\n"), "\n") ];
}, "\n", Spacebars.include(self.lookupTemplate("memviewer")), "\n"), "\n", HTML.DIV({
"class": "panelthing",
id: "strace"
}, "\n", Spacebars.include(self.lookupTemplate("strace")), "\n"), "\n") ];
})}));
Meteor.startup(function () { if (! UI.body.INSTANTIATED) { UI.body.INSTANTIATED = true; UI.DomRange.insert(UI.render(UI.body).dom, document.body); } });
@ -52,6 +55,27 @@ Template.__define__("controls", (function() {
}) ];
}));
Template.__define__("strace", (function() {
var self = this;
var template = this;
return UI.Each(function() {
return Spacebars.call(self.lookup("strace"));
}, UI.block(function() {
var self = this;
return [ "\n ", HTML.DIV({
"class": "syscall"
}, "\n ", HTML.DIV({
"class": [ "change ", function() {
return Spacebars.mustache(self.lookup("ischange"));
} ]
}, function() {
return Spacebars.mustache(self.lookup("clnum"));
}), "\n ", function() {
return Spacebars.mustache(self.lookup("sc"));
}, "\n "), "\n" ];
}));
}));
Template.__define__("idump", (function() {
var self = this;
var template = this;