michael@0: #!/usr/bin/env python michael@0: # michael@0: # This script takes b2g process profiles and merged them into a single profile. michael@0: # The meta data is taken from the first profile. The startTime for each profile michael@0: # is used to syncronized the samples. Each thread is moved into the merged michael@0: # profile. michael@0: # michael@0: import json michael@0: import re michael@0: import sys michael@0: michael@0: def MergeProfiles(files): michael@0: threads = [] michael@0: fileData = [] michael@0: symTable = dict() michael@0: meta = None michael@0: libs = None michael@0: minStartTime = None michael@0: michael@0: for fname in files: michael@0: match = re.match('profile_([0-9]+)_(.+)\.sym', fname) michael@0: if match is None: michael@0: raise Exception("Filename '" + fname + "' doesn't match expected pattern") michael@0: pid = match.groups(0)[0] michael@0: pname = match.groups(0)[1] michael@0: michael@0: fp = open(fname, "r") michael@0: fileData = json.load(fp) michael@0: fp.close() michael@0: michael@0: if meta is None: michael@0: meta = fileData['profileJSON']['meta'].copy() michael@0: libs = fileData['profileJSON']['libs'] michael@0: minStartTime = meta['startTime'] michael@0: else: michael@0: minStartTime = min(minStartTime, fileData['profileJSON']['meta']['startTime']) michael@0: meta['startTime'] = minStartTime michael@0: michael@0: for thread in fileData['profileJSON']['threads']: michael@0: thread['name'] = thread['name'] + " (" + pname + ":" + pid + ")" michael@0: threads.append(thread) michael@0: michael@0: # Note that pid + sym, pid + location could be ambigious michael@0: # if we had pid=11 sym=1 && pid=1 sym=11. michael@0: pidStr = pid + ":" michael@0: michael@0: thread['startTime'] = fileData['profileJSON']['meta']['startTime'] michael@0: samples = thread['samples'] michael@0: for sample in thread['samples']: michael@0: for frame in sample['frames']: michael@0: if "location" in frame and frame['location'][0:2] == '0x': michael@0: oldLoc = frame['location'] michael@0: newLoc = pidStr + oldLoc michael@0: frame['location'] = newLoc michael@0: # Default to the unprefixed symbol if no translation is available michael@0: symTable[newLoc] = oldLoc michael@0: michael@0: filesyms = fileData['symbolicationTable'] michael@0: for sym in filesyms.keys(): michael@0: symTable[pidStr + sym] = filesyms[sym] michael@0: michael@0: # For each thread, make the time offsets line up based on the michael@0: # earliest start michael@0: for thread in threads: michael@0: delta = thread['startTime'] - minStartTime michael@0: for sample in thread['samples']: michael@0: if "time" in sample: michael@0: sample['time'] += delta michael@0: michael@0: result = dict() michael@0: result['profileJSON'] = dict() michael@0: result['profileJSON']['meta'] = meta michael@0: result['profileJSON']['libs'] = libs michael@0: result['profileJSON']['threads'] = threads michael@0: result['symbolicationTable'] = symTable michael@0: result['format'] = "profileJSONWithSymbolicationTable,1" michael@0: michael@0: json.dump(result, sys.stdout) michael@0: michael@0: michael@0: if len(sys.argv) > 1: michael@0: MergeProfiles(sys.argv[1:]) michael@0: sys.exit(0) michael@0: michael@0: print "Usage: merge-profile.py profile__.sym profile__.sym > merged.sym" michael@0: michael@0: michael@0: