Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b30ae6e authored by Mike Yu's avatar Mike Yu
Browse files

Add getAverageLatencyMs in DnsStats

This new method returns the average DNS response time by protocol,
for example, DoT. By this method, we can know the average waiting
time for a DNS response and then utilize this information to evaluate
the performance of DoT and Do53.

Original change: https://android-review.googlesource.com/c/platform/packages/modules/DnsResolver/+/1708170

Bug: 188153519
Test: cd packages/modules/DnsResolver && atest
Change-Id: I19eb25d18b1eb1ab97c7efc97d56ae9a315e4222
Merged-In: I19eb25d18b1eb1ab97c7efc97d56ae9a315e4222
parent e2cfe61f
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -239,6 +239,20 @@ std::vector<IPSockAddr> DnsStats::getSortedServers(Protocol protocol) const {
    return ret;
}

std::optional<microseconds> DnsStats::getAverageLatencyUs(Protocol protocol) const {
    const auto stats = getStats(protocol);

    int count = 0;
    microseconds sum;
    for (const auto& v : stats) {
        count += v.total;
        sum += v.latencyUs;
    }

    if (count == 0) return std::nullopt;
    return sum / count;
}

std::vector<StatsData> DnsStats::getStats(Protocol protocol) const {
    std::vector<StatsData> ret;

+3 −1
Original line number Diff line number Diff line
@@ -121,9 +121,11 @@ class DnsStats {

    std::vector<netdutils::IPSockAddr> getSortedServers(Protocol protocol) const;

    // Returns the average query latency in microseconds.
    std::optional<std::chrono::microseconds> getAverageLatencyUs(Protocol protocol) const;

    void dump(netdutils::DumpWriter& dw);

    // For testing.
    std::vector<StatsData> getStats(Protocol protocol) const;

    // TODO: Compatible support for getResolverInfo().
+56 −27
Original line number Diff line number Diff line
@@ -26,12 +26,24 @@ namespace android::net {

using namespace std::chrono_literals;
using android::netdutils::IPSockAddr;
using std::chrono::microseconds;
using std::chrono::milliseconds;
using ::testing::IsEmpty;
using ::testing::UnorderedElementsAreArray;

namespace {

constexpr auto NO_AVERAGE_LATENCY = std::nullopt;

// A helper which can propagate the failure to outside of the stmt to know which line
// of stmt fails. The expectation fails only for the first failed stmt.
#define EXPECT_NO_FAILURE(stmt)                                         \
    do {                                                                \
        bool alreadyFailed = HasFailure();                              \
        stmt;                                                           \
        if (!alreadyFailed && HasFailure()) EXPECT_FALSE(HasFailure()); \
    } while (0)

DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode,
                                const milliseconds& latency) {
    DnsQueryEvent event;
@@ -41,11 +53,11 @@ DnsQueryEvent makeDnsQueryEvent(const Protocol protocol, const NsRcode rcode,
    return event;
}

StatsData makeStatsData(const IPSockAddr& server, const int total, const milliseconds& latencyMs,
StatsData makeStatsData(const IPSockAddr& server, const int total, const microseconds& latencyUs,
                        const std::map<int, int>& rcodeCounts) {
    StatsData ret(server);
    ret.total = total;
    ret.latencyUs = latencyMs;
    ret.latencyUs = latencyUs;
    ret.rcodeCounts = rcodeCounts;
    return ret;
}
@@ -143,6 +155,17 @@ class DnsStatsTest : public ::testing::Test {
        EXPECT_EQ(dumpString, "\n");
    }

    void verifyDnsStatsContent(Protocol protocol, const std::vector<StatsData>& expectedStats,
                               const std::optional<microseconds>& expectedAvgLatency) {
        if (expectedStats.empty()) {
            EXPECT_THAT(mDnsStats.getStats(protocol), IsEmpty());
        } else {
            EXPECT_THAT(mDnsStats.getStats(protocol), UnorderedElementsAreArray(expectedStats));
        }

        EXPECT_EQ(mDnsStats.getAverageLatencyUs(protocol), expectedAvgLatency);
    }

    DnsStats mDnsStats;
};

@@ -200,9 +223,9 @@ TEST_F(DnsStatsTest, SetServers) {
            expectedStats.push_back(makeStatsData(IPSockAddr::toIPSockAddr(exp, 53), 0, 0ms, {}));
        }

        EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
        EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
        EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
        EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, expectedStats, NO_AVERAGE_LATENCY));
        EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, NO_AVERAGE_LATENCY));
        EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, expectedStats, NO_AVERAGE_LATENCY));
    }

    verifyDumpOutput({}, {}, {});
@@ -221,9 +244,9 @@ TEST_F(DnsStatsTest, SetServersDifferentPorts) {
    EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_UDP));
    EXPECT_FALSE(mDnsStats.setServers(servers, PROTO_DOT));

    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, {}, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, {}, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, {}, NO_AVERAGE_LATENCY));
    verifyDumpOutput({}, {}, {});

    EXPECT_TRUE(mDnsStats.setServers(std::vector(servers.begin() + 2, servers.end()), PROTO_TCP));
