• 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"""Utility functions for metrics."""
16
17import os
18import platform
19import sys
20import time
21import traceback
22
23from atest.metrics import metrics
24from atest.metrics import metrics_base
25
26CONTENT_LICENSES_URL = 'https://source.android.com/setup/start/licenses'
27CONTRIBUTOR_AGREEMENT_URL = {
28    'INTERNAL': 'https://cla.developers.google.com/',
29    'EXTERNAL': 'https://opensource.google.com/docs/cla/',
30}
31PRIVACY_POLICY_URL = 'https://policies.google.com/privacy'
32TERMS_SERVICE_URL = 'https://policies.google.com/terms'
33
34
35def static_var(varname, value):
36  """Decorator to cache static variable."""
37
38  def fun_var_decorate(func):
39    """Set the static variable in a function."""
40    setattr(func, varname, value)
41    return func
42
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__, value=exc_msg)
90  except Exception:
91    pass
92  send_exit_event(exit_code, stacktrace=stacktrace, logs=logs)
93
94
95def send_exit_event(exit_code, stacktrace='', logs=''):
96  """Log exit event and flush all events to clearcut.
97
98  Args:
99      exit_code: An integer of exit code.
100      stacktrace: A string of stacktrace.
101      logs: A string of logs.
102  """
103  clearcut = metrics.AtestExitEvent(
104      duration=convert_duration(time.time() - get_start_time()),
105      exit_code=exit_code,
106      stacktrace=stacktrace,
107      logs=str(logs),
108  )
109  # pylint: disable=no-member
110  if clearcut:
111    clearcut.flush_events()
112
113
114def send_start_event(
115    tool_name,
116    command_line='',
117    test_references='',
118    cwd=None,
119    operating_system=None,
120):
121  """Log start event of clearcut.
122
123  Args:
124      tool_name: A string of the asuite product name.
125      command_line: A string of the user input command.
126      test_references: A string of the input tests.
127      cwd: A string of current path.
128      operating_system: A string of user's operating system.
129  """
130  if not cwd:
131    cwd = os.getcwd()
132  if not operating_system:
133    operating_system = platform.platform()
134  # Without tool_name information, asuite's clearcut client will not send
135  # event to server.
136  metrics_base.MetricsBase.tool_name = tool_name
137  get_start_time()
138  metrics.AtestStartEvent(
139      command_line=command_line,
140      test_references=test_references,
141      cwd=cwd,
142      os=operating_system,
143  )
144
145
146def print_data_collection_notice(colorful=True):
147  """Print the data collection notice."""
148  # Do not print notice for external users as we are not collecting any external
149  # data.
150  if metrics_base.get_user_type() == metrics_base.EXTERNAL_USER:
151    return
152
153  red = '31m'
154  green = '32m'
155  start = '\033[1;'
156  end = '\033[0m'
157  delimiter = '=' * 18
158  notice = (
159      'We collect usage statistics (including usernames) in accordance with our '
160      'Content Licenses (%s), Contributor License Agreement (%s), Privacy '
161      'Policy (%s) and Terms of Service (%s).'
162  ) % (
163      CONTENT_LICENSES_URL,
164      CONTRIBUTOR_AGREEMENT_URL['INTERNAL'],
165      PRIVACY_POLICY_URL,
166      TERMS_SERVICE_URL,
167  )
168  if colorful:
169    print(f'\n{delimiter}\n{start}{red}Notice:{end}')
170    print(f'{start}{green} {notice}{end}\n{delimiter}\n')
171  else:
172    print(f'\n{delimiter}\nNotice:')
173    print(f' {notice}\n{delimiter}\n')
174