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