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

Commit dec6a881 authored by David Anderson's avatar David Anderson
Browse files

storaged: Cap io_history when loading stats from disk.

Similar to add_record_locked, load_uid_io_proto should respect the
maximum number of records.

Bug: 111578975
Test: storaged_test.load_uid_io_proto
Change-Id: Ic3c5095cdd09d2184f436813b5ab50d3ee0007b2
Merged-In: Ic3c5095cdd09d2184f436813b5ab50d3ee0007b2
parent 0026a14f
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -105,6 +105,10 @@ private:
    // writes io_history to protobuf
    void update_uid_io_proto(unordered_map<int, StoragedProto>* protos);

    // Ensure that io_history_ can append |n| items without exceeding
    // MAX_UID_RECORDS_SIZE in size.
    void maybe_shrink_history_for_items(size_t nitems);

public:
    uid_monitor();
    // called by storaged main thread
@@ -124,6 +128,8 @@ public:
    void clear_user_history(userid_t user_id);

    map<uint64_t, uid_records>& io_history() { return io_history_; }

    static constexpr int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours
};

#endif /* _STORAGED_UID_MONITOR_H_ */
+13 −6
Original line number Diff line number Diff line
@@ -201,8 +201,6 @@ std::unordered_map<uint32_t, uid_info> uid_monitor::get_uid_io_stats_locked()

namespace {

const int MAX_UID_RECORDS_SIZE = 1000 * 48; // 1000 uids in 48 hours

inline size_t history_size(
    const std::map<uint64_t, struct uid_records>& history)
{
@@ -246,15 +244,18 @@ void uid_monitor::add_records_locked(uint64_t curr_ts)
      return;

    // make some room for new records
    ssize_t overflow = history_size(io_history_) +
        new_records.entries.size() - MAX_UID_RECORDS_SIZE;
    maybe_shrink_history_for_items(new_records.entries.size());

    io_history_[curr_ts] = new_records;
}

void uid_monitor::maybe_shrink_history_for_items(size_t nitems) {
    ssize_t overflow = history_size(io_history_) + nitems - MAX_UID_RECORDS_SIZE;
    while (overflow > 0 && io_history_.size() > 0) {
        auto del_it = io_history_.begin();
        overflow -= del_it->second.entries.size();
        io_history_.erase(io_history_.begin());
    }

    io_history_[curr_ts] = new_records;
}

std::map<uint64_t, struct uid_records> uid_monitor::dump(
@@ -508,6 +509,12 @@ void uid_monitor::load_uid_io_proto(userid_t user_id, const UidIOUsage& uid_io_p
            }
            recs->entries.push_back(record);
        }

        // We already added items, so this will just cull down to the maximum
        // length. We do not remove anything if there is only one entry.
        if (io_history_.size() > 1) {
            maybe_shrink_history_for_items(0);
        }
    }
}

+30 −10
Original line number Diff line number Diff line
@@ -616,22 +616,24 @@ TEST(storaged_test, uid_monitor) {

    uidm.clear_user_history(0);

    EXPECT_EQ(uidm.io_history_.size(), 2UL);
    EXPECT_EQ(uidm.io_history_.count(200), 1UL);
    EXPECT_EQ(uidm.io_history_.count(300), 1UL);
    EXPECT_EQ(io_history.size(), 2UL);
    EXPECT_EQ(io_history.count(200), 1UL);
    EXPECT_EQ(io_history.count(300), 1UL);

    EXPECT_EQ(uidm.io_history_[200].entries.size(), 1UL);
    EXPECT_EQ(uidm.io_history_[300].entries.size(), 1UL);
    EXPECT_EQ(io_history[200].entries.size(), 1UL);
    EXPECT_EQ(io_history[300].entries.size(), 1UL);

    uidm.clear_user_history(1);

    EXPECT_EQ(uidm.io_history_.size(), 0UL);
    EXPECT_EQ(io_history.size(), 0UL);
}

TEST(storaged_test, load_uid_io_proto) {
    uid_monitor uidm;
    auto& io_history = uidm.io_history();

    uidm.io_history_[200] = {
    static const uint64_t kProtoTime = 200;
    io_history[kProtoTime] = {
        .start_ts = 100,
        .entries = {
            { "app1", {
@@ -657,10 +659,28 @@ TEST(storaged_test, load_uid_io_proto) {
    ASSERT_EQ(protos.size(), size_t(1));

    // Loading the same proto many times should not add duplicate entries.
    const UidIOUsage& user_0 = protos[0].uid_io_usage();
    UidIOUsage user_0 = protos[0].uid_io_usage();
    for (size_t i = 0; i < 10000; i++) {
        uidm.load_uid_io_proto(0, user_0);
    }
    ASSERT_EQ(uidm.io_history_.size(), size_t(1));
    ASSERT_EQ(uidm.io_history_[200].entries.size(), size_t(3));
    ASSERT_EQ(io_history.size(), size_t(1));
    ASSERT_EQ(io_history[kProtoTime].entries.size(), size_t(3));

    // Create duplicate entries until we go over the limit.
    auto record = io_history[kProtoTime];
    io_history.clear();
    for (size_t i = 0; i < uid_monitor::MAX_UID_RECORDS_SIZE * 2; i++) {
        if (i == kProtoTime) {
            continue;
        }
        io_history[i] = record;
    }
    ASSERT_GT(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));

    // After loading, the history should be truncated.
    for (auto& item : *user_0.mutable_uid_io_items()) {
        item.set_end_ts(io_history.size());
    }
    uidm.load_uid_io_proto(0, user_0);
    ASSERT_LE(io_history.size(), size_t(uid_monitor::MAX_UID_RECORDS_SIZE));
}