1#    Copyright 2016-2017 ARM Limited
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14#
15
16from trappy.ftrace import GenericFTrace
17import re
18
19SYSTRACE_EVENT = re.compile(
20    r'^(?P<event>[A-Z])(\|(?P<pid>\d+)\|(?P<func>[^|]*)(\|(?P<data>.*))?)?')
21
22class drop_before_trace(object):
23    """Object that, when called, returns True if the line is not part of
24the trace
25
26    We have to first look for the "<!-- BEGIN TRACE -->" and then skip
27    the headers that start with #
28
29    """
30    def __init__(self, tracer):
31        self.before_begin_trace = True
32        self.before_actual_trace = True
33        self.tracer = tracer
34
35    def __call__(self, line):
36        if self.before_begin_trace:
37            if line.startswith("<!-- BEGIN TRACE -->") or \
38               line.startswith("<title>Android System Trace</title>"):
39                self.before_begin_trace = False
40        elif self.before_actual_trace:
41            if line.startswith('  <script class="trace-data"') or \
42               line.startswith("  var linuxPerfData"):
43                self.before_actual_trace = False
44
45        if not self.before_actual_trace:
46            base_call = super(SysTrace, self.tracer).trace_hasnt_started()
47            return base_call(line)
48        else:
49            return True
50
51class SysTrace(GenericFTrace):
52    """A wrapper that parses all events of a SysTrace run
53
54    It receives the same parameters as :mod:`trappy.ftrace.FTrace`.
55
56    """
57
58    def __init__(self, path=".", name="", normalize_time=True, scope="all",
59                 events=[], window=(0, None), abs_window=(0, None)):
60
61        self.trace_path = path
62
63        super(SysTrace, self).__init__(name, normalize_time, scope, events,
64                                       window, abs_window)
65
66        self._do_parse()
67        try:
68            self._cpus = 1 + self.sched_switch.data_frame["__cpu"].max()
69        except AttributeError:
70            pass
71
72    def trace_hasnt_started(self):
73        return drop_before_trace(self)
74
75    def trace_hasnt_finished(self):
76        """Return a function that returns True while the current line is still part of the trace
77
78        In Systrace, the first line that is not part of the trace is
79        </script>.  There's a further "<!-- END TRACE -->" but there's
80        not point scanning for it, we should stop parsing as soon as
81        we see the </script>
82
83        """
84        return lambda x: not x.endswith("</script>\n")
85
86    def generate_data_dict(self, data_str):
87        """ Custom parsing for systrace's userspace events """
88        data_dict = None
89
90        match = SYSTRACE_EVENT.match(data_str)
91        if match:
92            data_dict = {
93                          'event': match.group('event'),
94                          'pid'  : int(match.group('pid')) if match.group('pid') else None,
95                          'func' : match.group('func' ),
96                          'data' : match.group('data' )
97                        }
98
99        return data_dict
100