1#!/usr/bin/env python3
2#
3# Copyright (C) 2018 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import os
19import statistics
20import subprocess
21import sys
22import tempfile
23import time
24
25# Make sure environment is setup, otherwise "adb" module is not available.
26if os.getenv("ANDROID_BUILD_TOP") is None:
27    print("Run source/lunch before running " + sys.argv[0])
28    sys.exit()
29
30import adb
31
32def lock_min(device):
33    device.shell_nocheck(["""
34        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
35            echo userspace > $x/scaling_governor
36            cat $x/scaling_min_freq > $x/scaling_setspeed
37        done
38    """])
39
40def lock_max(device):
41    device.shell_nocheck(["""
42        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
43            echo userspace > $x/scaling_governor
44            cat $x/scaling_max_freq > $x/scaling_setspeed
45        done
46    """])
47
48def unlock(device):
49    device.shell_nocheck(["""
50        for x in /sys/devices/system/cpu/cpu?/cpufreq; do
51            echo ondemand > $x/scaling_governor
52            echo sched > $x/scaling_governor
53            echo schedutil > $x/scaling_governor
54        done
55    """])
56
57def harmonic_mean(xs):
58    return 1.0 / statistics.mean([1.0 / x for x in xs])
59
60def analyze(name, speeds):
61    median = statistics.median(speeds)
62    mean = harmonic_mean(speeds)
63    stddev = statistics.stdev(speeds)
64    msg = "%s: %d runs: median %.2f MiB/s, mean %.2f MiB/s, stddev: %.2f MiB/s"
65    print(msg % (name, len(speeds), median, mean, stddev))
66
67def benchmark_sink(device=None, size_mb=100):
68    if device == None:
69        device = adb.get_device()
70
71    speeds = list()
72    cmd = device.adb_cmd + ["raw", "sink:%d" % (size_mb * 1024 * 1024)]
73
74    with tempfile.TemporaryFile() as tmpfile:
75        tmpfile.truncate(size_mb * 1024 * 1024)
76
77        for _ in range(0, 10):
78            tmpfile.seek(0)
79            begin = time.time()
80            subprocess.check_call(cmd, stdin=tmpfile)
81            end = time.time()
82            speeds.append(size_mb / float(end - begin))
83
84    analyze("sink %dMiB" % size_mb, speeds)
85
86def benchmark_source(device=None, size_mb=100):
87    if device == None:
88        device = adb.get_device()
89
90    speeds = list()
91    cmd = device.adb_cmd + ["raw", "source:%d" % (size_mb * 1024 * 1024)]
92
93    with open(os.devnull, 'w') as devnull:
94        for _ in range(0, 10):
95            begin = time.time()
96            subprocess.check_call(cmd, stdout=devnull)
97            end = time.time()
98            speeds.append(size_mb / float(end - begin))
99
100    analyze("source %dMiB" % size_mb, speeds)
101
102def benchmark_push(device=None, file_size_mb=100):
103    if device == None:
104        device = adb.get_device()
105
106    remote_path = "/dev/null"
107    local_path = "/tmp/adb_benchmark_temp"
108
109    with open(local_path, "wb") as f:
110        f.truncate(file_size_mb * 1024 * 1024)
111
112    speeds = list()
113    for _ in range(0, 10):
114        begin = time.time()
115        device.push(local=local_path, remote=remote_path)
116        end = time.time()
117        speeds.append(file_size_mb / float(end - begin))
118
119    analyze("push %dMiB" % file_size_mb, speeds)
120
121def benchmark_pull(device=None, file_size_mb=100):
122    if device == None:
123        device = adb.get_device()
124
125    remote_path = "/data/local/tmp/adb_benchmark_temp"
126    local_path = "/tmp/adb_benchmark_temp"
127
128    device.shell(["dd", "if=/dev/zero", "of=" + remote_path, "bs=1m",
129                  "count=" + str(file_size_mb)])
130    speeds = list()
131    for _ in range(0, 10):
132        begin = time.time()
133        device.pull(remote=remote_path, local=local_path)
134        end = time.time()
135        speeds.append(file_size_mb / float(end - begin))
136
137    analyze("pull %dMiB" % file_size_mb, speeds)
138
139def benchmark_shell(device=None, file_size_mb=100):
140    if device == None:
141        device = adb.get_device()
142
143    speeds = list()
144    for _ in range(0, 10):
145        begin = time.time()
146        device.shell(["dd", "if=/dev/zero", "bs=1m",
147                      "count=" + str(file_size_mb)])
148        end = time.time()
149        speeds.append(file_size_mb / float(end - begin))
150
151    analyze("shell %dMiB" % file_size_mb, speeds)
152
153def main():
154    device = adb.get_device()
155    unlock(device)
156    benchmark_sink(device)
157    benchmark_source(device)
158    benchmark_push(device)
159    benchmark_pull(device)
160
161if __name__ == "__main__":
162    main()
163