1# Copyright 2018, The Android Open Source Project
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"""
16Utility functions for metrics.
17"""
18
19import os
20import platform
21import sys
22import time
23import traceback
24
25from . import metrics
26from . import metrics_base
27
28
29def static_var(varname, value):
30    """Decorator to cache static variable."""
31    def fun_var_decorate(func):
32        """Set the static variable in a function."""
33        setattr(func, varname, value)
34        return func
35    return fun_var_decorate
36
37
38@static_var("start_time", [])
39def get_start_time():
40    """Get start time.
41
42    Return:
43        start_time: Start time in seconds. Return cached start_time if exists,
44        time.time() otherwise.
45    """
46    if not get_start_time.start_time:
47        get_start_time.start_time = time.time()
48    return get_start_time.start_time
49
50
51def convert_duration(diff_time_sec):
52    """Compute duration from time difference.
53
54    A Duration represents a signed, fixed-length span of time represented
55    as a count of seconds and fractions of seconds at nanosecond
56    resolution.
57
58    Args:
59        dur_time_sec: The time in seconds as a floating point number.
60
61    Returns:
62        A dict of Duration.
63    """
64    seconds = int(diff_time_sec)
65    nanos = int((diff_time_sec - seconds)*10**9)
66    return {'seconds': seconds, 'nanos': nanos}
67
68
69# pylint: disable=broad-except
70def handle_exc_and_send_exit_event(exit_code):
71    """handle exceptions and send exit event.
72
73    Args:
74        exit_code: An integer of exit code.
75    """
76    stacktrace = logs = ''
77    try:
78        exc_type, exc_msg, _ = sys.exc_info()
79        stacktrace = traceback.format_exc()
80        if exc_type:
81            logs = '{etype}: {value}'.format(etype=exc_type.__name__,
82                                             value=exc_msg)
83    except Exception:
84        pass
85    send_exit_event(exit_code, stacktrace=stacktrace, logs=logs)
86
87
88def send_exit_event(exit_code, stacktrace='', logs=''):
89    """Log exit event and flush all events to clearcut.
90
91    Args:
92        exit_code: An integer of exit code.
93        stacktrace: A string of stacktrace.
94        logs: A string of logs.
95    """
96    clearcut = metrics.AtestExitEvent(
97        duration=convert_duration(time.time()-get_start_time()),
98        exit_code=exit_code,
99        stacktrace=stacktrace,
100        logs=logs)
101    # pylint: disable=no-member
102    if clearcut:
103        clearcut.flush_events()
104
105
106def send_start_event(tool_name, command_line='', test_references='',
107                     cwd=None, operating_system=None):
108    """Log start event of clearcut.
109
110    Args:
111        tool_name: A string of the asuite product name.
112        command_line: A string of the user input command.
113        test_references: A string of the input tests.
114        cwd: A string of current path.
115        operating_system: A string of user's operating system.
116    """
117    if not cwd:
118        cwd = os.getcwd()
119    if not operating_system:
120        operating_system = platform.platform()
121    # Without tool_name information, asuite's clearcut client will not send
122    # event to server.
123    metrics_base.MetricsBase.tool_name = tool_name
124    get_start_time()
125    metrics.AtestStartEvent(command_line=command_line,
126                            test_references=test_references,
127                            cwd=cwd,
128                            os=operating_system)
129