Loading logcat/logcat.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <memory> #include <regex> #include <set> #include <string> #include <utility> #include <vector> Loading Loading @@ -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" Loading Loading @@ -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(); Loading @@ -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' }, Loading Loading @@ -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 } Loading Loading @@ -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': Loading Loading @@ -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_) { Loading logcat/tests/logcat_test.cpp +83 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <ctype.h> #include <dirent.h> #include <pwd.h> #include <signal.h> #include <stdint.h> #include <stdio.h> Loading @@ -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> Loading Loading @@ -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()); } Loading
logcat/logcat.cpp +23 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ #include <memory> #include <regex> #include <set> #include <string> #include <utility> #include <vector> Loading Loading @@ -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" Loading Loading @@ -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(); Loading @@ -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' }, Loading Loading @@ -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 } Loading Loading @@ -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': Loading Loading @@ -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_) { Loading
logcat/tests/logcat_test.cpp +83 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #include <ctype.h> #include <dirent.h> #include <pwd.h> #include <signal.h> #include <stdint.h> #include <stdio.h> Loading @@ -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> Loading Loading @@ -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()); }