• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "tests/common/LeakChecker.h"
18 #include "tests/common/TestScene.h"
19 
20 #include "hwui/Typeface.h"
21 #include "protos/hwui.pb.h"
22 #include "Properties.h"
23 
24 #include <benchmark/benchmark.h>
25 #include <../src/sysinfo.h>
26 #include <getopt.h>
27 #include <stdio.h>
28 #include <string>
29 #include <unistd.h>
30 #include <unordered_map>
31 #include <vector>
32 #include <pthread.h>
33 
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <errno.h>
38 
39 using namespace android;
40 using namespace android::uirenderer;
41 using namespace android::uirenderer::test;
42 
43 static int gRepeatCount = 1;
44 static std::vector<TestScene::Info> gRunTests;
45 static TestScene::Options gOpts;
46 std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter;
47 
48 void run(const TestScene::Info& info, const TestScene::Options& opts,
49         benchmark::BenchmarkReporter* reporter);
50 
printHelp()51 static void printHelp() {
52     printf(R"(
53 USAGE: hwuimacro [OPTIONS] <TESTNAME>
54 
55 OPTIONS:
56   -c, --count=NUM      NUM loops a test should run (example, number of frames)
57   -r, --runs=NUM       Repeat the test(s) NUM times
58   -h, --help           Display this help
59   --list               List all tests
60   --wait-for-gpu       Set this to wait for the GPU before producing the
61                        next frame. Note that without locked clocks this will
62                        pathologically bad performance due to large idle time
63   --report-frametime[=weight] If set, the test will print to stdout the
64                        moving average frametime. Weight is optional, default is 10
65   --cpuset=name        Adds the test to the specified cpuset before running
66                        Not supported on all devices and needs root
67   --offscreen          Render tests off device screen. This option is on by default
68   --onscreen           Render tests on device screen. By default tests
69                        are offscreen rendered
70   --benchmark_format   Set output format. Possible values are tabular, json, csv
71 )");
72 }
73 
listTests()74 static void listTests() {
75     printf("Tests: \n");
76     for (auto&& test : TestScene::testMap()) {
77         auto&& info = test.second;
78         const char* col1 = info.name.c_str();
79         int dlen = info.description.length();
80         const char* col2 = info.description.c_str();
81         // World's best line breaking algorithm.
82         do {
83             int toPrint = dlen;
84             if (toPrint > 50) {
85                 char* found = (char*) memrchr(col2, ' ', 50);
86                 if (found) {
87                     toPrint = found - col2;
88                 } else {
89                     toPrint = 50;
90                 }
91             }
92             printf("%-20s %.*s\n", col1, toPrint, col2);
93             col1 = "";
94             col2 += toPrint;
95             dlen -= toPrint;
96             while (*col2 == ' ') {
97                 col2++; dlen--;
98             }
99         } while (dlen > 0);
100         printf("\n");
101     }
102 }
103 
moveToCpuSet(const char * cpusetName)104 static void moveToCpuSet(const char* cpusetName) {
105     if (access("/dev/cpuset/tasks", F_OK)) {
106         fprintf(stderr, "don't have access to cpusets, skipping...\n");
107         return;
108     }
109     static const int BUF_SIZE = 100;
110     char buffer[BUF_SIZE];
111 
112     if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) {
113         fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName);
114         return;
115     }
116     int fd = open(buffer, O_WRONLY | O_CLOEXEC);
117     if (fd == -1) {
118         fprintf(stderr, "Error opening file %d\n", errno);
119         return;
120     }
121     pid_t pid = getpid();
122 
123     int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long) pid);
124     if (towrite >= BUF_SIZE) {
125         fprintf(stderr, "Buffer wasn't large enough?\n");
126     } else {
127         if (write(fd, buffer, towrite) != towrite) {
128             fprintf(stderr, "Failed to write, errno=%d", errno);
129         }
130     }
131     close(fd);
132 }
133 
setBenchmarkFormat(const char * format)134 static bool setBenchmarkFormat(const char* format) {
135     if (!strcmp(format, "tabular")) {
136         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
137     } else if (!strcmp(format, "json")) {
138         gBenchmarkReporter.reset(new benchmark::JSONReporter());
139     } else if (!strcmp(format, "csv")) {
140         gBenchmarkReporter.reset(new benchmark::CSVReporter());
141     } else {
142         fprintf(stderr, "Unknown format '%s'", format);
143         return false;
144     }
145     return true;
146 }
147 
148 // For options that only exist in long-form. Anything in the
149 // 0-255 range is reserved for short options (which just use their ASCII value)
150 namespace LongOpts {
151 enum {
152     Reserved = 255,
153     List,
154     WaitForGpu,
155     ReportFrametime,
156     CpuSet,
157     BenchmarkFormat,
158     Onscreen,
159     Offscreen,
160 };
161 }
162 
163 static const struct option LONG_OPTIONS[] = {
164     { "frames", required_argument, nullptr, 'f' },
165     { "repeat", required_argument, nullptr, 'r' },
166     { "help", no_argument, nullptr, 'h' },
167     { "list", no_argument, nullptr, LongOpts::List },
168     { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu },
169     { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime },
170     { "cpuset", required_argument, nullptr, LongOpts::CpuSet },
171     { "benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat },
172     { "onscreen", no_argument, nullptr, LongOpts::Onscreen },
173     { "offscreen", no_argument, nullptr, LongOpts::Offscreen },
174     { 0, 0, 0, 0 }
175 };
176 
177 static const char* SHORT_OPTIONS = "c:r:h";
178 
parseOptions(int argc,char * argv[])179 void parseOptions(int argc, char* argv[]) {
180     int c;
181     bool error = false;
182     opterr = 0;
183 
184     while (true) {
185 
186         /* getopt_long stores the option index here. */
187         int option_index = 0;
188 
189         c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
190 
191         if (c == -1)
192             break;
193 
194         switch (c) {
195         case 0:
196             // Option set a flag, don't need to do anything
197             // (although none of the current LONG_OPTIONS do this...)
198             break;
199 
200         case LongOpts::List:
201             listTests();
202             exit(EXIT_SUCCESS);
203             break;
204 
205         case 'c':
206             gOpts.count = atoi(optarg);
207             if (!gOpts.count) {
208                 fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
209                 error = true;
210             }
211             break;
212 
213         case 'r':
214             gRepeatCount = atoi(optarg);
215             if (!gRepeatCount) {
216                 fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
217                 error = true;
218             } else {
219                 gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX);
220             }
221             break;
222 
223         case LongOpts::ReportFrametime:
224             if (optarg) {
225                 gOpts.reportFrametimeWeight = atoi(optarg);
226                 if (!gOpts.reportFrametimeWeight) {
227                     fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg);
228                     error = true;
229                 }
230             } else {
231                 gOpts.reportFrametimeWeight = 10;
232             }
233             break;
234 
235         case LongOpts::WaitForGpu:
236             Properties::waitForGpuCompletion = true;
237             break;
238 
239         case LongOpts::CpuSet:
240             if (!optarg) {
241                 error = true;
242                 break;
243             }
244             moveToCpuSet(optarg);
245             break;
246 
247         case LongOpts::BenchmarkFormat:
248             if (!optarg) {
249                 error = true;
250                 break;
251             }
252             if (!setBenchmarkFormat(optarg)) {
253                 error = true;
254             }
255             break;
256 
257         case LongOpts::Onscreen:
258             gOpts.renderOffscreen = false;
259             break;
260 
261         case LongOpts::Offscreen:
262             gOpts.renderOffscreen = true;
263             break;
264 
265         case 'h':
266             printHelp();
267             exit(EXIT_SUCCESS);
268             break;
269 
270         case '?':
271             fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
272             // fall-through
273         default:
274             error = true;
275             break;
276         }
277     }
278 
279     if (error) {
280         fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
281         exit(EXIT_FAILURE);
282     }
283 
284     /* Print any remaining command line arguments (not options). */
285     if (optind < argc) {
286         do {
287             const char* test = argv[optind++];
288             auto pos = TestScene::testMap().find(test);
289             if (pos == TestScene::testMap().end()) {
290                 fprintf(stderr, "Unknown test '%s'\n", test);
291                 exit(EXIT_FAILURE);
292             } else {
293                 gRunTests.push_back(pos->second);
294             }
295         } while (optind < argc);
296     } else {
297         for (auto& iter : TestScene::testMap()) {
298             gRunTests.push_back(iter.second);
299         }
300     }
301 }
302 
main(int argc,char * argv[])303 int main(int argc, char* argv[]) {
304     // set defaults
305     gOpts.count = 150;
306 
307     Typeface::setRobotoTypefaceForTest();
308 
309     parseOptions(argc, argv);
310     if (!gBenchmarkReporter && gOpts.renderOffscreen) {
311         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
312     }
313 
314     if (gBenchmarkReporter) {
315         size_t name_field_width = 10;
316         for (auto&& test : gRunTests) {
317             name_field_width = std::max<size_t>(name_field_width, test.name.size());
318         }
319         // _50th, _90th, etc...
320         name_field_width += 5;
321 
322         benchmark::BenchmarkReporter::Context context;
323         context.num_cpus = benchmark::NumCPUs();
324         context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f;
325         context.cpu_scaling_enabled = benchmark::CpuScalingEnabled();
326         context.name_field_width = name_field_width;
327         gBenchmarkReporter->ReportContext(context);
328     }
329 
330     for (int i = 0; i < gRepeatCount; i++) {
331         for (auto&& test : gRunTests) {
332             run(test, gOpts, gBenchmarkReporter.get());
333         }
334     }
335 
336     if (gBenchmarkReporter) {
337         gBenchmarkReporter->Finalize();
338     }
339 
340     LeakChecker::checkForLeaks();
341     return 0;
342 }
343