xref: /aosp_15_r20/external/cronet/testing/scripts/host_info.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1#!/usr/bin/env python
2# Copyright 2015 The Chromium Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import json
7import multiprocessing
8import os
9import platform
10import sys
11
12# Add src/testing/ into sys.path for importing common without pylint errors.
13sys.path.append(
14    os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
15from scripts import common
16
17
18def is_linux():
19  return sys.platform.startswith('linux')
20
21
22def get_free_disk_space(failures):
23  """Returns the amount of free space on the current disk, in GiB.
24
25  Returns:
26    The amount of free space on the current disk, measured in GiB.
27  """
28  if os.name == 'posix':
29    # Stat the current path for info on the current disk.
30    stat_result = os.statvfs('.')
31    # Multiply block size by number of free blocks, express in GiB.
32    return stat_result.f_frsize * stat_result.f_bavail / (1024.0 ** 3)
33
34  failures.append('get_free_disk_space: OS %s not supported.' % os.name)
35  return 0
36
37
38def get_num_cpus(failures):
39  """Returns the number of logical CPUs on this machine.
40
41  Returns:
42    The number of logical CPUs on this machine, or 'unknown' if indeterminate.
43  """
44  try:
45    return multiprocessing.cpu_count()
46  except NotImplementedError:
47    failures.append('get_num_cpus')
48    return 'unknown'
49
50
51def get_device_info(args, failures):
52  """Parses the device info for each attached device, and returns a summary
53  of the device info and any mismatches.
54
55  Returns:
56    A dict indicating the result.
57  """
58  if not is_linux():
59    return {}
60
61  with common.temporary_file() as tempfile_path:
62    test_cmd = [
63        sys.executable,
64        os.path.join(args.paths['checkout'],
65                     'third_party',
66                     'catapult',
67                     'devil',
68                     'devil',
69                     'android',
70                     'tools',
71                     'device_status.py'),
72        '--json-output', tempfile_path,
73        '--denylist-file', os.path.join(
74              args.paths['checkout'], 'out', 'bad_devices.json')
75    ]
76    if args.args:
77      test_cmd.extend(args.args)
78
79    rc = common.run_command(test_cmd)
80    if rc:
81      failures.append('device_status')
82      return {}
83
84    with open(tempfile_path, 'r') as src:
85      device_info = json.load(src)
86
87  results = {}
88  results['devices'] = sorted(v['serial'] for v in device_info)
89
90  details = [
91      v['ro.build.fingerprint'] for v in device_info if not v['denylisted']]
92
93  def unique_build_details(index):
94    return sorted(list({v.split(':')[index] for v in details}))
95
96  parsed_details = {
97    'device_names': unique_build_details(0),
98    'build_versions': unique_build_details(1),
99    'build_types': unique_build_details(2),
100  }
101
102  for k, v in parsed_details.items():
103    if len(v) == 1:
104      results[k] = v[0]
105    else:
106      results[k] = 'MISMATCH'
107      results['%s_list' % k] = v
108      failures.append(k)
109
110  for v in device_info:
111    if v['denylisted']:
112      failures.append('Device %s denylisted' % v['serial'])
113
114  return results
115
116
117def main_run(args):
118  failures = []
119  host_info = {}
120  host_info['os_system'] = platform.system()
121  host_info['os_release'] = platform.release()
122
123  host_info['processor'] = platform.processor()
124  host_info['num_cpus'] = get_num_cpus(failures)
125  host_info['free_disk_space'] = get_free_disk_space(failures)
126
127  host_info['python_version'] = platform.python_version()
128  host_info['python_path'] = sys.executable
129
130  host_info['devices'] = get_device_info(args, failures)
131
132  json.dump({
133      'valid': True,
134      'failures': failures,
135      '_host_info': host_info,
136  }, args.output)
137
138  if len(failures) != 0:
139    return common.INFRA_FAILURE_EXIT_CODE
140  return 0
141
142
143def main_compile_targets(args):
144  json.dump([], args.output)
145
146
147if __name__ == '__main__':
148  funcs = {
149    'run': main_run,
150    'compile_targets': main_compile_targets,
151  }
152  sys.exit(common.run_script(sys.argv[1:], funcs))
153