main.cpp revision 682573c84b7c21dc8ce4a2375da3961147442c4a
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/TestScene.h"
18
19#include "protos/hwui.pb.h"
20#include "Properties.h"
21
22#include <getopt.h>
23#include <stdio.h>
24#include <string>
25#include <unistd.h>
26#include <unordered_map>
27#include <vector>
28
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <errno.h>
33
34using namespace android;
35using namespace android::uirenderer;
36using namespace android::uirenderer::test;
37
38static int gRepeatCount = 1;
39static std::vector<TestScene::Info> gRunTests;
40static TestScene::Options gOpts;
41
42void run(const TestScene::Info& info, const TestScene::Options& opts);
43
44static void printHelp() {
45    printf(R"(
46USAGE: hwuitest [OPTIONS] <TESTNAME>
47
48OPTIONS:
49  -c, --count=NUM      NUM loops a test should run (example, number of frames)
50  -r, --runs=NUM       Repeat the test(s) NUM times
51  -h, --help           Display this help
52  --list               List all tests
53  --wait-for-gpu       Set this to wait for the GPU before producing the
54                       next frame. Note that without locked clocks this will
55                       pathologically bad performance due to large idle time
56  --report-frametime[=weight] If set, the test will print to stdout the
57                       moving average frametime. Weight is optional, default is 10
58  --cpuset=name        Adds the test to the specified cpuset before running
59                       Not supported on all devices and needs root
60)");
61}
62
63static void listTests() {
64    printf("Tests: \n");
65    for (auto&& test : TestScene::testMap()) {
66        auto&& info = test.second;
67        const char* col1 = info.name.c_str();
68        int dlen = info.description.length();
69        const char* col2 = info.description.c_str();
70        // World's best line breaking algorithm.
71        do {
72            int toPrint = dlen;
73            if (toPrint > 50) {
74                char* found = (char*) memrchr(col2, ' ', 50);
75                if (found) {
76                    toPrint = found - col2;
77                } else {
78                    toPrint = 50;
79                }
80            }
81            printf("%-20s %.*s\n", col1, toPrint, col2);
82            col1 = "";
83            col2 += toPrint;
84            dlen -= toPrint;
85            while (*col2 == ' ') {
86                col2++; dlen--;
87            }
88        } while (dlen > 0);
89        printf("\n");
90    }
91}
92
93static void moveToCpuSet(const char* cpusetName) {
94    if (access("/dev/cpuset/tasks", F_OK)) {
95        fprintf(stderr, "don't have access to cpusets, skipping...\n");
96        return;
97    }
98    static const int BUF_SIZE = 100;
99    char buffer[BUF_SIZE];
100
101    if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) {
102        fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName);
103        return;
104    }
105    int fd = open(buffer, O_WRONLY | O_CLOEXEC);
106    if (fd == -1) {
107        fprintf(stderr, "Error opening file %d\n", errno);
108        return;
109    }
110    pid_t pid = getpid();
111
112    int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long) pid);
113    if (towrite >= BUF_SIZE) {
114        fprintf(stderr, "Buffer wasn't large enough?\n");
115    } else {
116        if (write(fd, buffer, towrite) != towrite) {
117            fprintf(stderr, "Failed to write, errno=%d", errno);
118        }
119    }
120    close(fd);
121}
122
123// For options that only exist in long-form. Anything in the
124// 0-255 range is reserved for short options (which just use their ASCII value)
125namespace LongOpts {
126enum {
127    Reserved = 255,
128    List,
129    WaitForGpu,
130    ReportFrametime,
131    CpuSet,
132};
133}
134
135static const struct option LONG_OPTIONS[] = {
136    { "frames", required_argument, nullptr, 'f' },
137    { "repeat", required_argument, nullptr, 'r' },
138    { "help", no_argument, nullptr, 'h' },
139    { "list", no_argument, nullptr, LongOpts::List },
140    { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu },
141    { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime },
142    { "cpuset", required_argument, nullptr, LongOpts::CpuSet },
143    { 0, 0, 0, 0 }
144};
145
146static const char* SHORT_OPTIONS = "c:r:h";
147
148void parseOptions(int argc, char* argv[]) {
149    int c;
150    bool error = false;
151    opterr = 0;
152
153    while (true) {
154
155        /* getopt_long stores the option index here. */
156        int option_index = 0;
157
158        c = getopt_long(argc, argv, SHORT_OPTIONS, LONG_OPTIONS, &option_index);
159
160        if (c == -1)
161            break;
162
163        switch (c) {
164        case 0:
165            // Option set a flag, don't need to do anything
166            // (although none of the current LONG_OPTIONS do this...)
167            break;
168
169        case LongOpts::List:
170            listTests();
171            exit(EXIT_SUCCESS);
172            break;
173
174        case 'c':
175            gOpts.count = atoi(optarg);
176            if (!gOpts.count) {
177                fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
178                error = true;
179            }
180            break;
181
182        case 'r':
183            gRepeatCount = atoi(optarg);
184            if (!gRepeatCount) {
185                fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
186                error = true;
187            } else {
188                gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX);
189            }
190            break;
191
192        case LongOpts::ReportFrametime:
193            if (optarg) {
194                gOpts.reportFrametimeWeight = atoi(optarg);
195                if (!gOpts.reportFrametimeWeight) {
196                    fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg);
197                    error = true;
198                }
199            } else {
200                gOpts.reportFrametimeWeight = 10;
201            }
202            break;
203
204        case LongOpts::WaitForGpu:
205            Properties::waitForGpuCompletion = true;
206            break;
207
208        case LongOpts::CpuSet:
209            if (!optarg) {
210                error = true;
211                break;
212            }
213            moveToCpuSet(optarg);
214            break;
215
216        case 'h':
217            printHelp();
218            exit(EXIT_SUCCESS);
219            break;
220
221        case '?':
222            fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]);
223            // fall-through
224        default:
225            error = true;
226            break;
227        }
228    }
229
230    if (error) {
231        fprintf(stderr, "Try 'hwuitest --help' for more information.\n");
232        exit(EXIT_FAILURE);
233    }
234
235    /* Print any remaining command line arguments (not options). */
236    if (optind < argc) {
237        do {
238            const char* test = argv[optind++];
239            auto pos = TestScene::testMap().find(test);
240            if (pos == TestScene::testMap().end()) {
241                fprintf(stderr, "Unknown test '%s'\n", test);
242                exit(EXIT_FAILURE);
243            } else {
244                gRunTests.push_back(pos->second);
245            }
246        } while (optind < argc);
247    } else {
248        gRunTests.push_back(TestScene::testMap()["shadowgrid"]);
249    }
250}
251
252int main(int argc, char* argv[]) {
253    // set defaults
254    gOpts.count = 150;
255
256    parseOptions(argc, argv);
257
258    for (int i = 0; i < gRepeatCount; i++) {
259        for (auto&& test : gRunTests) {
260            run(test, gOpts);
261        }
262    }
263    printf("Success!\n");
264    return 0;
265}
266