Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(179)

Unified Diff: tools/telemetry/telemetry/core/platform/profiler/perf_vis/perf_vis.py

Issue 296623002: Integrate perfvis profiling mode, and temporary report generator. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: tools/telemetry/telemetry/core/platform/profiler/perf_vis/perf_vis.py
diff --git a/tools/telemetry/telemetry/core/platform/profiler/perf_vis/perf_vis.py b/tools/telemetry/telemetry/core/platform/profiler/perf_vis/perf_vis.py
new file mode 100755
index 0000000000000000000000000000000000000000..e060640b6e7092bc610709b6d943269c000a60aa
--- /dev/null
+++ b/tools/telemetry/telemetry/core/platform/profiler/perf_vis/perf_vis.py
@@ -0,0 +1,264 @@
+#!/usr/bin/python
+import sys
+import os
+import json
+from datetime import date
+
+from collections import deque
+from optparse import OptionParser
+
+parser = OptionParser()
+parser.add_option("-f", "--frames", dest="nframes", default=3600,
+ type="int", help="Number of frames in input")
+parser.add_option("-s", "--start", dest="start_ts", default=0.0,
+ type="float", help="Start timestamp")
+parser.add_option("-e", "--end", dest="end_ts", default=1000000000000.0,
+ type="float", help="End timestamp")
+parser.add_option("-m", "--meta", dest="meta", default=None,
+ type="string", help="Trace metadata")
+parser.add_option("-o", dest="output", default=None,
+ type="string", help="Output filename")
+parser.add_option("-c", "--cpu-freq", dest="cpu_freq", default=1574400000,
+ type="int", help="CPU cycles per second")
+
+def Process(options, args):
+ thread_names = {}
+
+ if options.meta:
+ with open(options.meta) as meta_file:
+ meta = json.load(meta_file)
+
+ options.nframes = meta.get('num_frames', 1)
+ options.start_ts = meta.get('start_ts', 0.0)
+ options.end_ts = meta.get('end_ts', 1000000000000.0)
+ thread_names = meta.get('tid', {})
+
+ class Symbol:
+ def __init__(self, name):
+ self.name = name
+
+ class CallTreeNode:
+ stack_id = 0
+ def __init__(self, name):
+ CallTreeNode.stack_id += 1
+ self.stack_id = CallTreeNode.stack_id
+ self.parent_id = 0
+ self.parent = None
+ self.name = name
+ self.self_time = 0.0
+ self.tot_time = 0.0
+ self.have_tot_time = False
+ self.children = {}
+
+ def getTotTime(self):
+ if self.have_tot_time:
+ return self.tot_time
+ else:
+ self.tot_time = self.self_time
+ for c in self.children.values():
+ self.tot_time += c.getTotTime()
+ self.have_tot_time = True
+ return self.tot_time
+
+ class Thread:
+ def __init__(self, comm, tid):
+ self.comm = comm
+ self.tid = tid
+ self.symbols = {}
+ self.name = "???"
+
+ self.call_tree = CallTreeNode('root')
+ self.sf_map = {}
+ self.sf_map[0] = self.call_tree
+
+ def getCtree(self, sf_id, name):
+ if sf_id in self.sf_map:
+ return (self.sf_map[sf_id], False)
+ else:
+ self.getSymbol(name) # tag symbol
+ newNode = CallTreeNode(name)
+ self.sf_map[sf_id] = newNode
+ return (newNode, True)
+
+ def getSymbol(self, name):
+ if name in self.symbols:
+ sym = self.symbols[name]
+ else:
+ sym = Symbol(name)
+ self.symbols[name] = sym
+ return sym
+
+ # Derrive thread names based on existing symbols.
+ thread_maps = [ ("Browser Main", ["cc::SingleThreadProxy::DoCommit(scoped_ptr<cc::ResourceUpdateQueue, base::DefaultDeleter<cc::ResourceUpdateQueue> >)@Chrome"]),
+ ("Renderer Main", ["blink::WebViewImpl::layout()@Chrome"]),
+ ("Browser InProcGpuThread", ["gpu::GpuScheduler::PutChanged()@Chrome"]),
+ ("Browser AsyncTransferThread", ["gpu::(anonymous namespace)::TransferStateInternal::PerformAsyncTexImage2D(gpu::AsyncTexImage2DParams, gpu::AsyncMemoryParams, gpu::ScopedSafeSharedMemory*, scoped_refptr<gpu::AsyncPixelTransferUploadStats>)", "gpu::(anonymous namespace)::TransferStateInternal::PerformAsyncTexSubImage2D(gpu::AsyncTexSubImage2DParams, gpu::AsyncMemoryParams, gpu::ScopedSafeSharedMemory*, scoped_refptr<gpu::AsyncPixelTransferUploadStats>)@Chrome", "gpu::(anonymous namespace)::TransferStateInternal::PerformAsyncTexSubImage2D(gpu::AsyncTexSubImage2DParams, gpu::AsyncMemoryParams, scoped_refptr<gpu::AsyncPixelTransferUploadStats>)@Chrome"]),
+ ("Renderer Compositor", ["cc::Scheduler::NotifyReadyToCommit()@Chrome"]),
+ ("Browser IOThread", ["content::BrowserThreadImpl::IOThreadRun(base::MessageLoop*)@Chrome"]),
+ ("Browser FileThread", ["content::BrowserThreadImpl::FileThreadRun(base::MessageLoop*)@Chrome"]),
+ ("Browser DBThread", ["content::BrowserThreadImpl::DBThreadRun(base::MessageLoop*)@Chrome"]),
+ ("Browser ChildIOThread", ["content::GpuChannelMessageFilter::OnMessageReceived(IPC::Message const&)@Chrome"]),
+ ("Renderer ChildIOThread", ["IPC::SyncMessageFilter::SendOnIOThread(IPC::Message*)@Chrome"]),
+ ("Renderer RasterWorker", ["cc::Picture::Raster(SkCanvas*, SkDrawPictureCallback*, cc::Region const&, float)@Chrome", "cc::(anonymous namespace)::RasterFinishedTaskImpl::RunOnWorkerThread()@Chrome"]),
+ ("DVM Compiler", ["dvmCompilerAssembleLIR(CompilationUnit*, JitTranslationInfo*)@Java"]),
+ ("DVM GC", ["dvmHeapBitmapScanWalk(HeapBitmap*, void (*)(Object*, void*, void*), void*)@Java"]),
+ ("Adreno Driver", ["adreno_drawctxt_wait@Kernel"]),
+ ]
+
+ def filterSymbolModule(module):
+ return module
+
+ def filterSymbolName(module, orign_module, name):
+ return name
+
+ def outputPefVis(options, args):
+ time_scale = 1000.0 / options.nframes / options.cpu_freq
+
+ threads = {}
+
+ def getThread(comm, tid):
+ if tid in threads:
+ thread = threads[tid]
+ else:
+ thread = Thread(comm, tid)
+ threads[tid] = thread
+ return thread
+
+ fp = open(args[0])
+ trace = json.load(fp)
+ fp.close()
+
+ # Process samples.
+ stackFrames = trace['stackFrames']
+ samples = trace['samples']
+
+ for s in samples:
+ samp_ts = s['ts'] / 1000.0
+ if samp_ts < options.start_ts or samp_ts > options.end_ts:
+ continue
+ samp_time = float(s['weight']) * time_scale
+
+ curr_thread = getThread(s['comm'], s['tid'])
+ sf_id = s.get('sf')
+ chain = deque()
+ while sf_id != 0:
+ sf = stackFrames[str(sf_id)]
+ chain.appendleft((sf['name'], sf['category']))
+ sf_id = sf.get('parent', 0)
+
+ if len(chain) >= 1:
+ # Add an entry with the same category, and name = base_category.
+ c = chain[0]
+ chain.appendleft((c[1], c[1]))
+
+ # Add to call tree.
+ ctree_node = curr_thread.call_tree
+ for c in chain:
+ chain_name = c
+ if chain_name in ctree_node.children:
+ ctree_node = ctree_node.children[chain_name]
+ else:
+ new_node = CallTreeNode(chain_name)
+ new_node.parent_id = ctree_node.stack_id
+ ctree_node.children[chain_name] = new_node
+ ctree_node = new_node
+ curr_thread.getSymbol(chain_name[0] + '@' + chain_name[1]) # tag symbol
+ ctree_node.self_time += samp_time
+
+ # Map thread names.
+ for t in threads.values():
+ if str(t.tid) in thread_names:
+ t.name = thread_names[str(t.tid)]
+ else:
+ for m in thread_maps:
+ match = False
+ for s in m[1]:
+ if s in t.symbols:
+ match = True
+ break
+ if match:
+ t.name = m[0]
+ break
+
+ def jsonCallTree(node, is_root = False):
+ ret = {}
+ name_comp = node.name
+ ret['name'] = name_comp[0]
+ if len(name_comp) > 1:
+ ret['comp'] = name_comp[1]
+
+ if len(node.children) > 0 or is_root:
+ ret['children'] = []
+ for c in sorted(node.children.values(), key=lambda c: -c.getTotTime()):
+ ret['children'].append(jsonCallTree(c))
+ if node.self_time > 0.0:
+ ret['children'].append({'name': '<self>', 'comp': ret['comp'], 'size': node.self_time})
+ else:
+ ret['size'] = node.self_time
+ if is_root:
+ return ret['children']
+ else:
+ return ret
+
+ threads_json = {'name': '<All Threads>', 'comp': 'root', 'children':[]}
+ sorted_threads = sorted(threads.values(), key=lambda thread: -thread.call_tree.getTotTime())
+ for t in sorted_threads:
+ if ('chrome' in t.comm) or ('ntent_shell' in t.comm) or ('dboxed' in t.comm):
+ tjson = {}
+ tjson['name'] = '<' + t.comm + ':' + t.name + '>'
+ tjson['children'] = jsonCallTree(t.call_tree, True)
+ tjson['comp'] = 'Thread'
+ threads_json['children'].append(tjson)
+
+ out_path = options.output
+ if not out_path:
+ out_base = os.path.basename(args[0])
+ today = date.today()
+ out_path = out_base + '_%02d%02d%02d.html' % (today.day, today.month, today.year)
+
+ vis_path = os.path.abspath(os.path.dirname(__file__))
+ # Load template
+ with open(vis_path + '/perf-vis-template.html', 'r') as template_file:
+ html_temp = template_file.read()
+
+ # Add title
+ html_temp = html_temp.replace('<page-title>', os.path.basename(out_path))
+
+ # Add perf data json
+ html_temp = html_temp.replace('<data_json>', json.dumps(threads_json, indent=0))
+
+ # Add jquery-1.11.0.min.js
+ with open(vis_path + '/jquery-1.11.0.min.js', 'r') as js:
+ html_temp = html_temp.replace('<jquery-1.11.0.min.js>', js.read())
+
+ # Add sammy-latest.min.js script
+ with open(vis_path + '/sammy-latest.min.js', 'r') as js:
+ html_temp = html_temp.replace('<sammy-latest.min.js>', js.read())
+
+ # Add d3.v3.min.js script
+ with open(vis_path + '/d3.v3.min.js', 'r') as js:
+ html_temp = html_temp.replace('<d3.v3.min.js>', js.read())
+
+ # Add jquery.dataTables.min.js
+ with open(vis_path + '/jquery.dataTables.min.js', 'r') as js:
+ html_temp = html_temp.replace('<jquery.dataTables.min.js>', js.read())
+
+ # Add jquery.dataTables.min.css
+ with open(vis_path + '/jquery.dataTables.css', 'r') as css:
+ html_temp = html_temp.replace('<jquery.dataTables.css>', css.read())
+
+ # Add perf-vis.js script
+ with open(vis_path + '/perf-vis.js', 'r') as js:
+ html_temp = html_temp.replace('<perf-vis.js>', js.read())
+
+ # Write result
+ print '### perf-vis output:', os.path.join(os.getcwd(), out_path)
+ with open(out_path, 'w') as html_file:
+ html_file.write(html_temp)
+
+
+ outputPefVis(options, args)
+
+if __name__ == "__main__":
+ (options, arguments) = parser.parse_args()
+ Process(options, arguments)

Powered by Google App Engine
This is Rietveld 408576698