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

Commit 61b0ef89 authored by Sandeep Patil's avatar Sandeep Patil Committed by android-build-merger
Browse files

Merge "meminfo: Add support to track working set with idle page tracking." am:...

Merge "meminfo: Add support to track working set with idle page tracking." am: c1a65543 am: 40a307f5
am: f9658c65

Change-Id: I7f17c85091f17f9cf3a99c701bade4af961d546d
parents e049bdc3 f9658c65
Loading
Loading
Loading
Loading
+7 −2
Original line number Original line Diff line number Diff line
@@ -40,6 +40,11 @@ class ProcMemInfo final {
    const MemUsage& Usage();
    const MemUsage& Usage();
    const MemUsage& Wss();
    const MemUsage& Wss();


    // Same as Maps() except, only valid for reading working set using CONFIG_IDLE_PAGE_TRACKING
    // support in kernel. If the kernel support doesn't exist, the function will return an empty
    // vector.
    const std::vector<Vma>& MapsWithPageIdle();

    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
    // Collect all 'vma' or 'maps' from /proc/<pid>/smaps and store them in 'maps_'. Returns a
    // constant reference to the vma vector after the collection is done.
    // constant reference to the vma vector after the collection is done.
    //
    //
@@ -83,8 +88,8 @@ class ProcMemInfo final {
    ~ProcMemInfo() = default;
    ~ProcMemInfo() = default;


  private:
  private:
    bool ReadMaps(bool get_wss);
    bool ReadMaps(bool get_wss, bool use_pageidle = false);
    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss);
    bool ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle);


    pid_t pid_;
    pid_t pid_;
    bool get_wss_;
    bool get_wss_;
+20 −4
Original line number Original line Diff line number Diff line
@@ -121,6 +121,14 @@ const std::vector<Vma>& ProcMemInfo::Maps() {
    return maps_;
    return maps_;
}
}


const std::vector<Vma>& ProcMemInfo::MapsWithPageIdle() {
    if (maps_.empty() && !ReadMaps(get_wss_, true)) {
        LOG(ERROR) << "Failed to read maps with page idle for Process " << pid_;
    }

    return maps_;
}

