1#
2# Copyright (C) 2017 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import logging
17import os
18
19from vts.proto import VtsReportMessage_pb2 as ReportMsg
20from vts.runners.host import keys
21from vts.utils.python.systrace import systrace_controller
22from vts.utils.python.web import feature_utils
23
24_SYSTRACE_CONTROLLER = "systrace_controller"
25
26
27class SystraceFeature(feature_utils.Feature):
28    """Feature object for systrace functionality.
29
30    Attributes:
31        enabled: boolean, True if systrace is enabled, False otherwise
32        web: (optional) WebFeature, object storing web feature util for test run
33    """
34
35    _TOGGLE_PARAM = keys.ConfigKeys.IKEY_ENABLE_SYSTRACE
36    _REQUIRED_PARAMS = [
37        keys.ConfigKeys.KEY_TESTBED_NAME, keys.ConfigKeys.IKEY_ANDROID_DEVICE,
38        keys.ConfigKeys.IKEY_DATA_FILE_PATH,
39        keys.ConfigKeys.IKEY_SYSTRACE_REPORT_PATH,
40        keys.ConfigKeys.IKEY_SYSTRACE_REPORT_URL_PREFIX
41    ]
42    _OPTIONAL_PARAMS = [keys.ConfigKeys.IKEY_SYSTRACE_PROCESS_NAME]
43
44    def __init__(self, user_params, web=None):
45        """Initializes the systrace feature.
46
47        Args:
48            user_params: A dictionary from parameter name (String) to parameter value.
49            web: (optional) WebFeature, object storing web feature util for test run
50        """
51        self.ParseParameters(self._TOGGLE_PARAM, self._REQUIRED_PARAMS,
52                             self._OPTIONAL_PARAMS, user_params)
53        self.web = web
54        if self.enabled:
55            logging.info("Systrace is enabled.")
56        else:
57            logging.debug("Systrace is disabled.")
58
59    def StartSystrace(self):
60        """Initialize systrace controller if enabled.
61
62        Requires the feature to be enabled; no-op otherwise.
63        """
64        if not self.enabled:
65            return
66
67        process_name = getattr(self,
68                               keys.ConfigKeys.IKEY_SYSTRACE_PROCESS_NAME, '')
69        process_name = str(process_name)
70        data_file_path = getattr(self, keys.ConfigKeys.IKEY_DATA_FILE_PATH)
71
72        # TODO: handle device_serial for multi-device
73        android_devices = getattr(self, keys.ConfigKeys.IKEY_ANDROID_DEVICE)
74        if not isinstance(android_devices, list):
75            logging.warn("android device information not available")
76            return
77        device_spec = android_devices[0]
78        serial = device_spec.get(keys.ConfigKeys.IKEY_SERIAL)
79        if not serial:
80            logging.error("Serial for device at index 0 is not available.")
81            self.enabled = False
82        serial = str(serial)
83
84        android_vts_path = os.path.normpath(os.path.join(data_file_path, '..'))
85        self.controller = systrace_controller.SystraceController(
86            android_vts_path, device_serial=serial, process_name=process_name)
87        self.controller.Start()
88
89    def ProcessAndUploadSystrace(self, test_name):
90        """Stops and outputs the systrace data to configured path.
91
92        Requires the feature to be enabled; no-op otherwise.
93
94        Args:
95            test_name: String, the name of the test
96        """
97        if not self.enabled:
98            return
99
100        controller = getattr(self, "controller", None)
101        if not controller:
102            logging.warn("ProcessSystrace: missing systrace controller")
103            return
104
105        controller.Stop()
106
107        if not controller.has_output:
108            logging.warn("ProcessSystrace: systrace controller has no output")
109            return
110
111        try:
112            test_module_name = getattr(self, keys.ConfigKeys.KEY_TESTBED_NAME)
113            process = controller.process_name
114            time = feature_utils.GetTimestamp()
115            report_path = getattr(self,
116                                  keys.ConfigKeys.IKEY_SYSTRACE_REPORT_PATH)
117            report_destination_file_name = 'systrace_{test}_{process}_{time}.html'.format(
118                test=test_name, process=process, time=time)
119            report_destination_file_path = os.path.join(
120                report_path, report_destination_file_name)
121            if controller.SaveLastOutput(report_destination_file_path):
122                logging.debug('Systrace output saved to %s',
123                              report_destination_file_path)
124            else:
125                logging.error('Failed to save systrace output.')
126
127            report_url_prefix = getattr(
128                self, keys.ConfigKeys.IKEY_SYSTRACE_REPORT_URL_PREFIX)
129            report_url_prefix = str(report_url_prefix)
130            report_destination_file_url = '%s%s' % (
131                report_url_prefix, report_destination_file_name)
132
133            if self.web and self.web.enabled:
134                self.web.AddSystraceUrl(report_destination_file_url)
135            logging.info('systrace result path %s .',
136                         report_destination_file_url)
137        except Exception as e:  # TODO(yuexima): more specific exceptions catch
138            logging.exception('Failed to add systrace to report message %s', e)
139        finally:
140            if not controller.ClearLastOutput():
141                logging.error('failed to clear last systrace output.')
142