main.cpp revision b6e20139755afbb4968ec0ac71182c179ea33ac0
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
39using namespace android;
40using namespace android::uirenderer;
41using namespace android::uirenderer::test;
42
43static int gRepeatCount = 1;
44static std::vector<TestScene::Info> gRunTests;
45static TestScene::Options gOpts;
46std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter;
47
48void run(const TestScene::Info& info, const TestScene::Options& opts,
49        benchmark::BenchmarkReporter* reporter);
50
51static void printHelp() {
52    printf(R"(
53USAGE: hwuimacro [OPTIONS] <TESTNAME>
54
55OPTIONS:
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
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++; dlen--;
98            }
99        } while (dlen > 0);
100        printf("\n");
101    }
102}
103
104static 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
134static 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)
150namespace LongOpts {
151enum {
152    Reserved = 255,
153    List,
154    WaitForGpu,
155    ReportFrametime,
156    CpuSet,
157    BenchmarkFormat,
158    Onscreen,
159    Offscreen,
160};
161}
162
163static 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
177static const char* SHORT_OPTIONS = "c:r:h";
178
179void 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
303int 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