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 "Properties.h" 21 #include "hwui/Typeface.h" 22 #include "HardwareBitmapUploader.h" 23 #include "renderthread/RenderProxy.h" 24 25 #include <benchmark/benchmark.h> 26 #include <getopt.h> 27 #include <pthread.h> 28 #include <stdio.h> 29 #include <unistd.h> 30 #include <string> 31 #include <unordered_map> 32 #include <vector> 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <sys/stat.h> 37 #include <sys/types.h> 38 39 using namespace android; 40 using namespace android::uirenderer; 41 using namespace android::uirenderer::test; 42 43 static int gRepeatCount = 1; 44 static std::vector<TestScene::Info> gRunTests; 45 static TestScene::Options gOpts; 46 std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; 47 48 void run(const TestScene::Info& info, const TestScene::Options& opts, 49 benchmark::BenchmarkReporter* reporter); 50 51 static void printHelp() { 52 printf(R"( 53 USAGE: hwuimacro [OPTIONS] <TESTNAME> 54 55 OPTIONS: 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 --renderer=TYPE Sets the render pipeline to use. May be skiagl or skiavk 72 --render-ahead=NUM Sets how far to render-ahead. Must be 0 (default), 1, or 2. 73 )"); 74 } 75 76 static void listTests() { 77 printf("Tests: \n"); 78 for (auto&& test : TestScene::testMap()) { 79 auto&& info = test.second; 80 const char* col1 = info.name.c_str(); 81 int dlen = info.description.length(); 82 const char* col2 = info.description.c_str(); 83 // World's best line breaking algorithm. 84 do { 85 int toPrint = dlen; 86 if (toPrint > 50) { 87 char* found = (char*)memrchr(col2, ' ', 50); 88 if (found) { 89 toPrint = found - col2; 90 } else { 91 toPrint = 50; 92 } 93 } 94 printf("%-20s %.*s\n", col1, toPrint, col2); 95 col1 = ""; 96 col2 += toPrint; 97 dlen -= toPrint; 98 while (*col2 == ' ') { 99 col2++; 100 dlen--; 101 } 102 } while (dlen > 0); 103 printf("\n"); 104 } 105 } 106 107 static void moveToCpuSet(const char* cpusetName) { 108 if (access("/dev/cpuset/tasks", F_OK)) { 109 fprintf(stderr, "don't have access to cpusets, skipping...\n"); 110 return; 111 } 112 static const int BUF_SIZE = 100; 113 char buffer[BUF_SIZE]; 114 115 if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) { 116 fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName); 117 return; 118 } 119 int fd = open(buffer, O_WRONLY | O_CLOEXEC); 120 if (fd == -1) { 121 fprintf(stderr, "Error opening file %d\n", errno); 122 return; 123 } 124 pid_t pid = getpid(); 125 126 int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long)pid); 127 if (towrite >= BUF_SIZE) { 128 fprintf(stderr, "Buffer wasn't large enough?\n"); 129 } else { 130 if (write(fd, buffer, towrite) != towrite) { 131 fprintf(stderr, "Failed to write, errno=%d", errno); 132 } 133 } 134 close(fd); 135 } 136 137 static bool setBenchmarkFormat(const char* format) { 138 if (!strcmp(format, "tabular")) { 139 gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); 140 } else if (!strcmp(format, "json")) { 141 gBenchmarkReporter.reset(new benchmark::JSONReporter()); 142 } else { 143 fprintf(stderr, "Unknown format '%s'", format); 144 return false; 145 } 146 return true; 147 } 148 149 static bool setRenderer(const char* renderer) { 150 if (!strcmp(renderer, "skiagl")) { 151 Properties::overrideRenderPipelineType(RenderPipelineType::SkiaGL); 152 } else if (!strcmp(renderer, "skiavk")) { 153 Properties::overrideRenderPipelineType(RenderPipelineType::SkiaVulkan); 154 } else { 155 fprintf(stderr, "Unknown format '%s'", renderer); 156 return false; 157 } 158 return true; 159 } 160 161 // For options that only exist in long-form. Anything in the 162 // 0-255 range is reserved for short options (which just use their ASCII value) 163 namespace LongOpts { 164 enum { 165 Reserved = 255, 166 List, 167 WaitForGpu, 168 ReportFrametime, 169 CpuSet, 170 BenchmarkFormat, 171 Onscreen, 172 Offscreen, 173 Renderer, 174 RenderAhead, 175 }; 176 } 177 178 static const struct option LONG_OPTIONS[] = { 179 {"frames", required_argument, nullptr, 'f'}, 180 {"repeat", required_argument, nullptr, 'r'}, 181 {"help", no_argument, nullptr, 'h'}, 182 {"list", no_argument, nullptr, LongOpts::List}, 183 {"wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu}, 184 {"report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime}, 185 {"cpuset", required_argument, nullptr, LongOpts::CpuSet}, 186 {"benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat}, 187 {"onscreen", no_argument, nullptr, LongOpts::Onscreen}, 188 {"offscreen", no_argument, nullptr, LongOpts::Offscreen}, 189 {"renderer", required_argument, nullptr, LongOpts::Renderer}, 190 {"render-ahead", required_argument, nullptr, LongOpts::RenderAhead}, 191 {0, 0, 0, 0}}; 192 193 static const char* SHORT_OPTIONS = "c:r:h"; 194 195 void 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 LongOpts::RenderAhead: 290 if (!optarg) { 291 error = true; 292 } 293 gOpts.renderAhead = atoi(optarg); 294 if (gOpts.renderAhead < 0 || gOpts.renderAhead > 2) { 295 error = true; 296 } 297 break; 298 299 case 'h': 300 printHelp(); 301 exit(EXIT_SUCCESS); 302 break; 303 304 case '?': 305 fprintf(stderr, "Unrecognized option '%s'\n", argv[optind - 1]); 306 [[fallthrough]]; 307 default: 308 error = true; 309 break; 310 } 311 } 312 313 if (error) { 314 fprintf(stderr, "Try 'hwuitest --help' for more information.\n"); 315 exit(EXIT_FAILURE); 316 } 317 318 /* Print any remaining command line arguments (not options). */ 319 if (optind < argc) { 320 do { 321 const char* test = argv[optind++]; 322 auto pos = TestScene::testMap().find(test); 323 if (pos == TestScene::testMap().end()) { 324 fprintf(stderr, "Unknown test '%s'\n", test); 325 exit(EXIT_FAILURE); 326 } else { 327 gRunTests.push_back(pos->second); 328 } 329 } while (optind < argc); 330 } else { 331 for (auto& iter : TestScene::testMap()) { 332 gRunTests.push_back(iter.second); 333 } 334 } 335 } 336 337 int main(int argc, char* argv[]) { 338 // set defaults 339 gOpts.count = 150; 340 341 Typeface::setRobotoTypefaceForTest(); 342 343 parseOptions(argc, argv); 344 if (!gBenchmarkReporter && gOpts.renderOffscreen) { 345 gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); 346 } 347 348 if (gBenchmarkReporter) { 349 size_t name_field_width = 10; 350 for (auto&& test : gRunTests) { 351 name_field_width = std::max<size_t>(name_field_width, test.name.size()); 352 } 353 // _50th, _90th, etc... 354 name_field_width += 5; 355 356 benchmark::BenchmarkReporter::Context context; 357 context.name_field_width = name_field_width; 358 gBenchmarkReporter->ReportContext(context); 359 } 360 361 for (int i = 0; i < gRepeatCount; i++) { 362 for (auto&& test : gRunTests) { 363 run(test, gOpts, gBenchmarkReporter.get()); 364 } 365 } 366 367 if (gBenchmarkReporter) { 368 gBenchmarkReporter->Finalize(); 369 } 370 371 renderthread::RenderProxy::trimMemory(100); 372 HardwareBitmapUploader::terminate(); 373 374 LeakChecker::checkForLeaks(); 375 return 0; 376 } 377