blob: fc3642c9127a034e5723ec248968ad00b3f01b50 [file] [log] [blame]
Vishnu Naire97d6122018-01-18 13:58:56 -08001/*
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>
Nandana Dutt16d1aee2019-02-15 16:13:53 +000024#include <android/os/BnDumpstate.h>
25#include <android/os/BnDumpstateListener.h>
26#include <binder/IServiceManager.h>
27#include <binder/ProcessState.h>
Vishnu Naire97d6122018-01-18 13:58:56 -080028#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
35namespace android {
36namespace os {
37namespace dumpstate {
38
39using ::testing::Test;
40using ::std::literals::chrono_literals::operator""s;
Nandana Dutt16d1aee2019-02-15 16:13:53 +000041using android::base::unique_fd;
42
43class DumpstateListener;
44
45namespace {
46
47sp<IDumpstate> GetDumpstateService() {
48 return android::interface_cast<IDumpstate>(
49 android::defaultServiceManager()->getService(String16("dumpstate")));
50}
51
52int 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
Vishnu Naire97d6122018-01-18 13:58:56 -080059
60struct 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 */
Nandana Dutt16d1aee2019-02-15 16:13:53 +000071class DumpstateListener : public BnDumpstateListener {
Vishnu Naire97d6122018-01-18 13:58:56 -080072 public:
Vishnu Naire97d6122018-01-18 13:58:56 -080073 DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
Nandana Dutt16d1aee2019-02-15 16:13:53 +000074 : out_fd_(fd), sections_(sections) {
Vishnu Naire97d6122018-01-18 13:58:56 -080075 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +000076
77 DumpstateListener(int fd) : out_fd_(fd) {
78 }
79
Nandana Dutta6a28bd2019-01-14 16:54:38 +000080 binder::Status onProgress(int32_t progress) override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +000081 dprintf(out_fd_, "\rIn progress %d", progress);
Nandana Dutta6a28bd2019-01-14 16:54:38 +000082 return binder::Status::ok();
83 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +000084
Nandana Dutta6a28bd2019-01-14 16:54:38 +000085 binder::Status onError(int32_t error_code) override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +000086 std::lock_guard<std::mutex> lock(lock_);
87 error_code_ = error_code;
88 dprintf(out_fd_, "\rError code %d", error_code);
Nandana Dutta6a28bd2019-01-14 16:54:38 +000089 return binder::Status::ok();
90 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +000091
Nandana Duttcc4ead82019-01-23 08:29:23 +000092 binder::Status onFinished() override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +000093 std::lock_guard<std::mutex> lock(lock_);
94 is_finished_ = true;
95 dprintf(out_fd_, "\rFinished");
Nandana Dutta6a28bd2019-01-14 16:54:38 +000096 return binder::Status::ok();
97 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +000098
Vishnu Naire97d6122018-01-18 13:58:56 -080099 binder::Status onProgressUpdated(int32_t progress) override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000100 dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_);
Vishnu Naire97d6122018-01-18 13:58:56 -0800101 return binder::Status::ok();
102 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000103
Vishnu Naire97d6122018-01-18 13:58:56 -0800104 binder::Status onMaxProgressUpdated(int32_t max_progress) override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000105 std::lock_guard<std::mutex> lock(lock_);
Vishnu Naire97d6122018-01-18 13:58:56 -0800106 max_progress_ = max_progress;
107 return binder::Status::ok();
108 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000109
Vishnu Naire97d6122018-01-18 13:58:56 -0800110 binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
111 int32_t duration_ms) override {
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000112 std::lock_guard<std::mutex> lock(lock_);
113 if (sections_.get() != nullptr) {
114 sections_->push_back({name, status, size_bytes, duration_ms});
115 }
Vishnu Naire97d6122018-01-18 13:58:56 -0800116 return binder::Status::ok();
117 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000118
119 bool getIsFinished() {
120 std::lock_guard<std::mutex> lock(lock_);
121 return is_finished_;
Vishnu Naire97d6122018-01-18 13:58:56 -0800122 }
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000123
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_;
Vishnu Naire97d6122018-01-18 13:58:56 -0800136};
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 */
142class 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();
Nandana Duttf02564e2019-02-15 15:24:24 +0000164 ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
Vishnu Naire97d6122018-01-18 13:58:56 -0800165 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};
173std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
174 std::make_shared<std::vector<SectionInfo>>();
175Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
176std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
177
178TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
179 EXPECT_EQ(access(getZipFilePath(), F_OK), 0);
180}
181
182TEST_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
189TEST_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 */
199class 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
217TEST_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
230TEST_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
242TEST_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
248TEST_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 */
259class 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
282TEST_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
288TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
289 int numSections = numMatches("DUMPSYS CRITICAL");
290 EXPECT_GE(numSections, 3);
291}
292
293TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
294 int numSections = numMatches("DUMPSYS HIGH");
295 EXPECT_GE(numSections, 2);
296}
297
298TEST_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
308TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) {
309 int numSections = numMatches("proto/");
310 EXPECT_GE(numSections, 1);
311}
312
313// Test if some critical sections are being generated.
314TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
315 SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
316}
317
318TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
319 SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
320 SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
321}
322
323TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
324 SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
325}
326
327TEST_F(BugreportSectionTest, WindowSectionGenerated) {
328 SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
329}
330
331TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
332 SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
333 SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
334}
335
336TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
337 SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
338}
339
340TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
341 SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
342}
343
344TEST_F(BugreportSectionTest, WifiSectionGenerated) {
345 SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
346}
347
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000348class 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
380TEST_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
422TEST_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
450TEST_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());
Nandana Dutt41d7dac2019-02-19 13:05:37 +0000477 EXPECT_EQ(listener2->getErrorCode(),
Nandana Dutt0eb86bf2019-02-21 16:10:10 +0000478 IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
Nandana Dutt16d1aee2019-02-15 16:13:53 +0000479
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
Vishnu Naire97d6122018-01-18 13:58:56 -0800490} // namespace dumpstate
491} // namespace os
492} // namespace android