Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright (C) 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <gmock/gmock.h>
     18 #include <gtest/gtest.h>
     19 
     20 #include <fcntl.h>
     21 #include <libgen.h>
     22 
     23 #include <android-base/file.h>
     24 #include <android/os/BnDumpstate.h>
     25 #include <android/os/BnDumpstateListener.h>
     26 #include <binder/IServiceManager.h>
     27 #include <binder/ProcessState.h>
     28 #include <cutils/properties.h>
     29 #include <ziparchive/zip_archive.h>
     30 
     31 #include "dumpstate.h"
     32 
     33 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
     34 
     35 namespace android {
     36 namespace os {
     37 namespace dumpstate {
     38 
     39 using ::testing::Test;
     40 using ::std::literals::chrono_literals::operator""s;
     41 using android::base::unique_fd;
     42 
     43 class DumpstateListener;
     44 
     45 namespace {
     46 
     47 sp<IDumpstate> GetDumpstateService() {
     48     return android::interface_cast<IDumpstate>(
     49         android::defaultServiceManager()->getService(String16("dumpstate")));
     50 }
     51 
     52 int OpenForWrite(const std::string& filename) {
     53     return TEMP_FAILURE_RETRY(open(filename.c_str(),
     54                                    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
     55                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
     56 }
     57 
     58 }  // namespace
     59 
     60 struct SectionInfo {
     61     std::string name;
     62     status_t status;
     63     int32_t size_bytes;
     64     int32_t duration_ms;
     65 };
     66 
     67 /**
     68  * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
     69  * section details generated by dumpstate are added to a vector to be used by Tests later.
     70  */
     71 class DumpstateListener : public BnDumpstateListener {
     72   public:
     73     DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
     74         : out_fd_(fd), sections_(sections) {
     75     }
     76 
     77     DumpstateListener(int fd) : out_fd_(fd) {
     78     }
     79 
     80     binder::Status onProgress(int32_t progress) override {
     81         dprintf(out_fd_, "\rIn progress %d", progress);
     82         return binder::Status::ok();
     83     }
     84 
     85     binder::Status onError(int32_t error_code) override {
     86         std::lock_guard<std::mutex> lock(lock_);
     87         error_code_ = error_code;
     88         dprintf(out_fd_, "\rError code %d", error_code);
     89         return binder::Status::ok();
     90     }
     91 
     92     binder::Status onFinished() override {
     93         std::lock_guard<std::mutex> lock(lock_);
     94         is_finished_ = true;
     95         dprintf(out_fd_, "\rFinished");
     96         return binder::Status::ok();
     97     }
     98 
     99     binder::Status onProgressUpdated(int32_t progress) override {
    100         dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_);
    101         return binder::Status::ok();
    102     }
    103 
    104     binder::Status onMaxProgressUpdated(int32_t max_progress) override {
    105         std::lock_guard<std::mutex> lock(lock_);
    106         max_progress_ = max_progress;
    107         return binder::Status::ok();
    108     }
    109 
    110     binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
    111                                      int32_t duration_ms) override {
    112         std::lock_guard<std::mutex> lock(lock_);
    113         if (sections_.get() != nullptr) {
    114             sections_->push_back({name, status, size_bytes, duration_ms});
    115         }
    116         return binder::Status::ok();
    117     }
    118 
    119     bool getIsFinished() {
    120         std::lock_guard<std::mutex> lock(lock_);
    121         return is_finished_;
    122     }
    123 
    124     int getErrorCode() {
    125         std::lock_guard<std::mutex> lock(lock_);
    126         return error_code_;
    127     }
    128 
    129   private:
    130     int out_fd_;
    131     int max_progress_ = 5000;
    132     int error_code_ = -1;
    133     bool is_finished_ = false;
    134     std::shared_ptr<std::vector<SectionInfo>> sections_;
    135     std::mutex lock_;
    136 };
    137 
    138 /**
    139  * Generates bug report and provide access to the bug report file and other info for other tests.
    140  * Since bug report generation is slow, the bugreport is only generated once.
    141  */
    142 class ZippedBugreportGenerationTest : public Test {
    143   public:
    144     static std::shared_ptr<std::vector<SectionInfo>> sections;
    145     static Dumpstate& ds;
    146     static std::chrono::milliseconds duration;
    147     static void SetUpTestCase() {
    148         property_set("dumpstate.options", "bugreportplus");
    149         // clang-format off
    150         char* argv[] = {
    151             (char*)"dumpstate",
    152             (char*)"-d",
    153             (char*)"-z",
    154             (char*)"-B",
    155             (char*)"-o",
    156             (char*)dirname(android::base::GetExecutablePath().c_str())
    157         };
    158         // clang-format on
    159         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
    160         ds.listener_ = listener;
    161         ds.listener_name_ = "Smokey";
    162         ds.report_section_ = true;
    163         auto start = std::chrono::steady_clock::now();
    164         ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
    165         auto end = std::chrono::steady_clock::now();
    166         duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    167     }
    168 
    169     static const char* getZipFilePath() {
    170         return ds.GetPath(".zip").c_str();
    171     }
    172 };
    173 std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
    174     std::make_shared<std::vector<SectionInfo>>();
    175 Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
    176 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
    177 
    178 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
    179     EXPECT_EQ(access(getZipFilePath(), F_OK), 0);
    180 }
    181 
    182 TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) {
    183     struct stat st;
    184     EXPECT_EQ(stat(getZipFilePath(), &st), 0);
    185     EXPECT_GE(st.st_size, 3000000 /* 3MB */);
    186     EXPECT_LE(st.st_size, 30000000 /* 30MB */);
    187 }
    188 
    189 TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) {
    190     EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time "
    191                              << duration.count() << " s.";
    192     EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time "
    193                               << duration.count() << " s.";
    194 }
    195 
    196 /**
    197  * Run tests on contents of zipped bug report.
    198  */
    199 class ZippedBugReportContentsTest : public Test {
    200   public:
    201     ZipArchiveHandle handle;
    202     void SetUp() {
    203         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0);
    204     }
    205     void TearDown() {
    206         CloseArchive(handle);
    207     }
    208 
    209     void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) {
    210         ZipEntry entry;
    211         EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0);
    212         EXPECT_GT(entry.uncompressed_length, minsize);
    213         EXPECT_LT(entry.uncompressed_length, maxsize);
    214     }
    215 };
    216 
    217 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
    218     ZipEntry mainEntryLoc;
    219     // contains main entry name file
    220     EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0);
    221 
    222     char* buf = new char[mainEntryLoc.uncompressed_length];
    223     ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length);
    224     delete[] buf;
    225 
    226     // contains main entry file
    227     FileExists(buf, 1000000U, 50000000U);
    228 }
    229 
    230 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
    231     ZipEntry entry;
    232     // contains main entry name file
    233     EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0);
    234 
    235     char* buf = new char[entry.uncompressed_length + 1];
    236     ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
    237     buf[entry.uncompressed_length] = 0;
    238     EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
    239     delete[] buf;
    240 }
    241 
    242 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
    243     FileExists("dumpstate_board.bin", 1000000U, 80000000U);
    244     FileExists("dumpstate_board.txt", 100000U, 1000000U);
    245 }
    246 
    247 // Spot check on some files pulled from the file system
    248 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
    249     // FS/proc/*/mountinfo size > 0
    250     FileExists("FS/proc/1/mountinfo", 0U, 100000U);
    251 
    252     // FS/data/misc/profiles/cur/0/*/primary.prof size > 0
    253     FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U);
    254 }
    255 
    256 /**
    257  * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
    258  */
    259 class BugreportSectionTest : public Test {
    260   public:
    261     int numMatches(const std::string& substring) {
    262         int matches = 0;
    263         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
    264             if (section.name.find(substring) != std::string::npos) {
    265                 matches++;
    266             }
    267         }
    268         return matches;
    269     }
    270     void SectionExists(const std::string& sectionName, int minsize) {
    271         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
    272             if (sectionName == section.name) {
    273                 EXPECT_GE(section.size_bytes, minsize);
    274                 return;
    275             }
    276         }
    277         FAIL() << sectionName << " not found.";
    278     }
    279 };
    280 
    281 // Test all sections are generated without timeouts or errors
    282 TEST_F(BugreportSectionTest, GeneratedWithoutErrors) {
    283     for (auto const& section : *ZippedBugreportGenerationTest::sections) {
    284         EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status;
    285     }
    286 }
    287 
    288 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
    289     int numSections = numMatches("DUMPSYS CRITICAL");
    290     EXPECT_GE(numSections, 3);
    291 }
    292 
    293 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
    294     int numSections = numMatches("DUMPSYS HIGH");
    295     EXPECT_GE(numSections, 2);
    296 }
    297 
    298 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
    299     int allSections = numMatches("DUMPSYS");
    300     int criticalSections = numMatches("DUMPSYS CRITICAL");
    301     int highSections = numMatches("DUMPSYS HIGH");
    302     int normalSections = allSections - criticalSections - highSections;
    303 
    304     EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
    305                                   << "High:" << highSections << "Normal:" << normalSections << ")";
    306 }
    307 
    308 TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
    309     int numSections = numMatches("proto/");
    310     EXPECT_GE(numSections, 1);
    311 }
    312 
    313 // Test if some critical sections are being generated.
    314 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
    315     SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
    316 }
    317 
    318 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
    319     SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
    320     SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
    321 }
    322 
    323 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
    324     SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
    325 }
    326 
    327 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
    328     SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
    329 }
    330 
    331 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
    332     SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
    333     SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
    334 }
    335 
    336 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
    337     SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
    338 }
    339 
    340 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
    341     SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
    342 }
    343 
    344 TEST_F(BugreportSectionTest, WifiSectionGenerated) {
    345     SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
    346 }
    347 
    348 class DumpstateBinderTest : public Test {
    349   protected:
    350     void SetUp() override {
    351         // In case there is a stray service, stop it first.
    352         property_set("ctl.stop", "bugreportd");
    353         // dry_run results in a faster bugreport.
    354         property_set("dumpstate.dry_run", "true");
    355         // We need to receive some async calls later. Ensure we have binder threads.
    356         ProcessState::self()->startThreadPool();
    357     }
    358 
    359     void TearDown() override {
    360         property_set("ctl.stop", "bugreportd");
    361         property_set("dumpstate.dry_run", "");
    362 
    363         unlink("/data/local/tmp/tmp.zip");
    364         unlink("/data/local/tmp/tmp.png");
    365     }
    366 
    367     // Waits until listener gets the callbacks.
    368     void WaitTillExecutionComplete(DumpstateListener* listener) {
    369         // Wait till one of finished, error or timeout.
    370         static const int kBugreportTimeoutSeconds = 120;
    371         int i = 0;
    372         while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
    373                i < kBugreportTimeoutSeconds) {
    374             sleep(1);
    375             i++;
    376         }
    377     }
    378 };
    379 
    380 TEST_F(DumpstateBinderTest, Baseline) {
    381     // In the beginning dumpstate binder service is not running.
    382     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
    383     EXPECT_EQ(ds_binder, nullptr);
    384 
    385     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
    386     // and makes it wait.
    387     property_set("dumpstate.dry_run", "true");
    388     property_set("ctl.start", "bugreportd");
    389 
    390     // Now we are able to retrieve dumpstate binder service.
    391     ds_binder = GetDumpstateService();
    392     EXPECT_NE(ds_binder, nullptr);
    393 
    394     // Prepare arguments
    395     unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
    396     unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
    397 
    398     EXPECT_NE(bugreport_fd.get(), -1);
    399     EXPECT_NE(screenshot_fd.get(), -1);
    400 
    401     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
    402     android::binder::Status status =
    403         ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
    404                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
    405     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
    406     // gets expected callbacks.
    407     EXPECT_TRUE(status.isOk());
    408     WaitTillExecutionComplete(listener.get());
    409 
    410     // Bugreport generation requires user consent, which we cannot get in a test set up,
    411     // so instead of getting is_finished_, we are more likely to get a consent error.
    412     EXPECT_TRUE(
    413         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
    414         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
    415 
    416     // The service should have died on its own, freeing itself up for a new invocation.
    417     sleep(2);
    418     ds_binder = GetDumpstateService();
    419     EXPECT_EQ(ds_binder, nullptr);
    420 }
    421 
    422 TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
    423     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
    424     // and makes it wait.
    425     property_set("ctl.start", "bugreportd");
    426     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
    427     EXPECT_NE(ds_binder, nullptr);
    428 
    429     // Prepare arguments
    430     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
    431     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
    432 
    433     EXPECT_NE(bugreport_fd.get(), -1);
    434     EXPECT_NE(screenshot_fd.get(), -1);
    435 
    436     // Call startBugreport with bad arguments.
    437     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
    438     android::binder::Status status =
    439         ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
    440                                   2000,  // invalid bugreport mode
    441                                   listener);
    442     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
    443 
    444     // The service should have died, freeing itself up for a new invocation.
    445     sleep(2);
    446     ds_binder = GetDumpstateService();
    447     EXPECT_EQ(ds_binder, nullptr);
    448 }
    449 
    450 TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
    451     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
    452     // and makes it wait.
    453     property_set("dumpstate.dry_run", "true");
    454     property_set("ctl.start", "bugreportd");
    455     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
    456     EXPECT_NE(ds_binder, nullptr);
    457 
    458     // Prepare arguments
    459     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
    460     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
    461 
    462     EXPECT_NE(bugreport_fd.get(), -1);
    463     EXPECT_NE(screenshot_fd.get(), -1);
    464 
    465     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
    466     android::binder::Status status =
    467         ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
    468                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
    469     EXPECT_TRUE(status.isOk());
    470 
    471     // try to make another call to startBugreport. This should fail.
    472     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
    473     status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
    474                                        Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
    475     EXPECT_FALSE(status.isOk());
    476     WaitTillExecutionComplete(listener2.get());
    477     EXPECT_EQ(listener2->getErrorCode(),
    478               IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
    479 
    480     // Meanwhile the first call works as expected. Service should not die in this case.
    481     WaitTillExecutionComplete(listener1.get());
    482 
    483     // Bugreport generation requires user consent, which we cannot get in a test set up,
    484     // so instead of getting is_finished_, we are more likely to get a consent error.
    485     EXPECT_TRUE(
    486         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
    487         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
    488 }
    489 
    490 }  // namespace dumpstate
    491 }  // namespace os
    492 }  // namespace android
    493