X Tutup
Skip to content

Commit 20cd649

Browse files
tadeuzagallofacebook-github-bot-7
authored andcommitted
Automatically save and convert JavaScript profile to chrome format
Summary: @​public Migrate scripts to open source and add new route on the packager to directly convert profiler outputs to a devtools compatible format. Reviewed By: @jspahrsummers Differential Revision: D2425740
1 parent 360d04e commit 20cd649

File tree

8 files changed

+936
-42
lines changed

8 files changed

+936
-42
lines changed

JSCLegacyProfiler/json2trace

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
#!/usr/bin/env python
2+
from __future__ import absolute_import
3+
from __future__ import division
4+
from __future__ import print_function
5+
from __future__ import unicode_literals
6+
7+
import argparse
8+
import json
9+
import smap
10+
import trace_data
11+
import urllib
12+
13+
SECONDS_TO_NANOSECONDS = (1000*1000)
14+
SAMPLE_DELTA_IN_SECONDS = 0.0001
15+
16+
class Marker(object):
17+
def __init__(self, _name, _timestamp, _depth, _is_end, _ident, url, line, col):
18+
self.name = _name
19+
self.timestamp = _timestamp
20+
self.depth = _depth
21+
self.is_end = _is_end
22+
self.ident = _ident
23+
self.url = url
24+
self.line = line
25+
self.col = col
26+
27+
# sort markers making sure they are ordered by timestamp then depth of function call
28+
# and finally that markers of the same ident are sorted in the order begin then end
29+
def __cmp__(self, other):
30+
if self.timestamp < other.timestamp:
31+
return -1
32+
if self.timestamp > other.timestamp:
33+
return 1
34+
if self.depth < other.depth:
35+
return -1
36+
if self.depth > other.depth:
37+
return 1
38+
if self.ident == other.ident:
39+
if self.is_end:
40+
return 1
41+
return 0
42+
43+
# calculate marker name based on combination of function name and location
44+
def _calcname(entry):
45+
funcname = ""
46+
if "functionName" in entry:
47+
funcname = funcname + entry["functionName"]
48+
return funcname
49+
50+
def _calcurl(mapcache, entry, map_file):
51+
if entry.url not in mapcache:
52+
map_url = entry.url.replace('.bundle', '.map')
53+
54+
if map_url != entry.url:
55+
if map_file:
56+
print('Loading sourcemap from:' + map_file)
57+
map_url = map_file
58+
59+
try:
60+
url_file = urllib.urlopen(map_url)
61+
if url_file != None:
62+
entries = smap.parse(url_file)
63+
mapcache[entry.url] = entries
64+
except Exception, e:
65+
mapcache[entry.url] = []
66+
67+
if entry.url in mapcache:
68+
source_entry = smap.find(mapcache[entry.url], entry.line, entry.col)
69+
if source_entry:
70+
entry.url = 'file://' + source_entry.src
71+
entry.line = source_entry.src_line
72+
entry.col = source_entry.src_col
73+
74+
def _compute_markers(markers, call_point, depth):
75+
name = _calcname(call_point)
76+
ident = len(markers)
77+
url = ""
78+
lineNumber = -1
79+
columnNumber = -1
80+
if "url" in call_point:
81+
url = call_point["url"]
82+
if "lineNumber" in call_point:
83+
lineNumber = call_point["lineNumber"]
84+
if "columnNumber" in call_point:
85+
columnNumber = call_point["columnNumber"]
86+
87+
for call in call_point["calls"]:
88+
markers.append(Marker(name, call["startTime"], depth, 0, ident, url, lineNumber, columnNumber))
89+
markers.append(Marker(name, call["startTime"] + call["totalTime"], depth, 1, ident, url, lineNumber, columnNumber))
90+
ident = ident + 2
91+
if "children" in call_point:
92+
for child in call_point["children"]:
93+
_compute_markers(markers, child, depth+1);
94+
95+
def _find_child(children, name):
96+
for child in children:
97+
if child['functionName'] == name:
98+
return child
99+
return None
100+
101+
def _add_entry_cpuprofiler_program(newtime, cpuprofiler):
102+
curnode = _find_child(cpuprofiler['head']['children'], '(program)')
103+
if cpuprofiler['lastTime'] != None:
104+
lastTime = cpuprofiler['lastTime']
105+
while lastTime < newtime:
106+
curnode['hitCount'] += 1
107+
cpuprofiler['samples'].append(curnode['callUID'])
108+
cpuprofiler['timestamps'].append(int(lastTime*SECONDS_TO_NANOSECONDS))
109+
lastTime += SAMPLE_DELTA_IN_SECONDS
110+
cpuprofiler['lastTime'] = lastTime
111+
else:
112+
cpuprofiler['lastTime'] = newtime
113+
114+
115+
def _add_entry_cpuprofiler(stack, newtime, cpuprofiler):
116+
index = len(stack) - 1
117+
marker = stack[index]
118+
119+
if marker.name not in cpuprofiler['markers']:
120+
cpuprofiler['markers'][marker.name] = cpuprofiler['id']
121+
cpuprofiler['callUID'] += 1
122+
callUID = cpuprofiler['markers'][marker.name]
123+
124+
curnode = cpuprofiler['head']
125+
index = 0
126+
while index < len(stack):
127+
newnode = _find_child(curnode['children'], stack[index].name)
128+
if newnode == None:
129+
newnode = {}
130+
newnode['callUID'] = callUID
131+
newnode['url'] = marker.url
132+
newnode['functionName'] = stack[index].name
133+
newnode['hitCount'] = 0
134+
newnode['lineNumber'] = marker.line
135+
newnode['columnNumber'] = marker.col
136+
newnode['scriptId'] = callUID
137+
newnode['positionTicks'] = []
138+
newnode['id'] = cpuprofiler['id']
139+
cpuprofiler['id'] += 1
140+
newnode['children'] = []
141+
curnode['children'].append(newnode)
142+
curnode['deoptReason'] = ''
143+
curnode = newnode
144+
index += 1
145+
146+
if cpuprofiler['lastTime'] == None:
147+
cpuprofiler['lastTime'] = newtime
148+
149+
if cpuprofiler['lastTime'] != None:
150+
lastTime = cpuprofiler['lastTime']
151+
while lastTime < newtime:
152+
curnode['hitCount'] += 1
153+
if len(curnode['positionTicks']) == 0:
154+
ticks = {}
155+
ticks['line'] = curnode['callUID']
156+
ticks['ticks'] = 0
157+
curnode['positionTicks'].append(ticks)
158+
curnode['positionTicks'][0]['ticks'] += 1
159+
cpuprofiler['samples'].append(curnode['callUID'])
160+
cpuprofiler['timestamps'].append(int(lastTime*1000*1000))
161+
lastTime += 0.0001
162+
cpuprofiler['lastTime'] = lastTime
163+
164+
def _create_default_cpuprofiler_node(name, _id, _uid):
165+
return {'functionName': name,
166+
'scriptId':'0',
167+
'url':'',
168+
'lineNumber':0,
169+
'columnNumber':0,
170+
'positionTicks':[],
171+
'id':_id,
172+
'callUID':_uid,
173+
'children': [],
174+
'hitCount': 0,
175+
'deoptReason':''}
176+
177+
def main():
178+
parser = argparse.ArgumentParser(description="Converts JSON profile format to fbsystrace text output")
179+
180+
parser.add_argument(
181+
"-o",
182+
dest = "output_file",
183+
default = None,
184+
help = "Output file for trace data")
185+
parser.add_argument(
186+
"-cpuprofiler",
187+
dest = "output_cpuprofiler",
188+
default = None,
189+
help = "Output file for cpuprofiler data")
190+
parser.add_argument(
191+
"-map",
192+
dest = "map_file",
193+
default = None,
194+
help = "Map file for symbolicating")
195+
parser.add_argument( "file", help = "JSON trace input_file")
196+
197+
args = parser.parse_args()
198+
199+
markers = []
200+
with open(args.file, "r") as trace_file:
201+
trace = json.load(trace_file)
202+
for root_entry in trace["rootNodes"]:
203+
_compute_markers(markers, root_entry, 0)
204+
205+
mapcache = {}
206+
for m in markers:
207+
_calcurl(mapcache, m, args.map_file)
208+
209+
sorted_markers = list(sorted(markers));
210+
211+
if args.output_cpuprofiler != None:
212+
cpuprofiler = {}
213+
cpuprofiler['startTime'] = None
214+
cpuprofiler['endTime'] = None
215+
cpuprofiler['lastTime'] = None
216+
cpuprofiler['id'] = 4
217+
cpuprofiler['callUID'] = 4
218+
cpuprofiler['samples'] = []
219+
cpuprofiler['timestamps'] = []
220+
cpuprofiler['markers'] = {}
221+
cpuprofiler['head'] = _create_default_cpuprofiler_node('(root)', 1, 1)
222+
cpuprofiler['head']['children'].append(_create_default_cpuprofiler_node('(root)', 2, 2))
223+
cpuprofiler['head']['children'].append(_create_default_cpuprofiler_node('(program)', 3, 3))
224+
marker_stack = []
225+
with open(args.output_cpuprofiler, 'w') as file_out:
226+
for marker in sorted_markers:
227+
if len(marker_stack):
228+
_add_entry_cpuprofiler(marker_stack, marker.timestamp, cpuprofiler)
229+
else:
230+
_add_entry_cpuprofiler_program(marker.timestamp, cpuprofiler)
231+
if marker.is_end:
232+
marker_stack.pop()
233+
else:
234+
marker_stack.append(marker)
235+
cpuprofiler['startTime'] = cpuprofiler['timestamps'][0] / 1000000.0
236+
cpuprofiler['endTime'] = cpuprofiler['timestamps'][len(cpuprofiler['timestamps']) - 1] / 1000000.0
237+
json.dump(cpuprofiler, file_out, sort_keys=False, indent=4, separators=(',', ': '))
238+
239+
240+
if args.output_file != None:
241+
with open(args.output_file,"w") as trace_file:
242+
for marker in sorted_markers:
243+
start_or_end = None
244+
if marker.is_end:
245+
start_or_end = "E"
246+
else:
247+
start_or_end = "B"
248+
#output with timestamp at high level of precision
249+
trace_file.write("json-0 [000] .... {0:.12f}: tracing_mark_write: {1}|0|{2}\n".format(
250+
marker.timestamp,
251+
start_or_end,
252+
marker.name))
253+
254+
main()

0 commit comments

Comments
 (0)
X Tutup