Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
S2-group
GitHub Repository: S2-group/android-runner
Path: blob/master/AndroidRunner/Plugins/android/Android.py
907 views
1
import csv
2
import os
3
import os.path as op
4
import threading
5
import time
6
import timeit
7
from collections import OrderedDict
8
from functools import reduce
9
10
from AndroidRunner import util
11
from AndroidRunner import Tests
12
from AndroidRunner.Plugins.Profiler import Profiler
13
14
15
class Android(Profiler):
16
def __init__(self, config, paths):
17
super(Android, self).__init__(config, paths)
18
self.output_dir = ''
19
self.paths = paths
20
self.profile = False
21
available_data_points = ['cpu', 'mem']
22
self.interval = float(Tests.is_integer(
23
config.get('sample_interval', 0))
24
) / 1000
25
self.data_points = config['data_points']
26
invalid_data_points = [
27
dp for dp in config['data_points'] if dp not in set(available_data_points)]
28
if invalid_data_points:
29
self.logger.warning(
30
'Invalid data points in config: {}'.format(invalid_data_points))
31
self.data_points = [dp for dp in config['data_points']
32
if dp in set(available_data_points)]
33
self.data = [['datetime'] + self.data_points]
34
self.lock = threading.Lock()
35
36
@staticmethod
37
def get_cpu_usage(device):
38
"""Get CPU usage in percentage"""
39
# return device.shell('dumpsys cpuinfo | grep TOTAL | cut -d" " -f1').strip()[:-1]
40
shell_result = device.shell('dumpsys cpuinfo | grep TOTAL')
41
shell_splitted = shell_result.split('%')[0]
42
if '.-' in shell_splitted:
43
shell_splitted = shell_splitted.replace('.-', '.')
44
return shell_splitted
45
# return device.shell('dumpsys cpuinfo | grep TOTAL').split('%')[0]
46
47
@staticmethod
48
def get_mem_usage(device, app):
49
"""Get memory usage in KB for app, if app is None system usage is used"""
50
if not app:
51
# return device.shell('dumpsys meminfo | grep Used | cut -d" " -f5').strip()[1:-1]
52
# return device.shell('dumpsys meminfo | grep Used').split()[2].strip()[1:-1].replace(",", ".")
53
# https://stackoverflow.com/questions/23175809/str-translate-gives-typeerror-translate-takes-one-argument-2-given-worked-i
54
return device.shell('dumpsys meminfo | grep Used').translate(str.maketrans('', '', '(kB,K')).split()[2]
55
else:
56
result = device.shell(
57
'dumpsys meminfo {} | grep TOTAL'.format(app))
58
if result == '':
59
result = device.shell('dumpsys meminfo {}'.format(app))
60
if 'No process found' in result:
61
raise Exception('Android Profiler: {}'.format(result))
62
return ' '.join(result.strip().split()).split()[1]
63
64
def start_profiling(self, device, **kwargs):
65
self.profile = True
66
self.data = [['datetime'] + self.data_points]
67
app = kwargs.get('app', None)
68
self.get_data(device, app)
69
70
def get_data(self, device, app):
71
"""Runs the profiling methods every self.interval seconds in a separate thread"""
72
self.lock.acquire()
73
if not self.profile:
74
self.lock.release()
75
return
76
start = timeit.default_timer()
77
device_time = device.shell('date -u')
78
row = [device_time]
79
if 'cpu' in self.data_points:
80
row.append(self.get_cpu_usage(device))
81
if 'mem' in self.data_points:
82
row.append(self.get_mem_usage(device, app))
83
self.data.append(row)
84
end = timeit.default_timer()
85
# timer results could be negative
86
interval = max(float(0), self.interval - max(0, int(end - start)))
87
self.lock.release()
88
threading.Timer(interval, self.get_data, args=(device, app)).start()
89
90
def stop_profiling(self, device, **kwargs):
91
self.lock.acquire()
92
self.profile = False
93
self.lock.release()
94
95
def collect_results(self, device):
96
filename = '{}_{}.csv'.format(
97
device.id, time.strftime('%Y.%m.%d_%H%M%S'))
98
with open(op.join(self.output_dir, filename), 'w+') as f:
99
writer = csv.writer(f)
100
for row in self.data:
101
writer.writerow(row)
102
103
def set_output(self, output_dir):
104
self.output_dir = output_dir
105
106
def dependencies(self):
107
return []
108
109
def load(self, device):
110
return
111
112
def unload(self, device):
113
return
114
115
def aggregate_subject(self):
116
filename = os.path.join(self.output_dir, 'Aggregated.csv')
117
subject_rows = list()
118
subject_rows.append(self.aggregate_android_subject(self.output_dir))
119
120
util.write_to_file(filename, subject_rows)
121
122
def aggregate_end(self, data_dir, output_file):
123
rows = self.aggregate_final(data_dir)
124
125
util.write_to_file(output_file, rows)
126
127
@staticmethod
128
def aggregate_android_subject(logs_dir):
129
def add_row(accum, new):
130
row = {k: v + float(new[k]) for k, v in list(accum.items())
131
if k not in ['Component', 'count']}
132
count = accum['count'] + 1
133
return dict(row, **{'count': count})
134
135
runs = []
136
for run_file in [f for f in os.listdir(logs_dir) if os.path.isfile(os.path.join(logs_dir, f))]:
137
with open(os.path.join(logs_dir, run_file), 'r') as run:
138
reader = csv.DictReader(run)
139
init = dict(
140
{fn: 0 for fn in reader.fieldnames if fn != 'datetime'}, **{'count': 0})
141
run_total = reduce(add_row, reader, init)
142
runs.append(
143
{k: v / run_total['count'] for k, v in list(run_total.items()) if k != 'count'})
144
runs_total = reduce(
145
lambda x, y: {k: v + y[k] for k, v in list(x.items())}, runs)
146
return OrderedDict(
147
sorted(list({'android_' + k: v / len(runs) for k, v in list(runs_total.items())}.items()), key=lambda x: x[0]))
148
149
def aggregate_final(self, data_dir):
150
rows = []
151
for device in util.list_subdir(data_dir):
152
row = OrderedDict({'device': device})
153
device_dir = os.path.join(data_dir, device)
154
for subject in util.list_subdir(device_dir):
155
row.update({'subject': subject})
156
subject_dir = os.path.join(device_dir, subject)
157
if os.path.isdir(os.path.join(subject_dir, 'android')):
158
row.update(self.aggregate_android_final(
159
os.path.join(subject_dir, 'android')))
160
rows.append(row.copy())
161
else:
162
for browser in util.list_subdir(subject_dir):
163
row.update({'browser': browser})
164
browser_dir = os.path.join(subject_dir, browser)
165
if os.path.isdir(os.path.join(browser_dir, 'android')):
166
row.update(self.aggregate_android_final(
167
os.path.join(browser_dir, 'android')))
168
rows.append(row.copy())
169
return rows
170
171
@staticmethod
172
def aggregate_android_final(logs_dir):
173
for aggregated_file in [f for f in os.listdir(logs_dir) if os.path.isfile(os.path.join(logs_dir, f))]:
174
if aggregated_file == "Aggregated.csv":
175
with open(os.path.join(logs_dir, aggregated_file), 'r') as aggregated:
176
reader = csv.DictReader(aggregated)
177
row_dict = OrderedDict()
178
for row in reader:
179
for f in reader.fieldnames:
180
row_dict.update({f: row[f]})
181
return OrderedDict(row_dict)
182