|
1 #!/usr/bin/env python |
|
2 # |
|
3 # This script takes b2g process profiles and merged them into a single profile. |
|
4 # The meta data is taken from the first profile. The startTime for each profile |
|
5 # is used to syncronized the samples. Each thread is moved into the merged |
|
6 # profile. |
|
7 # |
|
8 import json |
|
9 import re |
|
10 import sys |
|
11 |
|
12 def MergeProfiles(files): |
|
13 threads = [] |
|
14 fileData = [] |
|
15 symTable = dict() |
|
16 meta = None |
|
17 libs = None |
|
18 minStartTime = None |
|
19 |
|
20 for fname in files: |
|
21 match = re.match('profile_([0-9]+)_(.+)\.sym', fname) |
|
22 if match is None: |
|
23 raise Exception("Filename '" + fname + "' doesn't match expected pattern") |
|
24 pid = match.groups(0)[0] |
|
25 pname = match.groups(0)[1] |
|
26 |
|
27 fp = open(fname, "r") |
|
28 fileData = json.load(fp) |
|
29 fp.close() |
|
30 |
|
31 if meta is None: |
|
32 meta = fileData['profileJSON']['meta'].copy() |
|
33 libs = fileData['profileJSON']['libs'] |
|
34 minStartTime = meta['startTime'] |
|
35 else: |
|
36 minStartTime = min(minStartTime, fileData['profileJSON']['meta']['startTime']) |
|
37 meta['startTime'] = minStartTime |
|
38 |
|
39 for thread in fileData['profileJSON']['threads']: |
|
40 thread['name'] = thread['name'] + " (" + pname + ":" + pid + ")" |
|
41 threads.append(thread) |
|
42 |
|
43 # Note that pid + sym, pid + location could be ambigious |
|
44 # if we had pid=11 sym=1 && pid=1 sym=11. |
|
45 pidStr = pid + ":" |
|
46 |
|
47 thread['startTime'] = fileData['profileJSON']['meta']['startTime'] |
|
48 samples = thread['samples'] |
|
49 for sample in thread['samples']: |
|
50 for frame in sample['frames']: |
|
51 if "location" in frame and frame['location'][0:2] == '0x': |
|
52 oldLoc = frame['location'] |
|
53 newLoc = pidStr + oldLoc |
|
54 frame['location'] = newLoc |
|
55 # Default to the unprefixed symbol if no translation is available |
|
56 symTable[newLoc] = oldLoc |
|
57 |
|
58 filesyms = fileData['symbolicationTable'] |
|
59 for sym in filesyms.keys(): |
|
60 symTable[pidStr + sym] = filesyms[sym] |
|
61 |
|
62 # For each thread, make the time offsets line up based on the |
|
63 # earliest start |
|
64 for thread in threads: |
|
65 delta = thread['startTime'] - minStartTime |
|
66 for sample in thread['samples']: |
|
67 if "time" in sample: |
|
68 sample['time'] += delta |
|
69 |
|
70 result = dict() |
|
71 result['profileJSON'] = dict() |
|
72 result['profileJSON']['meta'] = meta |
|
73 result['profileJSON']['libs'] = libs |
|
74 result['profileJSON']['threads'] = threads |
|
75 result['symbolicationTable'] = symTable |
|
76 result['format'] = "profileJSONWithSymbolicationTable,1" |
|
77 |
|
78 json.dump(result, sys.stdout) |
|
79 |
|
80 |
|
81 if len(sys.argv) > 1: |
|
82 MergeProfiles(sys.argv[1:]) |
|
83 sys.exit(0) |
|
84 |
|
85 print "Usage: merge-profile.py profile_<pid1>_<pname1>.sym profile_<pid2>_<pname2>.sym > merged.sym" |
|
86 |
|
87 |
|
88 |