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
35 using namespace android;
36 using namespace android::uirenderer;
37 using namespace android::uirenderer::test;
38
39 static int gRepeatCount = 1;
40 static std::vector<TestScene::Info> gRunTests;
41 static TestScene::Options gOpts;
42
43 void run(const TestScene::Info& info, const TestScene::Options& opts);
44
printHelp()45 static void printHelp() {
46 printf(R"(
47 USAGE: hwuitest [OPTIONS] <TESTNAME>
48
49 OPTIONS:
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
listTests()64 static 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
moveToCpuSet(const char * cpusetName)94 static 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)
126 namespace LongOpts {
127 enum {
128 Reserved = 255,
129 List,
130 WaitForGpu,
131 ReportFrametime,
132 CpuSet,
133 };
134 }
135
136 static 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
147 static const char* SHORT_OPTIONS = "c:r:h";
148
parseOptions(int argc,char * argv[])149 void 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
main(int argc,char * argv[])253 int 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