diff options
Diffstat (limited to 'scripts/IStatsMerge.py')
-rwxr-xr-x | scripts/IStatsMerge.py | 175 |
1 files changed, 175 insertions, 0 deletions
diff --git a/scripts/IStatsMerge.py b/scripts/IStatsMerge.py new file mode 100755 index 00000000..1bd61c3e --- /dev/null +++ b/scripts/IStatsMerge.py @@ -0,0 +1,175 @@ +#!/usr/bin/python + +from __future__ import division + +import sys, os + +class MergeError(Exception): + pass + +def checkAssemblies(directories): + def read(d): + try: + return open(os.path.join(d,'assembly.ll')).read() + except: + raise MergeError("unable to open assembly for: %s"%(`d`,)) + + reference = read(directories[0]) + for d in directories[1:]: + if reference != read(d): + return False + return True + +def allEqual(l): + return not [i for i in l if i!=l[0]] + +def merge(inputs, output, outputDir): + inputs = [[None,iter(i)] for i in inputs] + + def getLine(elt): + la,i = elt + if la is None: + try: + ln = i.next() + except StopIteration: + ln = None + except: + raise MergeError("unexpected IO error") + return ln + else: + elt[0] = None + return la + def getLines(): + return map(getLine,inputs) + def putback(ln,elt): + assert elt[0] is None + elt[0] = ln + + events = None + + # read header (up to ob=) + while 1: + lns = getLines() + ln = lns[0] + if ln.startswith('ob='): + if [l for l in lns if not l.startswith('ob=')]: + raise MergeError("headers differ") + output.write('ob=%s\n'%(os.path.join(outputDir,'assembly.ll'),)) + break + else: + if ln.startswith('positions:'): + if ln!='positions: instr line\n': + raise MergeError("unexpected 'positions' directive") + elif ln.startswith('events:'): + events = ln[len('events: '):].strip().split(' ') + if not allEqual(lns): + raise MergeError("headers differ") + output.write(ln) + + if events is None: + raise MergeError('missing events directive') + boolTypes = set(['Icov','Iuncov']) + numEvents = len(events) + eventTypes = [e in boolTypes for e in events] + + def mergeStats(datas): + if not allEqual([d[:2] for d in datas]): + raise MergeError("instruction or line specifications differ") + elif [d for d in datas if len(d)!=numEvents+2]: + raise MergeError("statistics differ in event counts") + + result = [datas[0][0],datas[0][1]] + for i,ev in enumerate(events): + s = sum([d[i+2] for d in datas]) + if ev=='Icov': + result.append(max([d[i+2] for d in datas])) # true iff any are non-zero + elif ev=='Iuncov': + result.append(min([d[i+2] for d in datas])) # true iff any are non-zero + else: + result.append(s) + return result + + def readCalls(): + results = {} + for elt in inputs: + while 1: + ln = getLine(elt) + if ln is not None and (ln.startswith('cfl=') or ln.startswith('cfn=')): + if ln.startswith('cfl='): + cfl = ln + cfn = getLine(elt) + if not cfn.startswith('cfn='): + raise MergeError("unexpected cfl directive without function") + else: + cfl = None + cfn = ln + target = getLine(elt) + if not target.startswith('calls='): + raise MergeError("unexpected cfn directive with calls") + stat = map(int,getLine(elt).strip().split(' ')) + key = target + existing = results.get(target) + if existing is None: + results[key] = [cfl,cfn,target,stat] + else: + if existing[0]!=cfl or existing[1]!=cfn: + raise MergeError("multiple call descriptions for a single target") + existing[3] = mergeStats([existing[3],stat]) + else: + putback(ln, elt) + break + return results + + # read statistics + while 1: + lns = getLines() + ln = lns[0] + if ln is None: + if not allEqual(lns): + raise MergeError("unexpected end of input") + break + elif ln.startswith('fn') or ln.startswith('fl'): + if not allEqual(lns): + raise MergeError("files differ") + output.write(ln) + else: + # an actual statistic + data = [map(int,ln.strip().split(' ')) for ln in lns] + print >>output,' '.join(map(str,mergeStats(data))) + + # read any associated calls + for cfl,cfn,calls,stat in readCalls().values(): + if cfl is not None: + output.write(cfl) + output.write(cfn) + output.write(calls) + print >>output,' '.join(map(str,stat)) + +def main(args): + from optparse import OptionParser + op = OptionParser("usage: %prog [options] directories+ output") + opts,args = op.parse_args() + + output = args.pop() + directories = args + + if len(directories)<=1: + op.error("incorrect number of arguments") + + print 'Merging:',', '.join(directories) + print 'Into:',output + + if not checkAssemblies(directories): + raise MergeError("executables differ") + + if not os.path.exists(output): + os.mkdir(output) + + assembly = open(os.path.join(directories[0],'assembly.ll')).read() + open(os.path.join(output,'assembly.ll'),'w').write(assembly) + + inputs = [open(os.path.join(d,'run.istats')) for d in directories] + merge(inputs, open(os.path.join(output,'run.istats'),'w'), output) + +if __name__=='__main__': + main(sys.argv) |