@@ -236,9 +259,9 @@ TEST_F(DnsStatsTest, SetServersDifferentPorts) {
            makeStatsData(servers[6], 0, 0ms, {}), makeStatsData(servers[7], 0, 0ms, {}),
    };

    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, expectedStats, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, expectedStats, NO_AVERAGE_LATENCY));
    verifyDumpOutput(expectedStats, expectedStats, expectedStats);
}

@@ -269,18 +292,18 @@ TEST_F(DnsStatsTest, AddStatsAndClear) {
            makeStatsData(servers[1], 1, 10ms, {{NS_R_NO_ERROR, 1}}),
    };

    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStatsForTcp));
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStatsForUdp));
    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, expectedStatsForTcp, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStatsForUdp, 10ms));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, {}, NO_AVERAGE_LATENCY));
    verifyDumpOutput(expectedStatsForTcp, expectedStatsForUdp, {});

    // Clear stats.
    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_TCP));
    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_UDP));
    EXPECT_TRUE(mDnsStats.setServers({}, PROTO_DOT));
    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), IsEmpty());
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), IsEmpty());
    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), IsEmpty());
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, {}, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, {}, NO_AVERAGE_LATENCY));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, {}, NO_AVERAGE_LATENCY));
    verifyDumpOutput({}, {}, {});
}

@@ -308,6 +331,7 @@ TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
            makeStatsData(servers[1], 4, 520ms, {{NS_R_NO_ERROR, 2}, {NS_R_TIMEOUT, 2}}),
    };
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, 106ms));
    verifyDumpOutput({}, expectedStats, {});

    // Update the server list, the stats of 127.0.0.2 will remain.
@@ -323,6 +347,7 @@ TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
            makeStatsData(servers[2], 0, 0ms, {}),
    };
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, 130ms));
    verifyDumpOutput({}, expectedStats, {});

    // Let's add a record to 127.0.0.2 again.
@@ -333,6 +358,7 @@ TEST_F(DnsStatsTest, StatsRemainsInExistentServer) {
            makeStatsData(servers[2], 0, 0ms, {}),
    };
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, 106ms));
    verifyDumpOutput({}, expectedStats, {});
}

@@ -363,9 +389,9 @@ TEST_F(DnsStatsTest, AddStatsRecords_100000) {

    for (size_t i = 0; i < operations; i++) {
        const NsRcode rcode = static_cast<NsRcode>(rcodes[i % rcodeNum]);
        const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, rcode, 10ms);
        const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, rcode, 10ms);
        const auto eventDot = makeDnsQueryEvent(PROTO_DOT, rcode, 10ms);
        const auto eventTcp = makeDnsQueryEvent(PROTO_TCP, rcode, milliseconds(i));
        const auto eventUdp = makeDnsQueryEvent(PROTO_UDP, rcode, milliseconds(i));
        const auto eventDot = makeDnsQueryEvent(PROTO_DOT, rcode, milliseconds(i));
        for (const auto& server : servers) {
            SCOPED_TRACE(server.toString() + "-" + std::to_string(i));
            ASSERT_TRUE(mDnsStats.addStats(server, eventTcp));
@@ -378,16 +404,19 @@ TEST_F(DnsStatsTest, AddStatsRecords_100000) {
    for (const auto& rcode : rcodes) {
        expectedRcodeCounts.try_emplace(rcode, 32);
    }

    // The average latency 99935.5 ms is derived from (99872ms + 99873ms + ... + 99999ms) / logSize,
    // where logSize is 128.
    const std::vector<StatsData> expectedStats = {
            makeStatsData(servers[0], logSize, logSize * 10ms, expectedRcodeCounts),
            makeStatsData(servers[1], logSize, logSize * 10ms, expectedRcodeCounts),
            makeStatsData(servers[2], logSize, logSize * 10ms, expectedRcodeCounts),
            makeStatsData(servers[3], logSize, logSize * 10ms, expectedRcodeCounts),
            makeStatsData(servers[0], logSize, logSize * 99935500us, expectedRcodeCounts),
            makeStatsData(servers[1], logSize, logSize * 99935500us, expectedRcodeCounts),
            makeStatsData(servers[2], logSize, logSize * 99935500us, expectedRcodeCounts),
            makeStatsData(servers[3], logSize, logSize * 99935500us, expectedRcodeCounts),
    };

    EXPECT_THAT(mDnsStats.getStats(PROTO_TCP), UnorderedElementsAreArray(expectedStats));
    EXPECT_THAT(mDnsStats.getStats(PROTO_UDP), UnorderedElementsAreArray(expectedStats));
    EXPECT_THAT(mDnsStats.getStats(PROTO_DOT), UnorderedElementsAreArray(expectedStats));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_TCP, expectedStats, 99935500us));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_UDP, expectedStats, 99935500us));
    EXPECT_NO_FAILURE(verifyDnsStatsContent(PROTO_DOT, expectedStats, 99935500us));
    verifyDumpOutput(expectedStats, expectedStats, expectedStats);
}