• 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 "Properties.h"
21 #include "hwui/Typeface.h"
22 #include "HardwareBitmapUploader.h"
23 #include "renderthread/RenderProxy.h"
24 
25 #include <benchmark/benchmark.h>
26 #include <fnmatch.h>
27 #include <getopt.h>
28 #include <pthread.h>
29 #include <stdio.h>
30 #include <unistd.h>
31 #include <string>
32 #include <unordered_map>
33 #include <vector>
34 
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <sys/types.h>
39 
40 using namespace android;
41 using namespace android::uirenderer;
42 using namespace android::uirenderer::test;
43 
44 static std::vector<TestScene::Info> gRunTests;
45 static TestScene::Options gOpts;
46 static bool gRunLeakCheck = true;
47 std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter;
48 
49 void run(const TestScene::Info& info, const TestScene::Options& opts,
50          benchmark::BenchmarkReporter* reporter);
51 
printHelp()52 static void printHelp() {
53     printf(R"(
54 USAGE: hwuimacro [OPTIONS] <TESTNAME>
55 
56 OPTIONS:
57   -c, --count=NUM      NUM loops a test should run (example, number of frames)
58   -r, --runs=NUM       Repeat the test(s) NUM times
59   -h, --help           Display this help
60   --list               List all tests
61   --wait-for-gpu       Set this to wait for the GPU before producing the
62                        next frame. Note that without locked clocks this will
63                        pathologically bad performance due to large idle time
64   --report-frametime[=weight] If set, the test will print to stdout the
65                        moving average frametime. Weight is optional, default is 10
66   --cpuset=name        Adds the test to the specified cpuset before running
67                        Not supported on all devices and needs root
68   --offscreen          Render tests off device screen. This option is on by default
69   --onscreen           Render tests on device screen. By default tests
70                        are offscreen rendered
71   --benchmark_format   Set output format. Possible values are tabular, json, csv
72   --renderer=TYPE      Sets the render pipeline to use. May be skiagl or skiavk
73   --skip-leak-check    Skips the memory leak check
74   --report-gpu-memory[=verbose]  Dumps the GPU memory usage after each test run
75 )");
76 }
77 
listTests()78 static void listTests() {
79     printf("Tests: \n");
80     for (auto&& test : TestScene::testMap()) {
81         auto&& info = test.second;
82         const char* col1 = info.name.c_str();
83         int dlen = info.description.length();
84         const char* col2 = info.description.c_str();
85         // World's best line breaking algorithm.
86         do {
87             int toPrint = dlen;
88             if (toPrint > 50) {
89                 char* found = (char*)memrchr(col2, ' ', 50);
90                 if (found) {
91                     toPrint = found - col2;
92                 } else {
93                     toPrint = 50;
94                 }
95             }
96             printf("%-20s %.*s\n", col1, toPrint, col2);
97             col1 = "";
98             col2 += toPrint;
99             dlen -= toPrint;
100             while (*col2 == ' ') {
101                 col2++;
102                 dlen--;
103             }
104         } while (dlen > 0);
105         printf("\n");
106     }
107 }
108 
moveToCpuSet(const char * cpusetName)109 static void moveToCpuSet(const char* cpusetName) {
110     if (access("/dev/cpuset/tasks", F_OK)) {
111         fprintf(stderr, "don't have access to cpusets, skipping...\n");
112         return;
113     }
114     static const int BUF_SIZE = 100;
115     char buffer[BUF_SIZE];
116 
117     if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) {
118         fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName);
119         return;
120     }
121     int fd = open(buffer, O_WRONLY | O_CLOEXEC);
122     if (fd == -1) {
123         fprintf(stderr, "Error opening file %d\n", errno);
124         return;
125     }
126     pid_t pid = getpid();
127 
128     int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long)pid);
129     if (towrite >= BUF_SIZE) {
130         fprintf(stderr, "Buffer wasn't large enough?\n");
131     } else {
132         if (write(fd, buffer, towrite) != towrite) {
133             fprintf(stderr, "Failed to write, errno=%d", errno);
134         }
135     }
136     close(fd);
137 }
138 
setBenchmarkFormat(const char * format)139 static bool setBenchmarkFormat(const char* format) {
140     if (!strcmp(format, "tabular")) {
141         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
142     } else if (!strcmp(format, "json")) {
143         gBenchmarkReporter.reset(new benchmark::JSONReporter());
144     } else {
145         fprintf(stderr, "Unknown format '%s'\n", format);
146         return false;
147     }
148     return true;
149 }
150 
setRenderer(const char * renderer)151 static bool setRenderer(const char* renderer) {
152     if (!strcmp(renderer, "skiagl")) {
153         Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL);
154     } else if (!strcmp(renderer, "skiavk")) {
155         Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan);
156     } else {
157         fprintf(stderr, "Unknown format '%s'\n", renderer);
158         return false;
159     }
160     return true;
161 }
162 
163 // For options that only exist in long-form. Anything in the
164 // 0-255 range is reserved for short options (which just use their ASCII value)
165 namespace LongOpts {
166 enum {
167     Reserved = 255,
168     List,
169     WaitForGpu,
170     ReportFrametime,
171     CpuSet,
172     BenchmarkFormat,
173     Onscreen,
174     Offscreen,
175     Renderer,
176     SkipLeakCheck,
177     ReportGpuMemory,
178 };
179 }
180 
181 static const struct option LONG_OPTIONS[] = {
182         {"frames", required_argument, nullptr, 'f'},
183         {"repeat", required_argument, nullptr, 'r'},
184         {"help", no_argument, nullptr, 'h'},
185         {"list", no_argument, nullptr, LongOpts::List},
186         {"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu},
187         {"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime},
188         {"cpuset", required_argument, nullptr, LongOpts::CpuSet},
189         {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat},
190         {"onscreen", no_argument, nullptr, LongOpts::Onscreen},
191         {"offscreen", no_argument, nullptr, LongOpts::Offscreen},
192         {"renderer", required_argument, nullptr, LongOpts::Renderer},
193         {"skip-leak-check", no_argument, nullptr, LongOpts::SkipLeakCheck},
194         {"report-gpu-memory", optional_argument, nullptr, LongOpts::ReportGpuMemory},
195         {0, 0, 0, 0}};
196 
197 static const char* SHORT_OPTIONS = "c:r:h";
198 
parseOptions(int argc,char * argv[])199 void parseOptions(int argc, char* argv[]) {
200     int c;
201     bool error = false;
202     opterr = 0;
203 
204     while (true) {
205         /* getopt_long stores the option index here. */
206         int option_index = 0;
207 
208         c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
209 
210         if (c == -1) break;
211 
212         switch (c) {
213             case 0:
214                 // Option set a flag, don't need to do anything
215                 // (although none of the current LONG_OPTIONS do this...)
216                 break;
217 
218             case LongOpts::List:
219                 listTests();
220                 exit(EXIT_SUCCESS);
221                 break;
222 
223             case 'c':
224                 gOpts.frameCount = atoi(optarg);
225                 if (!gOpts.frameCount) {
226                     fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
227                     error = true;
228                 }
229                 break;
230 
231             case 'r':
232                 gOpts.repeatCount = atoi(optarg);
233                 if (!gOpts.repeatCount) {
234                     fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
235                     error = true;
236                 } else {
237                     gOpts.repeatCount = (gOpts.repeatCount > 0 ? gOpts.repeatCount : INT_MAX);
238                 }
239                 break;
240 
241             case LongOpts::ReportFrametime:
242                 if (optarg) {
243                     gOpts.reportFrametimeWeight = atoi(optarg);
244                     if (!gOpts.reportFrametimeWeight) {
245                         fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg);
246                         error = true;
247                     }
248                 } else {
249                     gOpts.reportFrametimeWeight = 10;
250                 }
251                 break;
252 
253             case LongOpts::WaitForGpu:
254                 Properties::waitForGpuCompletion = true;
255                 break;
256 
257             case LongOpts::CpuSet:
258                 if (!optarg) {
259                     error = true;
260                     break;
261                 }
262                 moveToCpuSet(optarg);
263                 break;
264 
265             case LongOpts::BenchmarkFormat:
266                 if (!optarg) {
267                     error = true;
268                     break;
269                 }
270                 if (!setBenchmarkFormat(optarg)) {
271                     error = true;
272                 }
273                 break;
274 
275             case LongOpts::Renderer:
276                 if (!optarg) {
277                     error = true;
278                     break;
279                 }
280                 if (!setRenderer(optarg)) {
281                     error = true;
282                 }
283                 break;
284 
285             case LongOpts::Onscreen:
286                 gOpts.renderOffscreen = false;
287                 break;
288 
289             case LongOpts::Offscreen:
290                 gOpts.renderOffscreen = true;
291                 break;
292 
293             case LongOpts::SkipLeakCheck:
294                 gRunLeakCheck = false;
295                 break;
296 
297             case LongOpts::ReportGpuMemory:
298                 gOpts.reportGpuMemoryUsage = true;
299                 if (optarg) {
300                     if (!strcmp("verbose", optarg)) {
301                         gOpts.reportGpuMemoryUsageVerbose = true;
302                     } else {
303                         fprintf(stderr, "Invalid report gpu memory option '%s'\n", optarg);
304                         error = true;
305                     }
306                 }
307                 break;
308 
309             case 'h':
310                 printHelp();
311                 exit(EXIT_SUCCESS);
312                 break;
313 
314             case '?':
315                 fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
316                 [[fallthrough]];
317             default:
318                 error = true;
319                 break;
320         }
321     }
322 
323     if (error) {
324         fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]);
325         exit(EXIT_FAILURE);
326     }
327 
328     /* Print any remaining command line arguments (not options). */
329     if (optind < argc) {
330         do {
331             const char* test = argv[optind++];
332             if (strchr(test, '*')) {
333                 // Glob match
334                 for (auto& iter : TestScene::testMap()) {
335                     if (!fnmatch(test, iter.first.c_str(), 0)) {
336                         gRunTests.push_back(iter.second);
337                     }
338                 }
339             } else {
340                 auto pos = TestScene::testMap().find(test);
341                 if (pos == TestScene::testMap().end()) {
342                     fprintf(stderr, "Unknown test '%s'\n", test);
343                     exit(EXIT_FAILURE);
344                 } else {
345                     gRunTests.push_back(pos->second);
346                 }
347             }
348         } while (optind < argc);
349     } else {
350         for (auto& iter : TestScene::testMap()) {
351             gRunTests.push_back(iter.second);
352         }
353     }
354 }
355 
main(int argc,char * argv[])356 int main(int argc, char* argv[]) {
357     Typeface::setRobotoTypefaceForTest();
358 
359     parseOptions(argc, argv);
360     if (!gBenchmarkReporter && gOpts.renderOffscreen) {
361         gBenchmarkReporter.reset(new benchmark::ConsoleReporter());
362     }
363 
364     if (gBenchmarkReporter) {
365         size_t name_field_width = 10;
366         for (auto&& test : gRunTests) {
367             name_field_width = std::max<size_t>(name_field_width, test.name.size());
368         }
369         // _50th, _90th, etc...
370         name_field_width += 5;
371 
372         benchmark::BenchmarkReporter::Context context;
373         context.name_field_width = name_field_width;
374         gBenchmarkReporter->ReportContext(context);
375     }
376 
377     for (auto&& test : gRunTests) {
378         run(test, gOpts, gBenchmarkReporter.get());
379     }
380 
381     if (gBenchmarkReporter) {
382         gBenchmarkReporter->Finalize();
383     }
384 
385     renderthread::RenderProxy::trimMemory(100);
386     HardwareBitmapUploader::terminate();
387 
388     if (gRunLeakCheck) {
389         LeakChecker::checkForLeaks();
390     }
391     return 0;
392 }
393