Home | History | Annotate | Download | only in macrobench
      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