1
0
mirror of https://github.com/ARM-software/devlib.git synced 2025-01-31 10:10:46 +00:00

ftrace: Enable alternative tracers

"function_graph" tracer allows getting funcgraph_entry/funcgraph_exit events for
listed functions. This allows getting precise information on when a given
function was called, and how long its execution took (to build a time-based
heatmap for example).

This can be enabled using:
     FtraceCollector(target, functions=['foo', 'bar'], tracer='function_graph')

If needed, children functions can also be traced with
trace_children_functions=True .

Signed-off-by: Douglas RAILLARD <douglas.raillard@arm.com>
This commit is contained in:
Douglas RAILLARD 2019-11-01 16:00:43 +00:00 committed by Marc Bonnici
parent 56cdc2e6c3
commit 6b0b12d833

View File

@ -20,6 +20,7 @@ import time
import re import re
import subprocess import subprocess
import sys import sys
from pipes import quote
from devlib.trace import TraceCollector from devlib.trace import TraceCollector
from devlib.host import PACKAGE_BIN_DIRECTORY from devlib.host import PACKAGE_BIN_DIRECTORY
@ -54,6 +55,8 @@ class FtraceCollector(TraceCollector):
def __init__(self, target, def __init__(self, target,
events=None, events=None,
functions=None, functions=None,
tracer=None,
trace_children_functions=False,
buffer_size=None, buffer_size=None,
buffer_size_step=1000, buffer_size_step=1000,
tracing_path='/sys/kernel/debug/tracing', tracing_path='/sys/kernel/debug/tracing',
@ -69,6 +72,8 @@ class FtraceCollector(TraceCollector):
super(FtraceCollector, self).__init__(target) super(FtraceCollector, self).__init__(target)
self.events = events if events is not None else DEFAULT_EVENTS self.events = events if events is not None else DEFAULT_EVENTS
self.functions = functions self.functions = functions
self.tracer = tracer
self.trace_children_functions = trace_children_functions
self.buffer_size = buffer_size self.buffer_size = buffer_size
self.buffer_size_step = buffer_size_step self.buffer_size_step = buffer_size_step
self.tracing_path = tracing_path self.tracing_path = tracing_path
@ -137,13 +142,6 @@ class FtraceCollector(TraceCollector):
self.target.logger.warning(message) self.target.logger.warning(message)
else: else:
selected_events.append(event) selected_events.append(event)
# If function profiling is enabled we always need at least one event.
# Thus, if not other events have been specified, try to add at least
# a tracepoint which is always available and possibly triggered few
# times.
if self.functions and not selected_events:
selected_events = ['sched_wakeup_new']
self.event_string = _build_trace_events(selected_events)
# Check for function tracing support # Check for function tracing support
if self.functions: if self.functions:
@ -163,7 +161,19 @@ class FtraceCollector(TraceCollector):
self.target.logger.warning(message) self.target.logger.warning(message)
else: else:
selected_functions.append(function) selected_functions.append(function)
self.function_string = _build_trace_functions(selected_functions)
if self.tracer is None:
self.function_string = _build_trace_functions(selected_functions)
# If function profiling is enabled we always need at least one event.
# Thus, if not other events have been specified, try to add at least
# a tracepoint which is always available and possibly triggered few
# times.
if not selected_events:
selected_events = ['sched_wakeup_new']
elif self.tracer == 'function_graph':
self.function_string = _build_graph_functions(selected_functions, trace_children_functions)
self.event_string = _build_trace_events(selected_events)
def reset(self): def reset(self):
if self.buffer_size: if self.buffer_size:
@ -177,10 +187,24 @@ class FtraceCollector(TraceCollector):
if self._reset_needed: if self._reset_needed:
self.reset() self.reset()
if self.tracer is not None and 'function' in self.tracer:
tracecmd_functions = self.function_string
else:
tracecmd_functions = ''
tracer_string = '-p {}'.format(self.tracer) if self.tracer else ''
self.target.write_value(self.trace_clock_file, self.trace_clock, verify=False) self.target.write_value(self.trace_clock_file, self.trace_clock, verify=False)
self.target.write_value(self.save_cmdlines_size_file, self.saved_cmdlines_nr) self.target.write_value(self.save_cmdlines_size_file, self.saved_cmdlines_nr)
self.target.execute('{} start {}'.format(self.target_binary, self.event_string), self.target.execute(
as_root=True) '{} start {events} {tracer} {functions}'.format(
self.target_binary,
events=self.event_string,
tracer=tracer_string,
functions=tracecmd_functions,
),
as_root=True,
)
if self.automark: if self.automark:
self.mark_start() self.mark_start()
if 'cpufreq' in self.target.modules: if 'cpufreq' in self.target.modules:
@ -190,7 +214,7 @@ class FtraceCollector(TraceCollector):
self.logger.debug('Trace CPUIdle states') self.logger.debug('Trace CPUIdle states')
self.target.cpuidle.perturb_cpus() self.target.cpuidle.perturb_cpus()
# Enable kernel function profiling # Enable kernel function profiling
if self.functions: if self.functions and self.tracer is None:
self.target.execute('echo nop > {}'.format(self.current_tracer_file), self.target.execute('echo nop > {}'.format(self.current_tracer_file),
as_root=True) as_root=True)
self.target.execute('echo 0 > {}'.format(self.function_profile_file), self.target.execute('echo 0 > {}'.format(self.function_profile_file),
@ -203,7 +227,7 @@ class FtraceCollector(TraceCollector):
def stop(self): def stop(self):
# Disable kernel function profiling # Disable kernel function profiling
if self.functions: if self.functions and self.tracer is None:
self.target.execute('echo 1 > {}'.format(self.function_profile_file), self.target.execute('echo 1 > {}'.format(self.function_profile_file),
as_root=True) as_root=True)
if 'cpufreq' in self.target.modules: if 'cpufreq' in self.target.modules:
@ -243,7 +267,7 @@ class FtraceCollector(TraceCollector):
self.view(outfile) self.view(outfile)
def get_stats(self, outfile): def get_stats(self, outfile):
if not self.functions: if not (self.functions and self.tracer is None):
return return
if os.path.isdir(outfile): if os.path.isdir(outfile):
@ -360,3 +384,10 @@ def _build_trace_events(events):
def _build_trace_functions(functions): def _build_trace_functions(functions):
function_string = " ".join(functions) function_string = " ".join(functions)
return function_string return function_string
def _build_graph_functions(functions, trace_children_functions):
opt = 'g' if trace_children_functions else 'l'
return ' '.join(
'-{} {}'.format(opt, quote(f))
for f in functions
)