Index: tools/perf/metrics/timeline.py |
diff --git a/tools/perf/metrics/timeline.py b/tools/perf/metrics/timeline.py |
index 75efd73d33ec562c19242a377e0e2d0a69f354f5..0dd2bbbf037afa183b12501ff020b12d36bf57fa 100644 |
--- a/tools/perf/metrics/timeline.py |
+++ b/tools/perf/metrics/timeline.py |
@@ -4,10 +4,16 @@ |
import collections |
from metrics import Metric |
+from telemetry.page import page_measurement |
TRACING_MODE = 'tracing-mode' |
TIMELINE_MODE = 'timeline-mode' |
+class MissingFramesError(page_measurement.MeasurementFailure): |
+ def __init__(self): |
+ super(MissingFramesError, self).__init__( |
+ 'No frames found in trace. Unable to normalize results.') |
+ |
class TimelineMetric(Metric): |
def __init__(self, mode): |
""" Initializes a TimelineMetric object. |
@@ -114,8 +120,12 @@ TimelineThreadCategories = { |
"CrRendererMain" : "renderer_main", |
"Compositor" : "renderer_compositor", |
"IOThread" : "IO", |
- "CompositorRasterWorker": "raster" |
+ "CompositorRasterWorker": "raster", |
+ "DummyThreadName1" : "other", |
+ "DummyThreadName2" : "total_fast_path", |
+ "DummyThreadName3" : "total_all" |
} |
+ |
MatchBySubString = ["IOThread", "CompositorRasterWorker"] |
FastPath = ["GPU", |
"browser_main", |
@@ -123,18 +133,38 @@ FastPath = ["GPU", |
"renderer_compositor", |
"IO"] |
+AllThreads = TimelineThreadCategories.values() |
+NoThreads = [] |
+MainThread = ["renderer_main"] |
+FastPathResults = AllThreads |
+FastPathDetails = NoThreads |
+SilkResults = ["renderer_main", "total_all"] |
+SilkDetails = MainThread |
+ |
+def ThreadCategoryName(thread_name): |
+ thread_category = "other" |
+ for substring, category in TimelineThreadCategories.iteritems(): |
+ if substring in MatchBySubString and substring in thread_name: |
+ thread_category = category |
+ if thread_name in TimelineThreadCategories: |
+ thread_category = TimelineThreadCategories[thread_name] |
+ return thread_category |
-def ThreadTimePercentageName(category): |
- return "thread_" + category + "_clock_time_percentage" |
+def ThreadTimeResultName(thread_category): |
+ return "thread_" + thread_category + "_clock_time_per_frame" |
-def ThreadCPUTimePercentageName(category): |
- return "thread_" + category + "_cpu_time_percentage" |
+def ThreadCpuTimeResultName(thread_category): |
+ return "thread_" + thread_category + "_cpu_time_per_frame" |
+ |
+def ThreadDetailResultName(thread_category, detail): |
+ return "thread_" + thread_category + "|" + detail |
class ResultsForThread(object): |
- def __init__(self, model): |
+ def __init__(self, model, name): |
self.model = model |
self.toplevel_slices = [] |
self.all_slices = [] |
+ self.name = name |
@property |
def clock_time(self): |
@@ -153,80 +183,79 @@ class ResultsForThread(object): |
res += x.thread_duration |
return res |
- def AddDetailedResults(self, thread_category_name, results): |
+ def AppendThreadSlices(self, thread): |
+ self.all_slices.extend(thread.all_slices) |
+ self.toplevel_slices.extend(thread.toplevel_slices) |
+ |
+ def AddResults(self, num_frames, results): |
+ clock_report_name = ThreadTimeResultName(self.name) |
+ cpu_report_name = ThreadCpuTimeResultName(self.name) |
+ clock_per_frame = float(self.clock_time) / num_frames |
+ cpu_per_frame = float(self.cpu_time) / num_frames |
+ results.Add(clock_report_name, 'ms', clock_per_frame) |
+ results.Add(cpu_report_name, 'ms', cpu_per_frame) |
+ |
+ def AddDetailedResults(self, num_frames, results): |
slices_by_category = collections.defaultdict(list) |
for s in self.all_slices: |
slices_by_category[s.category].append(s) |
all_self_times = [] |
for category, slices_in_category in slices_by_category.iteritems(): |
self_time = sum([x.self_time for x in slices_in_category]) |
- results.Add('%s|%s' % (thread_category_name, category), 'ms', self_time) |
all_self_times.append(self_time) |
+ self_time_result = float(self_time) / num_frames |
+ results.Add(ThreadDetailResultName(self.name, category), |
+ 'ms', self_time_result) |
all_measured_time = sum(all_self_times) |
- idle_time = max(0, |
- self.model.bounds.bounds - all_measured_time) |
- results.Add('%s|idle' % thread_category_name, 'ms', idle_time) |
+ idle_time = max(0, self.model.bounds.bounds - all_measured_time) |
+ idle_time_result = float(idle_time) / num_frames |
+ results.Add(ThreadDetailResultName(self.name, "idle"), |
+ 'ms', idle_time_result) |
class ThreadTimesTimelineMetric(TimelineMetric): |
def __init__(self): |
super(ThreadTimesTimelineMetric, self).__init__(TRACING_MODE) |
- self.report_renderer_main_details = False |
- |
- def GetThreadCategoryName(self, thread): |
- # First determine if we care about this thread. |
- # Check substrings first, followed by exact matches |
- thread_category = None |
- for substring, category in TimelineThreadCategories.iteritems(): |
- if substring in thread.name: |
- thread_category = category |
- if thread.name in TimelineThreadCategories: |
- thread_category = TimelineThreadCategories[thread.name] |
- if thread_category == None: |
- thread_category = "other" |
- |
- return thread_category |
+ self.results_to_report = AllThreads |
+ self.details_to_report = NoThreads |
+ |
+ def CalcFrameCount(self): |
+ gpu_swaps = 0 |
+ for thread in self._model.GetAllThreads(): |
+ if (ThreadCategoryName(thread.name) == "GPU"): |
+ for event in thread.IterAllSlices(): |
+ if ":RealSwapBuffers" in event.name: |
+ gpu_swaps += 1 |
+ return gpu_swaps |
def AddResults(self, tab, results): |
- results_per_thread_category = collections.defaultdict( |
- lambda: ResultsForThread(self._model)) |
+ num_frames = self.CalcFrameCount() |
+ if not num_frames: |
+ raise MissingFramesError() |
- # Set up each category anyway so that we get consistant results. |
- for category in TimelineThreadCategories.values(): |
- results_per_thread_category[category] = ResultsForThread(self.model) |
- results_for_all_threads = results_per_thread_category['total_fast_path'] |
+ # Set up each thread category for consistant results. |
+ thread_category_results = {} |
+ for name in TimelineThreadCategories.values(): |
+ thread_category_results[name] = ResultsForThread(self.model, name) |
# Group the slices by their thread category. |
for thread in self._model.GetAllThreads(): |
- # First determine if we care about this thread. |
- # Check substrings first, followed by exact matches |
- thread_category = self.GetThreadCategoryName(thread) |
- |
- results_for_thread = results_per_thread_category[thread_category] |
- for event in thread.all_slices: |
- results_for_thread.all_slices.append(event) |
- results_for_all_threads.all_slices.append(event) |
- for event in thread.toplevel_slices: |
- results_for_thread.toplevel_slices.append(event) |
- results_for_all_threads.toplevel_slices.append(event) |
- |
- for thread_category, results_for_thread_category in ( |
- results_per_thread_category.iteritems()): |
- thread_report_name = ThreadTimePercentageName(thread_category) |
- time_as_percentage = (float(results_for_thread_category.clock_time) / |
- self._model.bounds.bounds) * 100 |
- results.Add(thread_report_name, '%', time_as_percentage) |
- |
- for thread_category, results_for_thread_category in ( |
- results_per_thread_category.iteritems()): |
- cpu_time_report_name = ThreadCPUTimePercentageName(thread_category) |
- time_as_percentage = (float(results_for_thread_category.cpu_time) / |
- self._model.bounds.bounds) * 100 |
- results.Add(cpu_time_report_name, '%', time_as_percentage) |
- |
- # TOOD(nduca): When generic results objects are done, this special case |
- # can be replaced with a generic UI feature. |
- for thread_category, results_for_thread_category in ( |
- results_per_thread_category.iteritems()): |
- if (thread_category == 'renderer_main' and |
- self.report_renderer_main_details): |
- results_for_thread_category.AddDetailedResults(thread_category, results) |
+ thread_category = ThreadCategoryName(thread.name) |
+ thread_category_results[thread_category].AppendThreadSlices(thread) |
+ |
+ # Group all threads. |
+ for thread in self._model.GetAllThreads(): |
+ thread_category_results['total_all'].AppendThreadSlices(thread) |
+ |
+ # Also group fast-path threads. |
+ for thread in self._model.GetAllThreads(): |
+ if ThreadCategoryName(thread.name) in FastPath: |
+ thread_category_results['total_fast_path'].AppendThreadSlices(thread) |
+ |
+ # Report the desired results and details. |
+ for thread_results in thread_category_results.values(): |
+ if thread_results.name in self.results_to_report: |
+ thread_results.AddResults(num_frames, results) |
+ # TOOD(nduca): When generic results objects are done, this special case |
+ # can be replaced with a generic UI feature. |
+ if thread_results.name in self.details_to_report: |
+ thread_results.AddDetailedResults(num_frames, results) |