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 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 )"); 72 } 73 74 static 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 104 static 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 134 static 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) 150 namespace LongOpts { 151 enum { 152 Reserved = 255, 153 List, 154 WaitForGpu, 155 ReportFrametime, 156 CpuSet, 157 BenchmarkFormat, 158 Onscreen, 159 Offscreen, 160 }; 161 } 162 163 static 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 177 static const char* SHORT_OPTIONS = "c:r:h"; 178 179 void 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 303 int 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