const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
const std::vector<Vma>& ProcMemInfo::Smaps(const std::string& path) {
    if (!maps_.empty()) {
    if (!maps_.empty()) {
        return maps_;
        return maps_;
@@ -226,7 +234,7 @@ bool ProcMemInfo::PageMap(const Vma& vma, std::vector<uint64_t>* pagemap) {
    return true;
    return true;
}
}


bool ProcMemInfo::ReadMaps(bool get_wss) {
bool ProcMemInfo::ReadMaps(bool get_wss, bool use_pageidle) {
    // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
    // Each object reads /proc/<pid>/maps only once. This is done to make sure programs that are
    // running for the lifetime of the system can recycle the objects and don't have to
    // running for the lifetime of the system can recycle the objects and don't have to
    // unnecessarily retain and update this object in memory (which can get significantly large).
    // unnecessarily retain and update this object in memory (which can get significantly large).
@@ -256,7 +264,7 @@ bool ProcMemInfo::ReadMaps(bool get_wss) {
    }
    }


    for (auto& vma : maps_) {
    for (auto& vma : maps_) {
        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss)) {
        if (!ReadVmaStats(pagemap_fd.get(), vma, get_wss, use_pageidle)) {
            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
            LOG(ERROR) << "Failed to read page map for vma " << vma.name << "[" << vma.start << "-"
                       << vma.end << "]";
                       << vma.end << "]";
            maps_.clear();
            maps_.clear();
@@ -268,7 +276,7 @@ bool ProcMemInfo::ReadMaps(bool get_wss) {
    return true;
    return true;
}
}


bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss, bool use_pageidle) {
    PageAcct& pinfo = PageAcct::Instance();
    PageAcct& pinfo = PageAcct::Instance();
    uint64_t pagesz = getpagesize();
    uint64_t pagesz = getpagesize();
    uint64_t num_pages = (vma.end - vma.start) / pagesz;
    uint64_t num_pages = (vma.end - vma.start) / pagesz;
@@ -281,6 +289,13 @@ bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
        return false;
        return false;
    }
    }


    if (get_wss && use_pageidle) {
        if (!pinfo.InitPageAcct(true)) {
            LOG(ERROR) << "Failed to init idle page accounting";
            return false;
        }
    }

    std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
    std::unique_ptr<uint64_t[]> pg_flags(new uint64_t[num_pages]);
    std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
    std::unique_ptr<uint64_t[]> pg_counts(new uint64_t[num_pages]);
    for (uint64_t i = 0; i < num_pages; ++i) {
    for (uint64_t i = 0; i < num_pages; ++i) {
@@ -323,7 +338,8 @@ bool ProcMemInfo::ReadVmaStats(int pagemap_fd, Vma& vma, bool get_wss) {
        bool is_private = (pg_counts[i] == 1);
        bool is_private = (pg_counts[i] == 1);
        // Working set
        // Working set
        if (get_wss) {
        if (get_wss) {
            bool is_referenced = !!(pg_flags[i] & (1 << KPF_REFERENCED));
            bool is_referenced = use_pageidle ? (pinfo.IsPageIdle(page_frame) == 1)
                                              : !!(pg_flags[i] & (1 << KPF_REFERENCED));
            if (!is_referenced) {
            if (!is_referenced) {
                continue;
                continue;
            }
            }
+14 −0
Original line number Original line Diff line number Diff line
@@ -67,3 +67,17 @@ cc_binary {
        "libmeminfo",
        "libmeminfo",
    ],
    ],
}
}

cc_binary {
    name: "wsstop",
    cflags: [
        "-Wall",
        "-Werror",
    ],
    srcs: ["wsstop.cpp"],
    shared_libs: [
        "libbase",
        "liblog",
        "libmeminfo",
    ],
}
+219 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <getopt.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#include <algorithm>
#include <memory>
#include <string>
#include <vector>

#include <meminfo/pageacct.h>
#include <meminfo/procmeminfo.h>

using ::android::meminfo::ProcMemInfo;
using ::android::meminfo::Vma;

// Global options
static int32_t g_delay = 0;
static int32_t g_total = 2;
static pid_t g_pid = -1;

[[noreturn]] static void usage(int exit_status) {
    fprintf(stderr,
            "%s [-d DELAY_BETWEEN_EACH_SAMPLE] [-n REFRESH_TOTAL] PID\n"
            "-d\tdelay between each working set sample (default 0)\n"
            "-n\ttotal number of refreshes before we exit (default 2)\n",
            getprogname());

    exit(exit_status);
}

static void print_header() {
    const char* addr1 = "           start              end ";
    const char* addr2 = "            addr             addr ";

    printf("%s  virtual                        shared    shared   private   private\n", addr1);
    printf("%s     size       RSS       PSS     clean     dirty     clean     dirty      swap   "
           "swapPSS",
           addr2);
    printf(" object\n");
}

static void print_divider() {
    printf("---------------- ---------------- ");
    printf("--------- --------- --------- --------- --------- --------- --------- --------- "
           "--------- ");
    printf("------------------------------\n");
}

static void print_vma(const Vma& v) {
    printf("%16" PRIx64 " %16" PRIx64 " ", v.start, v.end);
    printf("%8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64
           "K %8" PRIu64 "K %8" PRIu64 "K %8" PRIu64 "K ",
           v.usage.vss / 1024, v.usage.rss / 1024, v.usage.pss / 1024, v.usage.shared_clean / 1024,
           v.usage.shared_dirty / 1024, v.usage.private_clean / 1024, v.usage.private_dirty / 1024,
           v.usage.swap / 1024, v.usage.swap_pss / 1024);
    printf("%s\n", v.name.c_str());
}

static bool same_vma(const Vma& cur, const Vma& last) {
    return (cur.start == last.start && cur.end == last.end && cur.name == last.name &&
            cur.flags == last.flags && cur.offset == last.offset);
}

static Vma diff_vma_params(const Vma& cur, const Vma& last) {
    Vma res;
    res.usage.shared_clean = cur.usage.shared_clean > last.usage.shared_clean
                                     ? cur.usage.shared_clean - last.usage.shared_clean
                                     : 0;
    res.usage.shared_dirty = cur.usage.shared_dirty > last.usage.shared_dirty
                                     ? cur.usage.shared_dirty - last.usage.shared_dirty
                                     : 0;
    res.usage.private_clean = cur.usage.private_clean > last.usage.private_clean
                                      ? cur.usage.private_clean - last.usage.private_clean
                                      : 0;
    res.usage.private_dirty = cur.usage.private_dirty > last.usage.private_dirty
                                      ? cur.usage.private_dirty - last.usage.private_dirty
                                      : 0;

    res.usage.rss = cur.usage.rss > last.usage.rss ? cur.usage.rss - last.usage.rss : 0;
    res.usage.pss = cur.usage.pss > last.usage.pss ? cur.usage.pss - last.usage.pss : 0;
    res.usage.uss = cur.usage.uss > last.usage.uss ? cur.usage.uss - last.usage.uss : 0;
    res.usage.swap = cur.usage.swap > last.usage.swap ? cur.usage.swap - last.usage.swap : 0;
    res.usage.swap_pss =
            cur.usage.swap_pss > last.usage.swap_pss ? cur.usage.swap_pss - last.usage.swap_pss : 0;

    // set vma properties to the same as the current one.
    res.start = cur.start;
    res.end = cur.end;
    res.offset = cur.offset;
    res.flags = cur.flags;
    res.name = cur.name;
    return res;
}

static void diff_workingset(std::vector<Vma>& wss, std::vector<Vma>& old, std::vector<Vma>* res) {
    res->clear();
    auto vma_sorter = [](const Vma& a, const Vma& b) { return a.start < b.start; };
    std::sort(wss.begin(), wss.end(), vma_sorter);
    std::sort(old.begin(), old.end(), vma_sorter);
    if (old.empty()) {
        *res = wss;
        return;
    }

    for (auto& i : wss) {
        bool found_same_vma = false;
        // TODO: This is highly inefficient, fix it if it takes
        // too long. Worst case will be system_server
        for (auto& j : old) {
            if (same_vma(i, j)) {
                res->emplace_back(diff_vma_params(i, j));
                found_same_vma = true;
                break;
            }
        }

        if (!found_same_vma) {
            res->emplace_back(i);
        }
    }

    std::sort(res->begin(), res->end(), vma_sorter);
    return;
}

static int workingset() {
    std::vector<Vma> last_wss = {};
    std::vector<Vma> diff_wss = {};
    uint32_t nr_refresh = 0;

    while (true) {
        std::unique_ptr<ProcMemInfo> proc_mem = std::make_unique<ProcMemInfo>(g_pid, true);
        std::vector<Vma> wss = proc_mem->MapsWithPageIdle();

        diff_workingset(wss, last_wss, &diff_wss);
        diff_wss.erase(std::remove_if(diff_wss.begin(), diff_wss.end(),
                                      [](const auto& v) { return v.usage.rss == 0; }),
                       diff_wss.end());
        if ((nr_refresh % 5) == 0) {
            print_header();
            print_divider();
        }

        for (const auto& v : diff_wss) {
            print_vma(v);
        }

        nr_refresh++;
        if (nr_refresh == g_total) {
            break;
        }

        last_wss = wss;
        sleep(g_delay);
        print_divider();
    }

    return 0;
}

int main(int argc, char* argv[]) {
    struct option longopts[] = {
            {"help", no_argument, nullptr, 'h'},
            {0, 0, nullptr, 0},
    };

    int opt;
    while ((opt = getopt_long(argc, argv, "d:n:h", longopts, nullptr)) != -1) {
        switch (opt) {
            case 'd':
                g_delay = atoi(optarg);
                break;
            case 'n':
                g_total = atoi(optarg);
                break;
            case 'h':
                usage(EXIT_SUCCESS);
            default:
                usage(EXIT_FAILURE);
        }
    }

    if ((argc - 1) < optind) {
        fprintf(stderr, "Invalid arguments: Must provide <pid> at the end\n");
        usage(EXIT_FAILURE);
    }

    g_pid = atoi(argv[optind]);
    if (g_pid <= 0) {
        fprintf(stderr, "Invalid process id %s\n", argv[optind]);
        usage(EXIT_FAILURE);
    }

    if (!::android::meminfo::PageAcct::KernelHasPageIdle()) {
        fprintf(stderr, "Missing support for Idle page tracking in the kernel\n");
        return 0;
    }

    return workingset();
}