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 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 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 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 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 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