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