| Index: systrace/systrace/tracing_agents/ftrace_agent.py
|
| diff --git a/systrace/systrace/agents/ftrace_agent.py b/systrace/systrace/tracing_agents/ftrace_agent.py
|
| similarity index 48%
|
| rename from systrace/systrace/agents/ftrace_agent.py
|
| rename to systrace/systrace/tracing_agents/ftrace_agent.py
|
| index 758fc617e5e10dea6140501b7a7d678e6dbf49ce..281a88db31ad7dec298609366327331bcbea7b87 100644
|
| --- a/systrace/systrace/agents/ftrace_agent.py
|
| +++ b/systrace/systrace/tracing_agents/ftrace_agent.py
|
| @@ -4,10 +4,15 @@
|
|
|
| import os
|
| import sys
|
| -import time
|
|
|
| -from systrace import systrace_agent
|
| +from collections import namedtuple
|
| +from devil.utils import timeout_retry
|
| +from systrace.tracing_agents import TracingAgent
|
| +from systrace.tracing_agents import TraceResults
|
|
|
| +ftrace_postproc = namedtuple('ftrace_postproc', ['fix_threads',
|
| + 'fix_tgids',
|
| + 'fix_circular'])
|
|
|
| class FtraceAgentIo(object):
|
|
|
| @@ -25,6 +30,13 @@ class FtraceAgentIo(object):
|
| def haveWritePermissions(path):
|
| return os.access(path, os.W_OK)
|
|
|
| + @staticmethod
|
| + def checkAndWriteFile(path, data):
|
| + if FtraceAgentIo.haveWritePermissions(path):
|
| + FtraceAgentIo.writeFile(path, data)
|
| + else:
|
| + raise IOError('Cannot write to %s; did you forget sudo/root?' % path)
|
| +
|
| FT_DIR = "/sys/kernel/debug/tracing/"
|
| FT_CLOCK = FT_DIR + "trace_clock"
|
| FT_BUFFER_SIZE = FT_DIR + "buffer_size_kb"
|
| @@ -84,118 +96,145 @@ all_categories = {
|
| }
|
|
|
|
|
| -def try_create_agent(options, categories):
|
| +def list_categories(_):
|
| + agent = FtraceAgent(FtraceAgentIo)
|
| + agent._print_avail_categories()
|
| +
|
| +
|
| +def try_create_agent(options, _):
|
| if options.target != 'linux':
|
| return False
|
| - return FtraceAgent(options, categories, FtraceAgentIo)
|
| + return FtraceAgent(FtraceAgentIo)
|
|
|
|
|
| -class FtraceAgent(systrace_agent.SystraceAgent):
|
| +def _get_trace_buffer_size(options):
|
| + buffer_size = 4096
|
| + if ((options.trace_buf_size is not None)
|
| + and (options.trace_buf_size > 0)):
|
| + buffer_size = options.trace_buf_size
|
| + return buffer_size
|
|
|
| - def __init__(self, options, categories, fio=FtraceAgentIo):
|
| +
|
| +class FtraceAgent(TracingAgent):
|
| +
|
| + def __init__(self, fio=FtraceAgentIo):
|
| """Initialize a systrace agent.
|
|
|
| Args:
|
| options: The command-line options.
|
| categories: The trace categories to capture.
|
| """
|
| - super(FtraceAgent, self).__init__(options, categories)
|
| - if not self._categories:
|
| - self._categories = ["sched"]
|
| + super(FtraceAgent, self).__init__()
|
| self._fio = fio
|
| - self._categories = [x for x in self._categories
|
| - if self._is_category_available(x)]
|
| - self._expect_trace = False
|
| -
|
| - def _get_trace_buffer_size(self):
|
| - buffer_size = 4096
|
| - if ((self._options.trace_buf_size is not None)
|
| - and (self._options.trace_buf_size > 0)):
|
| - buffer_size = self._options.trace_buf_size
|
| - return buffer_size
|
| -
|
| - def _get_trace_time(self):
|
| - wait_time = 5
|
| - if ((self._options.trace_time is not None)
|
| - and (self._options.trace_time > 0)):
|
| - wait_time = self._options.trace_time
|
| - return wait_time
|
| -
|
| - def start(self):
|
| + self._postproc = None
|
| + self._categories = None
|
| +
|
| + def __str__(self):
|
| + return 'FtraceAgent'
|
| +
|
| + def _fix_categories(self, categories):
|
| + """Fix a list of categories.
|
| +
|
| + Applies the default category (sched) if there are no categories
|
| + in the list and removes unavailable categories from the list.
|
| +
|
| + Args:
|
| + categories: List of categories.
|
| + """
|
| + if not categories:
|
| + categories = ["sched"]
|
| + return [x for x in categories
|
| + if self._is_category_available(x)]
|
| +
|
| + def _StartAgentTracingImpl(self, options, categories):
|
| """Start tracing.
|
| +
|
| + Args:
|
| + options: Tracing options.
|
| + categories: Categories of trace events to capture.
|
| """
|
| - if self._options.list_categories or len(self._categories) == 0:
|
| - self._expect_trace = False
|
| - else:
|
| - self._expect_trace = True
|
| + categories = self._fix_categories(categories)
|
| + self._fio.checkAndWriteFile(FT_BUFFER_SIZE,
|
| + str(_get_trace_buffer_size(options)))
|
|
|
| - self._fio.writeFile(FT_BUFFER_SIZE, str(self._get_trace_buffer_size()))
|
| + self._fio.checkAndWriteFile(FT_CLOCK, 'global')
|
| + self._fio.checkAndWriteFile(FT_TRACER, 'nop')
|
| + self._fio.checkAndWriteFile(FT_OVERWRITE, "0")
|
|
|
| - self._fio.writeFile(FT_CLOCK, 'global')
|
| - self._fio.writeFile(FT_TRACER, 'nop')
|
| - self._fio.writeFile(FT_OVERWRITE, "0")
|
| + # TODO: riandrews to push necessary patches for TGID option to upstream
|
| + # linux kernel
|
| + # self._fio.checkAndWriteFile(FT_PRINT_TGID, '1')
|
|
|
| - # TODO: riandrews to push necessary patches for TGID option to upstream
|
| - # linux kernel
|
| - # self._fio.writeFile(FT_PRINT_TGID, '1')
|
| + for category in categories:
|
| + self._category_enable(category)
|
|
|
| - for category in self._categories:
|
| - self._category_enable(category)
|
| + self._categories = categories # need to store list of categories to disable
|
| + self._postproc = ftrace_postproc(options.fix_threads,
|
| + options.fix_tgids,
|
| + options.fix_circular)
|
|
|
| - self._fio.writeFile(FT_TRACE, '')
|
| + self._fio.checkAndWriteFile(FT_TRACE, '')
|
|
|
| - print "starting tracing."
|
| - sys.stdout.flush()
|
| + sys.stdout.flush()
|
|
|
| - self._fio.writeFile(FT_TRACE_ON, '1')
|
| + self._fio.checkAndWriteFile(FT_TRACE_ON, '1')
|
| + return True
|
| +
|
| + def StartAgentTracing(self, options, categories, timeout=10):
|
| + return timeout_retry.Run(self._StartAgentTracingImpl,
|
| + timeout, 1,
|
| + args=[options, categories])
|
|
|
| - def collect_result(self):
|
| + def _StopAgentTracingImpl(self):
|
| """Collect the result of tracing.
|
|
|
| - This function will block while collecting the result. For sync mode, it
|
| - reads the data, e.g., from stdout, until it finishes. For async mode, it
|
| - blocks until the agent is stopped and the data is ready.
|
| - """
|
| - if self._options.list_categories or len(self._categories) == 0:
|
| - self._print_avail_categories()
|
| - else:
|
| - try:
|
| - time.sleep(self._get_trace_time())
|
| - except KeyboardInterrupt:
|
| - pass
|
| - self._fio.writeFile(FT_TRACE_ON, '0')
|
| - for category in self._categories:
|
| - self._category_disable(category)
|
| - if self._options.fix_threads:
|
| - print "WARN: thread name fixing is not yet supported."
|
| - if self._options.fix_tgids:
|
| - print "WARN: tgid fixing is not yet supported."
|
| - if self._options.fix_circular:
|
| - print "WARN: circular buffer fixups are not yet supported."
|
| -
|
| - def expect_trace(self):
|
| - """Check if the agent is returning a trace or not.
|
| -
|
| - This will be determined in collect_result().
|
| - Returns:
|
| - Whether the agent is expecting a trace or not.
|
| + This function will block while collecting the result.
|
| """
|
| - return self._expect_trace
|
| + self._fio.checkAndWriteFile(FT_TRACE_ON, '0')
|
| + for category in self._categories:
|
| + self._category_disable(category)
|
| + if self._postproc.fix_threads:
|
| + print "WARN: thread name fixing is not yet supported."
|
| + if self._postproc.fix_tgids:
|
| + print "WARN: tgid fixing is not yet supported."
|
| + if self._postproc.fix_circular:
|
| + print "WARN: circular buffer fixups are not yet supported."
|
| + return True
|
| +
|
| + def StopAgentTracing(self, timeout=10):
|
| + return timeout_retry.Run(self._StopAgentTracingImpl,
|
| + timeout, 1)
|
|
|
| - def get_trace_data(self):
|
| + def _GetResultsImpl(self):
|
| """Get the trace data.
|
|
|
| Returns:
|
| The trace data.
|
| """
|
| d = self._fio.readFile(FT_TRACE)
|
| - self._fio.writeFile(FT_BUFFER_SIZE, "1")
|
| - return d
|
| + self._fio.checkAndWriteFile(FT_BUFFER_SIZE, "1")
|
| + return TraceResults('systemTraceEvents', d)
|
|
|
| - def get_class_name(self):
|
| - return 'trace-data'
|
| + def GetResults(self, timeout=30):
|
| + return timeout_retry.Run(self._GetResultsImpl,
|
| + timeout, 1)
|
| +
|
| + def SupportsExplicitClockSync(self):
|
| + return False
|
| +
|
| + def RecordClockSyncMarker(self, sync_id, callback):
|
| + raise NotImplementedError
|
|
|
| def _is_category_available(self, category):
|
| + """Determines if a category is available for tracing.
|
| +
|
| + For a category to be available, we must have write permissions
|
| + to the file path needed to record the types of events in
|
| + that category.
|
| +
|
| + Args:
|
| + category: Category to check.
|
| + """
|
| if category not in all_categories:
|
| return False
|
| events_dir = FT_DIR + "events/"
|
| @@ -207,6 +246,7 @@ class FtraceAgent(systrace_agent.SystraceAgent):
|
| return True
|
|
|
| def _avail_categories(self):
|
| + """Get list of available categories."""
|
| ret = []
|
| for event in all_categories:
|
| if self._is_category_available(event):
|
| @@ -214,6 +254,7 @@ class FtraceAgent(systrace_agent.SystraceAgent):
|
| return ret
|
|
|
| def _print_avail_categories(self):
|
| + """Print the list of available categories."""
|
| avail = self._avail_categories()
|
| if len(avail):
|
| print "tracing options:"
|
| @@ -224,6 +265,11 @@ class FtraceAgent(systrace_agent.SystraceAgent):
|
| print "No tracing categories available - perhaps you need root?"
|
|
|
| def _category_enable_paths(self, category):
|
| + """Gets the list of file paths that correspond to events in category.
|
| +
|
| + Args:
|
| + category: Category to check.
|
| + """
|
| events_dir = FT_DIR + "events/"
|
| req_events = all_categories[category]["req"]
|
| for event in req_events:
|
| @@ -237,9 +283,11 @@ class FtraceAgent(systrace_agent.SystraceAgent):
|
| yield event_full_path
|
|
|
| def _category_enable(self, category):
|
| + """Enables tracing for a category."""
|
| for path in self._category_enable_paths(category):
|
| - self._fio.writeFile(path, "1")
|
| + self._fio.checkAndWriteFile(path, "1")
|
|
|
| def _category_disable(self, category):
|
| + """Disables tracing for a category."""
|
| for path in self._category_enable_paths(category):
|
| - self._fio.writeFile(path, "0")
|
| + self._fio.checkAndWriteFile(path, "0")
|
|
|