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 <cutils/properties.h> 25 #include <ziparchive/zip_archive.h> 26 27 #include "dumpstate.h" 28 29 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 30 31 namespace android { 32 namespace os { 33 namespace dumpstate { 34 35 using ::testing::Test; 36 using ::std::literals::chrono_literals::operator""s; 37 38 struct SectionInfo { 39 std::string name; 40 status_t status; 41 int32_t size_bytes; 42 int32_t duration_ms; 43 }; 44 45 /** 46 * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the 47 * section details generated by dumpstate are added to a vector to be used by Tests later. 48 */ 49 class DumpstateListener : public IDumpstateListener { 50 public: 51 int outFd_, max_progress_; 52 std::shared_ptr<std::vector<SectionInfo>> sections_; 53 DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections) 54 : outFd_(fd), max_progress_(5000), sections_(sections) { 55 } 56 binder::Status onProgressUpdated(int32_t progress) override { 57 dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_); 58 return binder::Status::ok(); 59 } 60 binder::Status onMaxProgressUpdated(int32_t max_progress) override { 61 max_progress_ = max_progress; 62 return binder::Status::ok(); 63 } 64 binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, 65 int32_t duration_ms) override { 66 sections_->push_back({name, status, size_bytes, duration_ms}); 67 return binder::Status::ok(); 68 } 69 IBinder* onAsBinder() override { 70 return nullptr; 71 } 72 }; 73 74 /** 75 * Generates bug report and provide access to the bug report file and other info for other tests. 76 * Since bug report generation is slow, the bugreport is only generated once. 77 */ 78 class ZippedBugreportGenerationTest : public Test { 79 public: 80 static std::shared_ptr<std::vector<SectionInfo>> sections; 81 static Dumpstate& ds; 82 static std::chrono::milliseconds duration; 83 static void SetUpTestCase() { 84 property_set("dumpstate.options", "bugreportplus"); 85 // clang-format off 86 char* argv[] = { 87 (char*)"dumpstate", 88 (char*)"-d", 89 (char*)"-z", 90 (char*)"-B", 91 (char*)"-o", 92 (char*)dirname(android::base::GetExecutablePath().c_str()) 93 }; 94 // clang-format on 95 sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections)); 96 ds.listener_ = listener; 97 ds.listener_name_ = "Smokey"; 98 ds.report_section_ = true; 99 auto start = std::chrono::steady_clock::now(); 100 run_main(ARRAY_SIZE(argv), argv); 101 auto end = std::chrono::steady_clock::now(); 102 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); 103 } 104 105 static const char* getZipFilePath() { 106 return ds.GetPath(".zip").c_str(); 107 } 108 }; 109 std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections = 110 std::make_shared<std::vector<SectionInfo>>(); 111 Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance(); 112 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s; 113 114 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) { 115 EXPECT_EQ(access(getZipFilePath(), F_OK), 0); 116 } 117 118 TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) { 119 struct stat st; 120 EXPECT_EQ(stat(getZipFilePath(), &st), 0); 121 EXPECT_GE(st.st_size, 3000000 /* 3MB */); 122 EXPECT_LE(st.st_size, 30000000 /* 30MB */); 123 } 124 125 TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) { 126 EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time " 127 << duration.count() << " s."; 128 EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time " 129 << duration.count() << " s."; 130 } 131 132 /** 133 * Run tests on contents of zipped bug report. 134 */ 135 class ZippedBugReportContentsTest : public Test { 136 public: 137 ZipArchiveHandle handle; 138 void SetUp() { 139 ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0); 140 } 141 void TearDown() { 142 CloseArchive(handle); 143 } 144 145 void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { 146 ZipEntry entry; 147 EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0); 148 EXPECT_GT(entry.uncompressed_length, minsize); 149 EXPECT_LT(entry.uncompressed_length, maxsize); 150 } 151 }; 152 153 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { 154 ZipEntry mainEntryLoc; 155 // contains main entry name file 156 EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0); 157 158 char* buf = new char[mainEntryLoc.uncompressed_length]; 159 ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); 160 delete[] buf; 161 162 // contains main entry file 163 FileExists(buf, 1000000U, 50000000U); 164 } 165 166 TEST_F(ZippedBugReportContentsTest, ContainsVersion) { 167 ZipEntry entry; 168 // contains main entry name file 169 EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0); 170 171 char* buf = new char[entry.uncompressed_length + 1]; 172 ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); 173 buf[entry.uncompressed_length] = 0; 174 EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str()); 175 delete[] buf; 176 } 177 178 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { 179 FileExists("dumpstate_board.bin", 1000000U, 80000000U); 180 FileExists("dumpstate_board.txt", 100000U, 1000000U); 181 } 182 183 // Spot check on some files pulled from the file system 184 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { 185 // FS/proc/*/mountinfo size > 0 186 FileExists("FS/proc/1/mountinfo", 0U, 100000U); 187 188 // FS/data/misc/profiles/cur/0/*/primary.prof size > 0 189 FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U); 190 } 191 192 /** 193 * Runs tests on section data generated by dumpstate and captured by DumpstateListener. 194 */ 195 class BugreportSectionTest : public Test { 196 public: 197 int numMatches(const std::string& substring) { 198 int matches = 0; 199 for (auto const& section : *ZippedBugreportGenerationTest::sections) { 200 if (section.name.find(substring) != std::string::npos) { 201 matches++; 202 } 203 } 204 return matches; 205 } 206 void SectionExists(const std::string& sectionName, int minsize) { 207 for (auto const& section : *ZippedBugreportGenerationTest::sections) { 208 if (sectionName == section.name) { 209 EXPECT_GE(section.size_bytes, minsize); 210 return; 211 } 212 } 213 FAIL() << sectionName << " not found."; 214 } 215 }; 216 217 // Test all sections are generated without timeouts or errors 218 TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { 219 for (auto const& section : *ZippedBugreportGenerationTest::sections) { 220 EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; 221 } 222 } 223 224 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { 225 int numSections = numMatches("DUMPSYS CRITICAL"); 226 EXPECT_GE(numSections, 3); 227 } 228 229 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { 230 int numSections = numMatches("DUMPSYS HIGH"); 231 EXPECT_GE(numSections, 2); 232 } 233 234 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { 235 int allSections = numMatches("DUMPSYS"); 236 int criticalSections = numMatches("DUMPSYS CRITICAL"); 237 int highSections = numMatches("DUMPSYS HIGH"); 238 int normalSections = allSections - criticalSections - highSections; 239 240 EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections 241 << "High:" << highSections << "Normal:" << normalSections << ")"; 242 } 243 244 TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { 245 int numSections = numMatches("proto/"); 246 EXPECT_GE(numSections, 1); 247 } 248 249 // Test if some critical sections are being generated. 250 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { 251 SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); 252 } 253 254 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { 255 SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); 256 SectionExists("DUMPSYS - activity", /* bytes= */ 10000); 257 } 258 259 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { 260 SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); 261 } 262 263 TEST_F(BugreportSectionTest, WindowSectionGenerated) { 264 SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); 265 } 266 267 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { 268 SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); 269 SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); 270 } 271 272 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { 273 SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); 274 } 275 276 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { 277 SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); 278 } 279 280 TEST_F(BugreportSectionTest, WifiSectionGenerated) { 281 SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); 282 } 283 284 } // namespace dumpstate 285 } // namespace os 286 } // namespace android 287