diff options
| -rw-r--r-- | cmds/dumpstate/Android.bp | 52 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.cpp | 173 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.h | 8 | ||||
| -rw-r--r-- | cmds/dumpstate/tests/dumpstate_smoke_test.cpp | 286 | ||||
| -rw-r--r-- | cmds/dumpsys/dumpsys.cpp | 2 | ||||
| -rw-r--r-- | cmds/dumpsys/dumpsys.h | 2 |
6 files changed, 478 insertions, 45 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index 35cff5f62e..562898dd48 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -14,7 +14,7 @@ // limitations under the License. cc_defaults { - name: "dumpstate_defaults", + name: "dumpstate_cflag_defaults", cflags: [ "-Wall", "-Werror", @@ -26,7 +26,7 @@ cc_defaults { cc_library_shared { name: "libdumpstateutil", - defaults: ["dumpstate_defaults"], + defaults: ["dumpstate_cflag_defaults"], vendor_available: true, vndk: { enabled: true, @@ -47,7 +47,7 @@ cc_library_shared { cc_library_shared { name: "libdumpstateaidl", - defaults: ["dumpstate_defaults"], + defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "libbinder", "libutils", @@ -63,9 +63,9 @@ cc_library_shared { ], } -cc_binary { - name: "dumpstate", - defaults: ["dumpstate_defaults"], +cc_defaults { + name: "dumpstate_defaults", + defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "android.hardware.dumpstate@1.0", "libziparchive", @@ -82,12 +82,22 @@ cc_binary { "libutils", ], srcs: [ - "DumpstateInternal.cpp", "DumpstateSectionReporter.cpp", "DumpstateService.cpp", - "main.cpp", "utils.cpp", + ], + static_libs: [ + "libdumpsys", + "libserviceutils" + ], +} + +cc_binary { + name: "dumpstate", + defaults: ["dumpstate_defaults"], + srcs: [ "dumpstate.cpp", + "main.cpp", ], init_rc: ["dumpstate.rc"], } @@ -95,24 +105,18 @@ cc_binary { cc_test { name: "dumpstate_test", defaults: ["dumpstate_defaults"], - shared_libs: [ - "libziparchive", - "libbase", - "libbinder", - "libcutils", - "libdebuggerd_client", - "libdumpstateaidl", - "libdumpstateutil", - "libhidlbase", - "libhidltransport", - "liblog", - "libutils", - ], srcs: [ - "DumpstateInternal.cpp", - "DumpstateService.cpp", - "utils.cpp", "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], } + +cc_test { + name: "dumpstate_smoke_test", + defaults: ["dumpstate_defaults"], + srcs: [ + "dumpstate.cpp", + "tests/dumpstate_smoke_test.cpp", + ], + static_libs: ["libgmock"], +}
\ No newline at end of file diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 93f8d4354b..1a25335861 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -25,6 +25,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/poll.h> #include <sys/prctl.h> #include <sys/resource.h> #include <sys/stat.h> @@ -46,22 +47,39 @@ #include <android/hidl/manager/1.0/IServiceManager.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <dumpsys.h> #include <hidl/ServiceManagement.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> - +#include <serviceutils/PriorityDumper.h> #include "DumpstateInternal.h" +#include "DumpstateSectionReporter.h" #include "DumpstateService.h" #include "dumpstate.h" using ::android::hardware::dumpstate::V1_0::IDumpstateDevice; +using ::std::literals::chrono_literals::operator""ms; +using ::std::literals::chrono_literals::operator""s; // TODO: remove once moved to namespace +using android::defaultServiceManager; +using android::Dumpsys; +using android::INVALID_OPERATION; +using android::IServiceManager; +using android::OK; +using android::sp; +using android::status_t; +using android::String16; +using android::String8; +using android::TIMED_OUT; +using android::UNKNOWN_ERROR; +using android::Vector; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::PropertiesHelper; +using android::os::dumpstate::DumpstateSectionReporter; using android::os::dumpstate::GetPidByName; +using android::os::dumpstate::PropertiesHelper; /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; @@ -127,6 +145,8 @@ static const std::string ZIP_ROOT_DIR = "FS"; // Must be hardcoded because dumpstate HAL implementation need SELinux access to it static const std::string kDumpstateBoardPath = "/bugreports/"; +static const std::string kProtoPath = "proto/"; +static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { "dumpstate_board.txt", "dumpstate_board.bin" @@ -221,7 +241,7 @@ static bool AddDumps(const std::vector<DumpData>::const_iterator start, } if (ds.IsZipping() && add_to_zip) { - if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) { + if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) { MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str()); } } else { @@ -708,11 +728,12 @@ static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh" }; -bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { +status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, + std::chrono::milliseconds timeout = 0ms) { if (!IsZipping()) { MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", entry_name.c_str()); - return false; + return INVALID_OPERATION; } std::string valid_name = entry_name; @@ -734,32 +755,55 @@ bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(), ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } + auto start = std::chrono::steady_clock::now(); + auto end = start + timeout; + struct pollfd pfd = {fd, POLLIN}; std::vector<uint8_t> buffer(65536); while (1) { + if (timeout.count() > 0) { + // lambda to recalculate the timeout. + auto time_left_ms = [end]() { + auto now = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); + return std::max(diff.count(), 0LL); + }; + + int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); + if (rc < 0) { + MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(), + strerror(errno)); + return -errno; + } else if (rc == 0) { + MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms", + entry_name.c_str(), strerror(errno), timeout.count()); + return TIMED_OUT; + } + } + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); if (bytes_read == 0) { break; } else if (bytes_read == -1) { MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno)); - return false; + return -errno; } err = zip_writer_->WriteBytes(buffer.data(), bytes_read); if (err) { MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } } err = zip_writer_->FinishEntry(); if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } - return true; + return OK; } bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) { @@ -770,12 +814,12 @@ bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& en return false; } - return AddZipEntryFromFd(entry_name, fd.get()); + return (AddZipEntryFromFd(entry_name, fd.get()) == OK); } /* adds a file to the existing zipped bugreport */ static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) { - return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1; + return (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) == OK) ? 0 : 1; } void Dumpstate::AddDir(const std::string& dir, bool recursive) { @@ -1069,11 +1113,97 @@ static void DumpIpAddrAndRules() { RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } +void RunDumpsysText(const std::string& title, int priority, std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + sp<android::IServiceManager> sm = defaultServiceManager(); + Dumpsys dumpsys(sm.get()); + DurationReporter duration_reporter(title); + Vector<String16> args; + Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); + + if (!title.empty()) { + dprintf(STDOUT_FILENO, "------ %s (%s) ------\n", title.c_str(), "/system/bin/dumpsys"); + fsync(STDOUT_FILENO); + } + + auto start = std::chrono::steady_clock::now(); + Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false); + for (const String16& service : services) { + std::string path(title); + path.append(" - ").append(String8(service).c_str()); + DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); + size_t bytes_written = 0; + status_t status = dumpsys.startDumpThread(service, args); + if (status == OK) { + dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); + std::chrono::duration<double> elapsed_seconds; + status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, + /* as_proto = */ false, elapsed_seconds, bytes_written); + section_reporter.setSize(bytes_written); + dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); + bool dump_complete = (status == OK); + dumpsys.stopDumpThread(dump_complete); + } + section_reporter.setStatus(status); + + auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start); + if (elapsed_duration > timeout) { + MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), + elapsed_duration.count()); + break; + } + } +} + +void RunDumpsysProto(const std::string& title, int priority, std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + sp<android::IServiceManager> sm = defaultServiceManager(); + Dumpsys dumpsys(sm.get()); + Vector<String16> args; + Dumpsys::setServiceArgs(args, /* asProto = */ true, priority); + DurationReporter duration_reporter(title); + + auto start = std::chrono::steady_clock::now(); + Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true); + for (const String16& service : services) { + std::string path(kProtoPath); + path.append(String8(service).c_str()); + if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { + path.append("_CRITICAL"); + } else if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) { + path.append("_HIGH"); + } + path.append(kProtoExt); + DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); + status_t status = dumpsys.startDumpThread(service, args); + if (status == OK) { + status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); + bool dumpTerminated = (status == OK); + dumpsys.stopDumpThread(dumpTerminated); + } + ZipWriter::FileEntry file_entry; + ds.zip_writer_->GetLastEntry(&file_entry); + section_reporter.setSize(file_entry.compressed_size); + section_reporter.setStatus(status); + + auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start); + if (elapsed_duration > timeout) { + MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), + elapsed_duration.count()); + break; + } + } +} + // Runs dumpsys on services that must dump first and and will take less than 100ms to dump. static void RunDumpsysCritical() { if (ds.CurrentVersionSupportsPriorityDumps()) { - RunDumpsys("DUMPSYS CRITICAL", {"--priority", "CRITICAL"}, - CommandOptions::WithTimeout(5).DropRoot().Build()); + RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); + RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); } else { RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"}, CommandOptions::WithTimeout(90).DropRoot().Build()); @@ -1085,8 +1215,13 @@ static void RunDumpsysCritical() { // Runs dumpsys on services that must dump first but can take up to 250ms to dump. static void RunDumpsysHigh() { if (ds.CurrentVersionSupportsPriorityDumps()) { - RunDumpsys("DUMPSYS HIGH", {"--priority", "HIGH"}, - CommandOptions::WithTimeout(20).DropRoot().Build()); + // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both + // high priority. Reduce timeout once they are able to dump in a shorter time or + // moved to a parallel task. + RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 90s, /* service_timeout= */ 30s); + RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 5s, /* service_timeout= */ 1s); } else { RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}); } @@ -1095,8 +1230,10 @@ static void RunDumpsysHigh() { // Runs dumpsys on services that must dump but can take up to 10s to dump. static void RunDumpsysNormal() { if (ds.CurrentVersionSupportsPriorityDumps()) { - RunDumpsys("DUMPSYS NORMAL", {"--priority", "NORMAL"}, - CommandOptions::WithTimeout(90).DropRoot().Build()); + RunDumpsysText("DUMPSYS", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); + RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); } else { RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 843c545e50..2554b6374b 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -226,8 +226,14 @@ class Dumpstate { /* * Adds a new entry to the existing zip file. + * + * |entry_name| destination path of the new entry. + * |fd| file descriptor to read from. + * |timeout| timeout to terminate the read if not completed. Set + * value of 0s (default) to disable timeout. */ - bool AddZipEntryFromFd(const std::string& entry_name, int fd); + android::status_t AddZipEntryFromFd(const std::string& entry_name, int fd, + std::chrono::milliseconds timeout); /* * Adds a text entry entry to the existing zip file. diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp new file mode 100644 index 0000000000..61a5ef5b7d --- /dev/null +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2018 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <fcntl.h> +#include <libgen.h> + +#include <android-base/file.h> +#include <cutils/properties.h> +#include <ziparchive/zip_archive.h> + +#include "dumpstate.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +namespace android { +namespace os { +namespace dumpstate { + +using ::testing::Test; +using ::std::literals::chrono_literals::operator""s; + +struct SectionInfo { + std::string name; + status_t status; + int32_t size_bytes; + int32_t duration_ms; +}; + +/** + * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the + * section details generated by dumpstate are added to a vector to be used by Tests later. + */ +class DumpstateListener : public IDumpstateListener { + public: + int outFd_, max_progress_; + std::shared_ptr<std::vector<SectionInfo>> sections_; + DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections) + : outFd_(fd), max_progress_(5000), sections_(sections) { + } + binder::Status onProgressUpdated(int32_t progress) override { + dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_); + return binder::Status::ok(); + } + binder::Status onMaxProgressUpdated(int32_t max_progress) override { + max_progress_ = max_progress; + return binder::Status::ok(); + } + binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, + int32_t duration_ms) override { + sections_->push_back({name, status, size_bytes, duration_ms}); + return binder::Status::ok(); + } + IBinder* onAsBinder() override { + return nullptr; + } +}; + +/** + * Generates bug report and provide access to the bug report file and other info for other tests. + * Since bug report generation is slow, the bugreport is only generated once. + */ +class ZippedBugreportGenerationTest : public Test { + public: + static std::shared_ptr<std::vector<SectionInfo>> sections; + static Dumpstate& ds; + static std::chrono::milliseconds duration; + static void SetUpTestCase() { + property_set("dumpstate.options", "bugreportplus"); + // clang-format off + char* argv[] = { + (char*)"dumpstate", + (char*)"-d", + (char*)"-z", + (char*)"-B", + (char*)"-o", + (char*)dirname(android::base::GetExecutablePath().c_str()) + }; + // clang-format on + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections)); + ds.listener_ = listener; + ds.listener_name_ = "Smokey"; + ds.report_section_ = true; + auto start = std::chrono::steady_clock::now(); + run_main(ARRAY_SIZE(argv), argv); + auto end = std::chrono::steady_clock::now(); + duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + } + + static const char* getZipFilePath() { + return ds.GetPath(".zip").c_str(); + } +}; +std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections = + std::make_shared<std::vector<SectionInfo>>(); +Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance(); +std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s; + +TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) { + EXPECT_EQ(access(getZipFilePath(), F_OK), 0); +} + +TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) { + struct stat st; + EXPECT_EQ(stat(getZipFilePath(), &st), 0); + EXPECT_GE(st.st_size, 3000000 /* 3MB */); + EXPECT_LE(st.st_size, 30000000 /* 30MB */); +} + +TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) { + EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time " + << duration.count() << " s."; + EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time " + << duration.count() << " s."; +} + +/** + * Run tests on contents of zipped bug report. + */ +class ZippedBugReportContentsTest : public Test { + public: + ZipArchiveHandle handle; + void SetUp() { + ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0); + } + void TearDown() { + CloseArchive(handle); + } + + void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { + ZipEntry entry; + EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0); + EXPECT_GT(entry.uncompressed_length, minsize); + EXPECT_LT(entry.uncompressed_length, maxsize); + } +}; + +TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { + ZipEntry mainEntryLoc; + // contains main entry name file + EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0); + + char* buf = new char[mainEntryLoc.uncompressed_length]; + ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); + delete[] buf; + + // contains main entry file + FileExists(buf, 1000000U, 50000000U); +} + +TEST_F(ZippedBugReportContentsTest, ContainsVersion) { + ZipEntry entry; + // contains main entry name file + EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0); + + char* buf = new char[entry.uncompressed_length + 1]; + ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); + buf[entry.uncompressed_length] = 0; + EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str()); + delete[] buf; +} + +TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { + FileExists("dumpstate_board.bin", 1000000U, 80000000U); + FileExists("dumpstate_board.txt", 100000U, 1000000U); +} + +// Spot check on some files pulled from the file system +TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { + // FS/proc/*/mountinfo size > 0 + FileExists("FS/proc/1/mountinfo", 0U, 100000U); + + // FS/data/misc/profiles/cur/0/*/primary.prof size > 0 + FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U); +} + +/** + * Runs tests on section data generated by dumpstate and captured by DumpstateListener. + */ +class BugreportSectionTest : public Test { + public: + int numMatches(const std::string& substring) { + int matches = 0; + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + if (section.name.find(substring) != std::string::npos) { + matches++; + } + } + return matches; + } + void SectionExists(const std::string& sectionName, int minsize) { + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + if (sectionName == section.name) { + EXPECT_GE(section.size_bytes, minsize); + return; + } + } + FAIL() << sectionName << " not found."; + } +}; + +// Test all sections are generated without timeouts or errors +TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; + } +} + +TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { + int numSections = numMatches("DUMPSYS CRITICAL"); + EXPECT_GE(numSections, 3); +} + +TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { + int numSections = numMatches("DUMPSYS HIGH"); + EXPECT_GE(numSections, 2); +} + +TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { + int allSections = numMatches("DUMPSYS"); + int criticalSections = numMatches("DUMPSYS CRITICAL"); + int highSections = numMatches("DUMPSYS HIGH"); + int normalSections = allSections - criticalSections - highSections; + + EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections + << "High:" << highSections << "Normal:" << normalSections << ")"; +} + +TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { + int numSections = numMatches("proto/"); + EXPECT_GE(numSections, 1); +} + +// Test if some critical sections are being generated. +TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); +} + +TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { + SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); + SectionExists("DUMPSYS - activity", /* bytes= */ 10000); +} + +TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); +} + +TEST_F(BugreportSectionTest, WindowSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); +} + +TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { + SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); + SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); +} + +TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { + SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); +} + +TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { + SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); +} + +TEST_F(BugreportSectionTest, WifiSectionGenerated) { + SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); +} + +} // namespace dumpstate +} // namespace os +} // namespace android diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index ae0cc01ee4..ca7d95e152 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -283,7 +283,7 @@ Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByPro return services; } -void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const { +void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) { if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) || (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) { args.add(String16("-a")); diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 1d78aa426b..84f3b0236e 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -49,7 +49,7 @@ class Dumpsys { * @param priorityFlags indicates priority of dump by passing additional priority args * to the service */ - void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) const; + static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); /** * Starts a thread to connect to a service and get its dump output. The thread redirects |