Home | History | Annotate | Download | only in benchmarks
      1 /*
      2  * Copyright (C) 2016 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 #define LOG_TAG "connect_benchmark"
     18 
     19 /*
     20  * See README.md for general notes.
     21  *
     22  * This set of benchmarks measures the throughput of connect() calls on a single thread for IPv4 and
     23  * IPv6 under the following scenarios:
     24  *
     25  *  - FWmark disabled (::ANDROID_NO_USE_FWMARK_CLIENT).
     26  *
     27  *      The control case for other high load benchmarks. Essentially just testing performance of
     28  *      the kernel connect call. In real world use fwmark should stay on in order for traffic to
     29  *      be routed properly.
     30  *
     31  *  - FWmark enabled only for metrics (::ANDROID_FWMARK_METRICS_ONLY).
     32  *
     33  *      The default mode up to and including 7.1. Every time connect() is called on an AF_INET or
     34  *      AF_INET6 socket, netdclient sends a synchronous message to fwmarkserver to get the socket
     35  *      marked. Only the fields that are useful for marking or for metrics are sent in this mode;
     36  *      other fields are set to null for the RPC and ignored.
     37  *
     38  *  - FWmark enabled for all events.
     39  *
     40  *      The default mode starting from 7.1.2. As well as the normal connect() reporting, extra
     41  *      fields are filled in to log the IP and port of the connection.
     42  *
     43  *      A second synchronous message is sent to fwmarkserver after the connection completes, to
     44  *      record latency. This message is forwarded to the system server over a oneway binder call.
     45  *
     46  * Realtime timed tests
     47  * ====================
     48  *
     49  * The tests named *_high_load record the following useful information:
     50  *
     51  *   - real_time: the mean roundtrip time for one connect() call under load
     52  *
     53  *   - iterations: the number of times the test was run within the timelimit --- approximately
     54  *                 MinTime / real_time
     55  *
     56  * Manually timed tests
     57  * ====================
     58  *
     59  * All other sets of tests apart from *_high_load run with manual timing. The purpose of these is to
     60  * measure 90th-percentile latency for connect() calls compared to mean latency.
     61  *
     62  * (TODO: ideally this should be against median latency, but google-benchmark only supports one
     63  *        custom 'label' output for graphing. Stddev isn't appropriate because the latency
     64  *        distribution is usually spiky, not in a nice neat normal-like distribution.)
     65  *
     66  * The manually timed tests record the following useful information:
     67  *
     68  *  - real_time: the average time taken to complete a test run. Unlike the real_time used in high
     69  *               load tests, this is calculated from before-and-after values of the realtime clock
     70  *               over many iterations so may be less accurate than the under-load times.
     71  *
     72  *  - iterations: the number of times the test was run within the timelimit --- approximately
     73  *                MinTime / real_time, although as explained, may not be as meaningful because of
     74  *                overhead from timing.
     75  *
     76  *  - label: a manually-recorded time giving the 90th-percentile value of real_time over all
     77  *           individual runs. Should be compared to real_time.
     78  *
     79  */
     80 
     81 #include <arpa/inet.h>
     82 #include <cutils/sockets.h>
     83 #include <errno.h>
     84 #include <netinet/in.h>
     85 #include <time.h>
     86 
     87 #include <map>
     88 #include <functional>
     89 #include <thread>
     90 
     91 #include <android-base/stringprintf.h>
     92 #include <benchmark/benchmark.h>
     93 #include <log/log.h>
     94 #include <utils/StrongPointer.h>
     95 
     96 #include "FwmarkClient.h"
     97 #include "SockDiag.h"
     98 #include "Stopwatch.h"
     99 #include "android/net/metrics/INetdEventListener.h"
    100 
    101 using android::base::StringPrintf;
    102 using android::net::metrics::INetdEventListener;
    103 
    104 static int bindAndListen(int s) {
    105     sockaddr_in6 sin6 = { .sin6_family = AF_INET6 };
    106     if (bind(s, (sockaddr*) &sin6, sizeof(sin6)) == 0) {
    107         if (listen(s, 1)) {
    108             return -1;
    109         }
    110         sockaddr_in sin = {};
    111         socklen_t len = sizeof(sin);
    112         if (getsockname(s, (sockaddr*) &sin, &len)) {
    113             return -1;
    114         }
    115         return ntohs(sin.sin_port);
    116     } else {
    117         return -1;
    118     }
    119 }
    120 
    121 static void ipv4_loopback(benchmark::State& state, const bool waitBetweenRuns) {
    122     const int listensocket = socket(AF_INET6, SOCK_STREAM, 0);
    123     const int port = bindAndListen(listensocket);
    124     if (port == -1) {
    125         state.SkipWithError("Unable to bind server socket");
    126         return;
    127     }
    128 
    129     // ALOGW("Listening on port = %d", port);
    130     std::vector<uint64_t> latencies(state.max_iterations);
    131     uint64_t iterations = 0;
    132 
    133     while (state.KeepRunning()) {
    134         int sock = socket(AF_INET, SOCK_STREAM, 0);
    135         if (sock < 0) {
    136             state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
    137             break;
    138         }
    139 
    140         const Stopwatch stopwatch;
    141 
    142         sockaddr_in server = { .sin_family = AF_INET, .sin_port = htons(port) };
    143         if (connect(sock, (sockaddr*) &server, sizeof(server))) {
    144             state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
    145             close(sock);
    146             break;
    147         }
    148 
    149         if (waitBetweenRuns) {
    150             latencies[iterations] = stopwatch.timeTaken() * 1e6L;
    151             state.SetIterationTime(latencies[iterations] / 1e9L);
    152             std::this_thread::sleep_for(std::chrono::milliseconds(10));
    153             ++iterations;
    154         }
    155 
    156         sockaddr_in6 client;
    157         socklen_t clientlen = sizeof(client);
    158         int accepted = accept(listensocket, (sockaddr *) &client, &clientlen);
    159         if (accepted < 0) {
    160             state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
    161             close(sock);
    162             break;
    163         }
    164 
    165         close(accepted);
    166         close(sock);
    167     }
    168     close(listensocket);
    169     // ALOGI("Finished test on port = %d", port);
    170 
    171     if (iterations > 0) {
    172         latencies.resize(iterations);
    173         sort(latencies.begin(), latencies.end());
    174         state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
    175     }
    176 }
    177 
    178 static void ipv6_loopback(benchmark::State& state, const bool waitBetweenRuns) {
    179     const int listensocket = socket(AF_INET6, SOCK_STREAM, 0);
    180     const int port = bindAndListen(listensocket);
    181     if (port == -1) {
    182         state.SkipWithError("Unable to bind server socket");
    183         return;
    184     }
    185 
    186     // ALOGW("Listening on port = %d", port);
    187     std::vector<uint64_t> latencies(state.max_iterations);
    188     uint64_t iterations = 0;
    189 
    190     while (state.KeepRunning()) {
    191         int sock = socket(AF_INET6, SOCK_STREAM, 0);
    192         if (sock < 0) {
    193             state.SkipWithError(StringPrintf("socket() failed with errno=%d", errno).c_str());
    194             break;
    195         }
    196 
    197         const Stopwatch stopwatch;
    198 
    199         sockaddr_in6 server = { .sin6_family = AF_INET6, .sin6_port = htons(port) };
    200         if (connect(sock, (sockaddr*) &server, sizeof(server))) {
    201             state.SkipWithError(StringPrintf("connect() failed with errno=%d", errno).c_str());
    202             close(sock);
    203             break;
    204         }
    205 
    206         if (waitBetweenRuns) {
    207             latencies[iterations] = stopwatch.timeTaken() * 1e6L;
    208             state.SetIterationTime(latencies[iterations] / 1e9L);
    209             std::this_thread::sleep_for(std::chrono::milliseconds(10));
    210             ++iterations;
    211         }
    212 
    213         sockaddr_in6 client;
    214         socklen_t clientlen = sizeof(client);
    215         int accepted = accept(listensocket, (sockaddr *) &client, &clientlen);
    216         if (accepted < 0) {
    217             state.SkipWithError(StringPrintf("accept() failed with errno=%d", errno).c_str());
    218             close(sock);
    219             break;
    220         }
    221 
    222         close(accepted);
    223         close(sock);
    224     }
    225     close(listensocket);
    226     // ALOGI("Finished test on port = %d", port);
    227 
    228     if (iterations > 0) {
    229         latencies.resize(iterations);
    230         sort(latencies.begin(), latencies.end());
    231         state.SetLabel(StringPrintf("%lld", (long long) latencies[iterations * 9 / 10]));
    232     }
    233 }
    234 
    235 static void run_at_reporting_level(decltype(ipv4_loopback) benchmarkFunction,
    236                                    ::benchmark::State& state, const int reportingLevel,
    237                                    const bool waitBetweenRuns) {
    238     // Our master thread (thread_index == 0) will control setup and teardown for other threads.
    239     const bool isMaster = (state.thread_index == 0);
    240 
    241     // Previous values of env variables used by fwmarkclient (only read/written by master thread)
    242     const std::string savedSettings[] = {
    243         FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT,
    244         FwmarkClient::ANDROID_FWMARK_METRICS_ONLY
    245     };
    246     std::map<std::string, std::string> prevSettings;
    247 
    248     // SETUP
    249     if (isMaster) {
    250         for (const auto setting : savedSettings) {
    251             const char* prevEnvStr = getenv(setting.c_str());
    252             if (prevEnvStr != nullptr) {
    253                 prevSettings[setting.c_str()] = prevEnvStr;
    254             }
    255         }
    256         switch (reportingLevel) {
    257             case INetdEventListener::REPORTING_LEVEL_NONE:
    258                 setenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT, "", 1);
    259                 break;
    260             case INetdEventListener::REPORTING_LEVEL_METRICS:
    261                 unsetenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT);
    262                 setenv(FwmarkClient::ANDROID_FWMARK_METRICS_ONLY, "", 1);
    263                 break;
    264             case INetdEventListener::REPORTING_LEVEL_FULL:
    265                 unsetenv(FwmarkClient::ANDROID_NO_USE_FWMARK_CLIENT);
    266                 unsetenv(FwmarkClient::ANDROID_FWMARK_METRICS_ONLY);
    267                 break;
    268         }
    269     }
    270 
    271     // TEST
    272     benchmarkFunction(state, waitBetweenRuns);
    273 
    274     // TEARDOWN
    275     if (isMaster) {
    276         for (const auto setting : savedSettings) {
    277             if (prevSettings.count(setting)) {
    278                 setenv(setting.c_str(), prevSettings[setting].c_str(), 1);
    279             } else {
    280                 unsetenv(setting.c_str());
    281             }
    282         }
    283     }
    284 }
    285 
    286 constexpr int MIN_THREADS = 1;
    287 constexpr int MAX_THREADS = 1;
    288 constexpr double MIN_TIME = 0.5 /* seconds */;
    289 
    290 static void ipv4_metrics_reporting_no_fwmark(::benchmark::State& state) {
    291     run_at_reporting_level(ipv4_loopback, state, INetdEventListener::REPORTING_LEVEL_NONE, true);
    292 }
    293 BENCHMARK(ipv4_metrics_reporting_no_fwmark)->MinTime(MIN_TIME)->UseManualTime();
    294 
    295 // IPv4 metrics under low load
    296 static void ipv4_metrics_reporting_no_load(::benchmark::State& state) {
    297     run_at_reporting_level(ipv4_loopback, state, INetdEventListener::REPORTING_LEVEL_METRICS, true);
    298 }
    299 BENCHMARK(ipv4_metrics_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
    300 
    301 static void ipv4_full_reporting_no_load(::benchmark::State& state) {
    302     run_at_reporting_level(ipv4_loopback, state, INetdEventListener::REPORTING_LEVEL_FULL, true);
    303 }
    304 BENCHMARK(ipv4_full_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
    305 
    306 // IPv4 benchmarks under high load
    307 static void ipv4_metrics_reporting_high_load(::benchmark::State& state) {
    308     run_at_reporting_level(ipv4_loopback, state, INetdEventListener::REPORTING_LEVEL_METRICS,
    309             false);
    310 }
    311 BENCHMARK(ipv4_metrics_reporting_high_load)
    312     ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
    313 
    314 static void ipv4_full_reporting_high_load(::benchmark::State& state) {
    315     run_at_reporting_level(ipv4_loopback, state, INetdEventListener::REPORTING_LEVEL_FULL, false);
    316 }
    317 BENCHMARK(ipv4_full_reporting_high_load)
    318     ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
    319 
    320 // IPv6 raw connect() without using fwmark
    321 static void ipv6_metrics_reporting_no_fwmark(::benchmark::State& state) {
    322     run_at_reporting_level(ipv6_loopback, state, INetdEventListener::REPORTING_LEVEL_NONE, true);
    323 }
    324 BENCHMARK(ipv6_metrics_reporting_no_fwmark)->MinTime(MIN_TIME)->UseManualTime();
    325 
    326 // IPv6 metrics under low load
    327 static void ipv6_metrics_reporting_no_load(::benchmark::State& state) {
    328     run_at_reporting_level(ipv6_loopback, state, INetdEventListener::REPORTING_LEVEL_METRICS, true);
    329 }
    330 BENCHMARK(ipv6_metrics_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
    331 
    332 static void ipv6_full_reporting_no_load(::benchmark::State& state) {
    333     run_at_reporting_level(ipv6_loopback, state, INetdEventListener::REPORTING_LEVEL_FULL, true);
    334 }
    335 BENCHMARK(ipv6_full_reporting_no_load)->MinTime(MIN_TIME)->UseManualTime();
    336 
    337 // IPv6 benchmarks under high load
    338 static void ipv6_metrics_reporting_high_load(::benchmark::State& state) {
    339     run_at_reporting_level(ipv6_loopback, state, INetdEventListener::REPORTING_LEVEL_METRICS,
    340             false);
    341 }
    342 BENCHMARK(ipv6_metrics_reporting_high_load)
    343     ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
    344 
    345 static void ipv6_full_reporting_high_load(::benchmark::State& state) {
    346     run_at_reporting_level(ipv6_loopback, state, INetdEventListener::REPORTING_LEVEL_FULL, false);
    347 }
    348 BENCHMARK(ipv6_full_reporting_high_load)
    349     ->ThreadRange(MIN_THREADS, MAX_THREADS)->MinTime(MIN_TIME)->UseRealTime();
    350