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

Commit 71de690f authored by Tom Cherry's avatar Tom Cherry Committed by Gerrit Code Review
Browse files

Merge "logcat: filter based on UID"

parents cba44a3c 9291f0f5
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@

#include <memory>
#include <regex>
#include <set>
#include <string>
#include <utility>
#include <vector>
@@ -358,6 +359,10 @@ Filtering:
  -T '<time>'                 Print the lines since specified time (not imply -d).
                              count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'
                              'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format.
  --uid=<uids>                Only display log messages from UIDs present in the comma separate list
                              <uids>. No name look-up is performed, so UIDs must be provided as
                              numeric values. This option is only useful for the 'root', 'log', and
                              'system' users since only those users can view logs from other users.
)init");

    fprintf(stderr, "\nfilterspecs are a series of \n"
@@ -535,6 +540,7 @@ int Logcat::Run(int argc, char** argv) {
    size_t pid = 0;
    bool got_t = false;
    unsigned id_mask = 0;
    std::set<uid_t> uids;

    if (argc == 2 && !strcmp(argv[1], "--help")) {
        show_help();
@@ -554,6 +560,7 @@ int Logcat::Run(int argc, char** argv) {
        static const char id_str[] = "id";
        static const char wrap_str[] = "wrap";
        static const char print_str[] = "print";
        static const char uid_str[] = "uid";
        // clang-format off
        static const struct option long_options[] = {
          { "binary",        no_argument,       nullptr, 'B' },
@@ -581,6 +588,7 @@ int Logcat::Run(int argc, char** argv) {
          { "statistics",    no_argument,       nullptr, 'S' },
          // hidden and undocumented reserved alias for -t
          { "tail",          required_argument, nullptr, 't' },
          { uid_str,         required_argument, nullptr, 0 },
          // support, but ignore and do not document, the optional argument
          { wrap_str,        optional_argument, nullptr, 0 },
          { nullptr,         0,                 nullptr, 0 }
@@ -631,6 +639,17 @@ int Logcat::Run(int argc, char** argv) {
                if (long_options[option_index].name == id_str) {
                    setId = (optarg && optarg[0]) ? optarg : nullptr;
                }
                if (long_options[option_index].name == uid_str) {
                    auto uid_strings = Split(optarg, delimiters);
                    for (const auto& uid_string : uid_strings) {
                        uid_t uid;
                        if (!ParseUint(uid_string, &uid)) {
                            error(EXIT_FAILURE, 0, "Unable to parse UID '%s'", uid_string.c_str());
                        }
                        uids.emplace(uid);
                    }
                    break;
                }
                break;

            case 's':
@@ -1164,6 +1183,10 @@ If you have enabled significant logging, look into using the -G option to increa
                  LOG_ID_MAX);
        }

        if (!uids.empty() && uids.count(log_msg.entry.uid) == 0) {
            continue;
        }

        PrintDividers(log_msg.id(), printDividers);

        if (print_binary_) {
+83 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

#include <ctype.h>
#include <dirent.h>
#include <pwd.h>
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
@@ -28,10 +29,12 @@
#include <unistd.h>

#include <memory>
#include <regex>
#include <string>

#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <gtest/gtest.h>
@@ -1748,3 +1751,83 @@ TEST(logcat, invalid_buffer) {

  ASSERT_TRUE(android::base::StartsWith(output, "unknown buffer foo\n"));
}

static void SniffUid(const std::string& line, uid_t& uid) {
    auto uid_regex = std::regex{"\\S+\\s+\\S+\\s+(\\S+).*"};

    auto trimmed_line = android::base::Trim(line);

    std::smatch match_results;
    ASSERT_TRUE(std::regex_match(trimmed_line, match_results, uid_regex))
            << "Unable to find UID in line '" << trimmed_line << "'";
    auto uid_string = match_results[1];
    if (!android::base::ParseUint(uid_string, &uid)) {
        auto pwd = getpwnam(uid_string.str().c_str());
        ASSERT_NE(nullptr, pwd) << "uid '" << uid_string << "' in line '" << trimmed_line << "'";
        uid = pwd->pw_uid;
    }
}

static void UidsInLog(std::optional<std::vector<uid_t>> filter_uid, std::map<uid_t, size_t>& uids) {
    std::string command;
    if (filter_uid) {
        std::vector<std::string> uid_strings;
        for (const auto& uid : *filter_uid) {
            uid_strings.emplace_back(std::to_string(uid));
        }
        command = android::base::StringPrintf(logcat_executable
                                              " -v uid -b all -d 2>/dev/null --uid=%s",
                                              android::base::Join(uid_strings, ",").c_str());
    } else {
        command = logcat_executable " -v uid -b all -d 2>/dev/null";
    }
    auto fp = std::unique_ptr<FILE, decltype(&pclose)>(popen(command.c_str(), "r"), pclose);
    ASSERT_NE(nullptr, fp);

    char buffer[BIG_BUFFER];
    while (fgets(buffer, sizeof(buffer), fp.get())) {
        // Ignore dividers, e.g. '--------- beginning of radio'
        if (android::base::StartsWith(buffer, "---------")) {
            continue;
        }
        uid_t uid;
        SniffUid(buffer, uid);
        uids[uid]++;
    }
}

static std::vector<uid_t> TopTwoInMap(const std::map<uid_t, size_t>& uids) {
    std::pair<uid_t, size_t> top = {0, 0};
    std::pair<uid_t, size_t> second = {0, 0};
    for (const auto& [uid, count] : uids) {
        if (count > top.second) {
            top = second;
            top = {uid, count};
        } else if (count > second.second) {
            second = {uid, count};
        }
    }
    return {top.first, second.first};
}

TEST(logcat, uid_filter) {
    std::map<uid_t, size_t> uids;
    UidsInLog({}, uids);

    ASSERT_GT(uids.size(), 2U);
    auto top_uids = TopTwoInMap(uids);

    // Test filtering with --uid=<top uid>
    std::map<uid_t, size_t> uids_only_top;
    std::vector<uid_t> top_uid = {top_uids[0]};
    UidsInLog(top_uid, uids_only_top);

    EXPECT_EQ(1U, uids_only_top.size());

    // Test filtering with --uid=<top uid>,<2nd top uid>
    std::map<uid_t, size_t> uids_only_top2;
    std::vector<uid_t> top2_uids = {top_uids[0], top_uids[1]};
    UidsInLog(top2_uids, uids_only_top2);

    EXPECT_EQ(2U, uids_only_top2.size());
}