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