• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 atest.metrics import metrics
26from atest.metrics import metrics_base
27
28CONTENT_LICENSES_URL = 'https://source.android.com/setup/start/licenses'
29CONTRIBUTOR_AGREEMENT_URL = {
30    'INTERNAL': 'https://cla.developers.google.com/',
31    'EXTERNAL': 'https://opensource.google.com/docs/cla/'
32}
33PRIVACY_POLICY_URL = 'https://policies.google.com/privacy'
34TERMS_SERVICE_URL = 'https://policies.google.com/terms'
35
36
37def static_var(varname, value):
38    """Decorator to cache static variable."""
39    def fun_var_decorate(func):
40        """Set the static variable in a function."""
41        setattr(func, varname, value)
42        return func
43    return fun_var_decorate
44
45
46@static_var("start_time", [])
47def get_start_time():
48    """Get start time.
49
50    Return:
51        start_time: Start time in seconds. Return cached start_time if exists,
52        time.time() otherwise.
53    """
54    if not get_start_time.start_time:
55        get_start_time.start_time = time.time()
56    return get_start_time.start_time
57
58
59def convert_duration(diff_time_sec):
60    """Compute duration from time difference.
61
62    A Duration represents a signed, fixed-length span of time represented
63    as a count of seconds and fractions of seconds at nanosecond
64    resolution.
65
66    Args:
67        diff_time_sec: The time in seconds as a floating point number.
68
69    Returns:
70        A dict of Duration.
71    """
72    seconds = int(diff_time_sec)
73    nanos = int((diff_time_sec - seconds)*10**9)
74    return {'seconds': seconds, 'nanos': nanos}
75
76
77# pylint: disable=broad-except
78def handle_exc_and_send_exit_event(exit_code):
79    """handle exceptions and send exit event.
80
81    Args:
82        exit_code: An integer of exit code.
83    """
84    stacktrace = logs = ''
85    try:
86        exc_type, exc_msg, _ = sys.exc_info()
87        stacktrace = traceback.format_exc()
88        if exc_type:
89            logs = '{etype}: {value}'.format(etype=exc_type.__name__,
90                                             value=exc_msg)
91    except Exception:
92        pass
93    send_exit_event(exit_code, stacktrace=stacktrace, logs=logs)
94
95
96def send_exit_event(exit_code, stacktrace='', logs=''):
97    """Log exit event and flush all events to clearcut.
98
99    Args:
100        exit_code: An integer of exit code.
101        stacktrace: A string of stacktrace.
102        logs: A string of logs.
103    """
104    clearcut = metrics.AtestExitEvent(
105        duration=convert_duration(time.time()-get_start_time()),
106        exit_code=exit_code,
107        stacktrace=stacktrace,
108        logs=str(logs))
109    # pylint: disable=no-member
110    if clearcut:
111        clearcut.flush_events()
112
113
114def send_start_event(tool_name, command_line='', test_references='',
115                     cwd=None, operating_system=None):
116    """Log start event of clearcut.
117
118    Args:
119        tool_name: A string of the asuite product name.
120        command_line: A string of the user input command.
121        test_references: A string of the input tests.
122        cwd: A string of current path.
123        operating_system: A string of user's operating system.
124    """
125    if not cwd:
126        cwd = os.getcwd()
127    if not operating_system:
128        operating_system = platform.platform()
129    # Without tool_name information, asuite's clearcut client will not send
130    # event to server.
131    metrics_base.MetricsBase.tool_name = tool_name
132    get_start_time()
133    metrics.AtestStartEvent(command_line=command_line,
134                            test_references=test_references,
135                            cwd=cwd,
136                            os=operating_system)
137
138
139def print_data_collection_notice(colorful=True):
140    """Print the data collection notice."""
141    red = '31m'
142    green = '32m'
143    start = '\033[1;'
144    end = '\033[0m'
145    delimiter = '=' * 18
146    anonymous = ''
147    user_type = 'INTERNAL'
148    if metrics_base.get_user_type() == metrics_base.EXTERNAL_USER:
149        anonymous = ' anonymous'
150        user_type = 'EXTERNAL'
151    notice = ('  We collect%s usage statistics in accordance with our Content '
152              'Licenses (%s), Contributor License Agreement (%s), Privacy '
153              'Policy (%s) and Terms of Service (%s).'
154             ) % (anonymous,
155                  CONTENT_LICENSES_URL,
156                  CONTRIBUTOR_AGREEMENT_URL[user_type],
157                  PRIVACY_POLICY_URL,
158                  TERMS_SERVICE_URL)
159    if colorful:
160        print(f'\n{delimiter}\n{start}{red}Notice:{end}')
161        print(f'{start}{green} {notice}{end}\n{delimiter}\n')
162    else:
163        print(f'\n{delimiter}\nNotice:')
164        print(f' {notice}\n{delimiter}\n')
165