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