• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <android-base/file.h>
18 #include <android/os/BnDumpstate.h>
19 #include <android/os/BnDumpstateListener.h>
20 #include <binder/IServiceManager.h>
21 #include <binder/ProcessState.h>
22 #include <cutils/properties.h>
23 #include <fcntl.h>
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <libgen.h>
27 #include <signal.h>
28 #include <ziparchive/zip_archive.h>
29 
30 #include <cstdio>
31 #include <fstream>
32 #include <regex>
33 
34 #include "dumpstate.h"
35 
36 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
37 
38 namespace android {
39 namespace os {
40 namespace dumpstate {
41 
42 using ::testing::Test;
43 using ::std::literals::chrono_literals::operator""s;
44 using android::base::unique_fd;
45 
46 class DumpstateListener;
47 
48 namespace {
49 
50 struct SectionInfo {
51     std::string name;
52     int32_t size_bytes;
53 };
54 
GetDumpstateService()55 sp<IDumpstate> GetDumpstateService() {
56     return android::interface_cast<IDumpstate>(
57         android::defaultServiceManager()->getService(String16("dumpstate")));
58 }
59 
OpenForWrite(const std::string & filename)60 int OpenForWrite(const std::string& filename) {
61     return TEMP_FAILURE_RETRY(open(filename.c_str(),
62                                    O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
63                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
64 }
65 
GetEntry(const ZipArchiveHandle archive,const std::string_view entry_name,ZipEntry * data)66 void GetEntry(const ZipArchiveHandle archive, const std::string_view entry_name, ZipEntry* data) {
67     int32_t e = FindEntry(archive, entry_name, data);
68     EXPECT_EQ(e, 0) << ErrorCodeString(e) << " entry name: " << entry_name;
69 }
70 
71 // Extracts the main bugreport txt from the given archive and writes into output_fd.
ExtractBugreport(const ZipArchiveHandle * handle,int output_fd)72 void ExtractBugreport(const ZipArchiveHandle* handle, int output_fd) {
73     // Read contents of main_entry.txt which is a single line indicating the name of the zip entry
74     // that contains the main bugreport txt.
75     ZipEntry main_entry;
76     GetEntry(*handle, "main_entry.txt", &main_entry);
77     std::string bugreport_txt_name;
78     bugreport_txt_name.resize(main_entry.uncompressed_length);
79     ExtractToMemory(*handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
80                     main_entry.uncompressed_length);
81 
82     // Read the main bugreport txt and extract to output_fd.
83     ZipEntry entry;
84     GetEntry(*handle, bugreport_txt_name, &entry);
85     ExtractEntryToFile(*handle, &entry, output_fd);
86 }
87 
IsSectionStart(const std::string & line,std::string * section_name)88 bool IsSectionStart(const std::string& line, std::string* section_name) {
89     static const std::regex kSectionStart = std::regex{"DUMP OF SERVICE (.*):"};
90     std::smatch match;
91     if (std::regex_match(line, match, kSectionStart)) {
92         *section_name = match.str(1);
93         return true;
94     }
95     return false;
96 }
97 
IsSectionEnd(const std::string & line)98 bool IsSectionEnd(const std::string& line) {
99     // Not all lines that contain "was the duration of" is a section end, but all section ends do
100     // contain "was the duration of". The disambiguation can be done by the caller.
101     return (line.find("was the duration of") != std::string::npos);
102 }
103 
104 // Extracts the zipped bugreport and identifies the sections.
ParseSections(const std::string & zip_path,std::vector<SectionInfo> * sections)105 void ParseSections(const std::string& zip_path, std::vector<SectionInfo>* sections) {
106     // Open the archive
107     ZipArchiveHandle handle;
108     ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
109 
110     // Extract the main entry to a temp file
111     TemporaryFile tmp_binary;
112     ASSERT_NE(-1, tmp_binary.fd);
113     ExtractBugreport(&handle, tmp_binary.fd);
114 
115     // Read line by line and identify sections
116     std::ifstream ifs(tmp_binary.path, std::ifstream::in);
117     std::string line;
118     int section_bytes = 0;
119     std::string current_section_name;
120     while (std::getline(ifs, line)) {
121         std::string section_name;
122         if (IsSectionStart(line, &section_name)) {
123             section_bytes = 0;
124             current_section_name = section_name;
125         } else if (IsSectionEnd(line)) {
126             if (!current_section_name.empty()) {
127                 sections->push_back({current_section_name, section_bytes});
128             }
129             current_section_name = "";
130         } else if (!current_section_name.empty()) {
131             section_bytes += line.length();
132         }
133     }
134 
135     CloseArchive(handle);
136 }
137 
138 }  // namespace
139 
140 /**
141  * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
142  * section details generated by dumpstate are added to a vector to be used by Tests later.
143  */
144 class DumpstateListener : public BnDumpstateListener {
145   public:
DumpstateListener(int fd,std::shared_ptr<std::vector<SectionInfo>> sections)146     DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
147         : out_fd_(fd), sections_(sections) {
148     }
149 
DumpstateListener(int fd)150     DumpstateListener(int fd) : out_fd_(fd) {
151     }
152 
onProgress(int32_t progress)153     binder::Status onProgress(int32_t progress) override {
154         dprintf(out_fd_, "\rIn progress %d", progress);
155         return binder::Status::ok();
156     }
157 
onError(int32_t error_code)158     binder::Status onError(int32_t error_code) override {
159         std::lock_guard<std::mutex> lock(lock_);
160         error_code_ = error_code;
161         dprintf(out_fd_, "\rError code %d", error_code);
162         return binder::Status::ok();
163     }
164 
onFinished(const std::string & bugreport_file)165     binder::Status onFinished([[maybe_unused]] const std::string& bugreport_file) override {
166         std::lock_guard<std::mutex> lock(lock_);
167         is_finished_ = true;
168         dprintf(out_fd_, "\rFinished");
169         return binder::Status::ok();
170     }
171 
onScreenshotTaken(bool success)172     binder::Status onScreenshotTaken(bool success) override {
173         std::lock_guard<std::mutex> lock(lock_);
174         dprintf(out_fd_, "\rResult of taking screenshot: %s", success ? "success" : "failure");
175         return binder::Status::ok();
176     }
177 
onUiIntensiveBugreportDumpsFinished()178     binder::Status onUiIntensiveBugreportDumpsFinished() override {
179         std::lock_guard <std::mutex> lock(lock_);
180         dprintf(out_fd_, "\rUi intensive bugreport dumps finished");
181         return binder::Status::ok();
182     }
183 
getIsFinished()184     bool getIsFinished() {
185         std::lock_guard<std::mutex> lock(lock_);
186         return is_finished_;
187     }
188 
getErrorCode()189     int getErrorCode() {
190         std::lock_guard<std::mutex> lock(lock_);
191         return error_code_;
192     }
193 
194   private:
195     int out_fd_;
196     int error_code_ = -1;
197     bool is_finished_ = false;
198     std::shared_ptr<std::vector<SectionInfo>> sections_;
199     std::mutex lock_;
200 };
201 
202 /**
203  * Generates bug report and provide access to the bug report file and other info for other tests.
204  * Since bug report generation is slow, the bugreport is only generated once.
205  */
206 class ZippedBugreportGenerationTest : public Test {
207   public:
208     static std::shared_ptr<std::vector<SectionInfo>> sections;
209     static Dumpstate& ds;
210     static std::chrono::milliseconds duration;
GenerateBugreport()211     static void GenerateBugreport() {
212         // clang-format off
213         char* argv[] = {
214             (char*)"dumpstate"
215         };
216         // clang-format on
217         sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections));
218         ds.listener_ = listener;
219         auto start = std::chrono::steady_clock::now();
220         ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv);
221         auto end = std::chrono::steady_clock::now();
222         duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
223     }
224 
getZipFilePath()225     static const std::string getZipFilePath() {
226         return ds.GetPath(".zip");
227     }
228 };
229 std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections =
230     std::make_shared<std::vector<SectionInfo>>();
231 Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance();
232 std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s;
233 
TEST_F(ZippedBugreportGenerationTest,IsGeneratedWithoutErrors)234 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
235     GenerateBugreport();
236     EXPECT_EQ(access(getZipFilePath().c_str(), F_OK), 0);
237 }
238 
TEST_F(ZippedBugreportGenerationTest,Is1MBMBinSize)239 TEST_F(ZippedBugreportGenerationTest, Is1MBMBinSize) {
240     struct stat st;
241     EXPECT_EQ(stat(getZipFilePath().c_str(), &st), 0);
242     EXPECT_GE(st.st_size, 1000000 /* 1MB */);
243 }
244 
TEST_F(ZippedBugreportGenerationTest,TakesBetween20And300Seconds)245 TEST_F(ZippedBugreportGenerationTest, TakesBetween20And300Seconds) {
246     EXPECT_GE(duration, 20s) << "Expected completion in more than 20s. Actual time "
247                              << duration.count() << " ms.";
248     EXPECT_LE(duration, 300s) << "Expected completion in less than 300s. Actual time "
249                               << duration.count() << " ms.";
250 }
251 
252 /**
253  * Run tests on contents of zipped bug report.
254  */
255 class ZippedBugReportContentsTest : public Test {
256   public:
257     ZipArchiveHandle handle;
SetUp()258     void SetUp() {
259         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
260     }
TearDown()261     void TearDown() {
262         CloseArchive(handle);
263     }
264 
FileExists(const char * filename,uint32_t minsize,uint32_t maxsize=std::numeric_limits<uint32_t>::max ())265     void FileExists(const char* filename, uint32_t minsize,
266                     uint32_t maxsize = std::numeric_limits<uint32_t>::max()) {
267         ZipEntry entry;
268         GetEntry(handle, filename, &entry);
269         EXPECT_GT(entry.uncompressed_length, minsize);
270         EXPECT_LT(entry.uncompressed_length, maxsize);
271     }
272 };
273 
TEST_F(ZippedBugReportContentsTest,ContainsMainEntry)274 TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) {
275     ZipEntry main_entry;
276     // contains main entry name file
277     GetEntry(handle, "main_entry.txt", &main_entry);
278 
279     std::string bugreport_txt_name;
280     bugreport_txt_name.resize(main_entry.uncompressed_length);
281     ExtractToMemory(handle, &main_entry, reinterpret_cast<uint8_t*>(bugreport_txt_name.data()),
282                     main_entry.uncompressed_length);
283 
284     // contains main entry file
285     FileExists(bugreport_txt_name.c_str(), 1000000U);
286 }
287 
TEST_F(ZippedBugReportContentsTest,ContainsVersion)288 TEST_F(ZippedBugReportContentsTest, ContainsVersion) {
289     ZipEntry entry;
290     // contains main entry name file
291     GetEntry(handle, "version.txt", &entry);
292 
293     char* buf = new char[entry.uncompressed_length + 1];
294     ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length);
295     buf[entry.uncompressed_length] = 0;
296     EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str());
297     delete[] buf;
298 }
299 
TEST_F(ZippedBugReportContentsTest,ContainsBoardSpecificFiles)300 TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) {
301     // TODO(b/160109027): cf_x86_phone-userdebug does not dump them.
302     // FileExists("dumpstate_board.bin", 1000000U, 80000000U);
303     // FileExists("dumpstate_board.txt", 100000U, 1000000U);
304 }
305 
TEST_F(ZippedBugReportContentsTest,ContainsProtoFile)306 TEST_F(ZippedBugReportContentsTest, ContainsProtoFile) {
307     FileExists("proto/activity.proto", 100000U, 1000000U);
308 }
309 
310 // Spot check on some files pulled from the file system
TEST_F(ZippedBugReportContentsTest,ContainsSomeFileSystemFiles)311 TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) {
312     // FS/proc/*/mountinfo size > 0
313     FileExists("FS/proc/1/mountinfo", 0U, 100000U);
314 
315     // FS/data/misc/profiles/cur/0/*/primary.prof should exist. Also, since dumpstate only adds
316     // profiles to the zip in the non-user build, a build checking is necessary here.
317     if (!PropertiesHelper::IsUserBuild()) {
318         ZipEntry entry;
319         GetEntry(handle, "FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", &entry);
320     }
321 }
322 
323 /**
324  * Runs tests on section data generated by dumpstate and captured by DumpstateListener.
325  */
326 class BugreportSectionTest : public Test {
327   public:
328     ZipArchiveHandle handle;
329 
SetUp()330     void SetUp() {
331         ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath().c_str(), &handle), 0);
332     }
333 
TearDown()334     void TearDown() {
335         CloseArchive(handle);
336     }
337 
SetUpTestCase()338     static void SetUpTestCase() {
339         ParseSections(ZippedBugreportGenerationTest::getZipFilePath().c_str(),
340                       ZippedBugreportGenerationTest::sections.get());
341     }
342 
numMatches(const std::string & substring)343     int numMatches(const std::string& substring) {
344         int matches = 0;
345         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
346             if (section.name.find(substring) != std::string::npos) {
347                 matches++;
348             }
349         }
350         return matches;
351     }
352 
SectionExists(const std::string & sectionName,int minsize)353     void SectionExists(const std::string& sectionName, int minsize) {
354         for (auto const& section : *ZippedBugreportGenerationTest::sections) {
355             if (sectionName == section.name) {
356                 EXPECT_GE(section.size_bytes, minsize) << " for section:" << sectionName;
357                 return;
358             }
359         }
360         FAIL() << sectionName << " not found.";
361     }
362 
363     /**
364      * Whether or not the content of the section is injected by other commands.
365      */
IsContentInjectedByOthers(const std::string & line)366     bool IsContentInjectedByOthers(const std::string& line) {
367         // Command header such as `------ APP ACTIVITIES (/system/bin/dumpsys activity -v) ------`.
368         static const std::regex kCommandHeader = std::regex{"------ .+ \\(.+\\) ------"};
369         std::smatch match;
370         if (std::regex_match(line, match, kCommandHeader)) {
371           return true;
372         }
373         return false;
374     }
375 };
376 
TEST_F(BugreportSectionTest,Atleast3CriticalDumpsysSectionsGenerated)377 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
378     int numSections = numMatches("CRITICAL");
379     EXPECT_GE(numSections, 3);
380 }
381 
TEST_F(BugreportSectionTest,Atleast2HighDumpsysSectionsGenerated)382 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
383     int numSections = numMatches("HIGH");
384     EXPECT_GE(numSections, 2);
385 }
386 
TEST_F(BugreportSectionTest,Atleast50NormalDumpsysSectionsGenerated)387 TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) {
388     int allSections = ZippedBugreportGenerationTest::sections->size();
389     int criticalSections = numMatches("CRITICAL");
390     int highSections = numMatches("HIGH");
391     int normalSections = allSections - criticalSections - highSections;
392 
393     EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections
394                                   << "High:" << highSections << "Normal:" << normalSections << ")";
395 }
396 
397 // Test if some critical sections are being generated.
TEST_F(BugreportSectionTest,CriticalSurfaceFlingerSectionGenerated)398 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
399     SectionExists("CRITICAL SurfaceFlinger", /* bytes= */ 10000);
400 }
401 
TEST_F(BugreportSectionTest,ActivitySectionsGenerated)402 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
403     SectionExists("CRITICAL activity", /* bytes= */ 5000);
404     SectionExists("activity", /* bytes= */ 10000);
405 }
406 
TEST_F(BugreportSectionTest,CpuinfoSectionGenerated)407 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
408     SectionExists("CRITICAL cpuinfo", /* bytes= */ 1000);
409 }
410 
TEST_F(BugreportSectionTest,WindowSectionGenerated)411 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
412     SectionExists("CRITICAL window", /* bytes= */ 20000);
413 }
414 
TEST_F(BugreportSectionTest,ConnectivitySectionsGenerated)415 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
416     SectionExists("connectivity", /* bytes= */ 5000);
417 }
418 
TEST_F(BugreportSectionTest,MeminfoSectionGenerated)419 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
420     SectionExists("HIGH meminfo", /* bytes= */ 100000);
421 }
422 
TEST_F(BugreportSectionTest,BatteryStatsSectionGenerated)423 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
424     SectionExists("batterystats", /* bytes= */ 1000);
425 }
426 
TEST_F(BugreportSectionTest,DISABLED_WifiSectionGenerated)427 TEST_F(BugreportSectionTest, DISABLED_WifiSectionGenerated) {
428     SectionExists("wifi", /* bytes= */ 100000);
429 }
430 
TEST_F(BugreportSectionTest,NoInjectedContentByOtherCommand)431 TEST_F(BugreportSectionTest, NoInjectedContentByOtherCommand) {
432     // Extract the main entry to a temp file
433     TemporaryFile tmp_binary;
434     ASSERT_NE(-1, tmp_binary.fd);
435     ExtractBugreport(&handle, tmp_binary.fd);
436 
437     // Read line by line and identify sections
438     std::ifstream ifs(tmp_binary.path, std::ifstream::in);
439     std::string line;
440     std::string current_section_name;
441     while (std::getline(ifs, line)) {
442         std::string section_name;
443         if (IsSectionStart(line, &section_name)) {
444             current_section_name = section_name;
445         } else if (IsSectionEnd(line)) {
446             current_section_name = "";
447         } else if (!current_section_name.empty()) {
448             EXPECT_FALSE(IsContentInjectedByOthers(line));
449         }
450     }
451 }
452 
453 class DumpstateBinderTest : public Test {
454   protected:
SetUp()455     void SetUp() override {
456         // In case there is a stray service, stop it first.
457         property_set("ctl.stop", "bugreportd");
458         // dry_run results in a faster bugreport.
459         property_set("dumpstate.dry_run", "true");
460         // We need to receive some async calls later. Ensure we have binder threads.
461         ProcessState::self()->startThreadPool();
462     }
463 
TearDown()464     void TearDown() override {
465         property_set("ctl.stop", "bugreportd");
466         property_set("dumpstate.dry_run", "");
467 
468         unlink("/data/local/tmp/tmp.zip");
469         unlink("/data/local/tmp/tmp.png");
470     }
471 
472     // Waits until listener gets the callbacks.
WaitTillExecutionComplete(DumpstateListener * listener)473     void WaitTillExecutionComplete(DumpstateListener* listener) {
474         // Wait till one of finished, error or timeout.
475         static const int kBugreportTimeoutSeconds = 120;
476         int i = 0;
477         while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
478                i < kBugreportTimeoutSeconds) {
479             sleep(1);
480             i++;
481         }
482     }
483 };
484 
TEST_F(DumpstateBinderTest,Baseline)485 TEST_F(DumpstateBinderTest, Baseline) {
486     // In the beginning dumpstate binder service is not running.
487     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
488     EXPECT_EQ(ds_binder, nullptr);
489 
490     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
491     // and makes it wait.
492     property_set("dumpstate.dry_run", "true");
493     property_set("ctl.start", "bugreportd");
494 
495     // Now we are able to retrieve dumpstate binder service.
496     ds_binder = GetDumpstateService();
497     EXPECT_NE(ds_binder, nullptr);
498 
499     // Prepare arguments
500     unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
501     unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
502     int flags = 0;
503 
504     EXPECT_NE(bugreport_fd.get(), -1);
505     EXPECT_NE(screenshot_fd.get(), -1);
506 
507     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
508     android::binder::Status status =
509         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
510                                   std::move(screenshot_fd),
511                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener,
512                                   true, false);
513     // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
514     // gets expected callbacks.
515     EXPECT_TRUE(status.isOk());
516     WaitTillExecutionComplete(listener.get());
517 
518     // Bugreport generation requires user consent, which we cannot get in a test set up,
519     // so instead of getting is_finished_, we are more likely to get a consent error.
520     EXPECT_TRUE(
521         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
522         listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
523 
524     // The service should have died on its own, freeing itself up for a new invocation.
525     sleep(2);
526     ds_binder = GetDumpstateService();
527     EXPECT_EQ(ds_binder, nullptr);
528 }
529 
TEST_F(DumpstateBinderTest,ServiceDies_OnInvalidInput)530 TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
531     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
532     // and makes it wait.
533     property_set("ctl.start", "bugreportd");
534     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
535     EXPECT_NE(ds_binder, nullptr);
536 
537     // Prepare arguments
538     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
539     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
540     int flags = 0;
541 
542     EXPECT_NE(bugreport_fd.get(), -1);
543     EXPECT_NE(screenshot_fd.get(), -1);
544 
545     // Call startBugreport with bad arguments.
546     sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
547     android::binder::Status status =
548         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
549                                   std::move(screenshot_fd), 2000,  // invalid bugreport mode
550                                   flags, listener, false, false);
551     EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
552 
553     // The service should have died, freeing itself up for a new invocation.
554     sleep(2);
555     ds_binder = GetDumpstateService();
556     EXPECT_EQ(ds_binder, nullptr);
557 }
558 
TEST_F(DumpstateBinderTest,SimultaneousBugreportsNotAllowed)559 TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
560     // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
561     // and makes it wait.
562     property_set("dumpstate.dry_run", "true");
563     property_set("ctl.start", "bugreportd");
564     sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
565     EXPECT_NE(ds_binder, nullptr);
566 
567     // Prepare arguments
568     unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
569     unique_fd bugreport_fd2(dup(bugreport_fd.get()));
570     unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
571     unique_fd screenshot_fd2(dup(screenshot_fd.get()));
572     int flags = 0;
573 
574     EXPECT_NE(bugreport_fd.get(), -1);
575     EXPECT_NE(bugreport_fd2.get(), -1);
576     EXPECT_NE(screenshot_fd.get(), -1);
577     EXPECT_NE(screenshot_fd2.get(), -1);
578 
579     sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
580     android::binder::Status status =
581         ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd),
582                                   std::move(screenshot_fd),
583                                   Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags, listener1,
584                                   true, false);
585     EXPECT_TRUE(status.isOk());
586 
587     // try to make another call to startBugreport. This should fail.
588     sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
589     status = ds_binder->startBugreport(123, "com.example.package", std::move(bugreport_fd2),
590                                         std::move(screenshot_fd2),
591                                        Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, flags,
592                                        listener2, true, false);
593     EXPECT_FALSE(status.isOk());
594     WaitTillExecutionComplete(listener2.get());
595     EXPECT_EQ(listener2->getErrorCode(),
596               IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
597 
598     // Meanwhile the first call works as expected. Service should not die in this case.
599     WaitTillExecutionComplete(listener1.get());
600 
601     // Bugreport generation requires user consent, which we cannot get in a test set up,
602     // so instead of getting is_finished_, we are more likely to get a consent error.
603     EXPECT_TRUE(
604         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
605         listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
606 }
607 
608 class DumpstateTracingTest : public Test {
609   protected:
TearDown()610     void TearDown() override {
611         for (int pid : bg_process_pids) {
612             kill(pid, SIGKILL);
613         }
614     }
615 
StartTracing(const std::string & config)616     void StartTracing(const std::string& config) {
617         // Write the perfetto config into a file.
618         const int id = static_cast<int>(bg_process_pids.size());
619         char cfg[64];
620         snprintf(cfg, sizeof(cfg), "/data/misc/perfetto-configs/br-%d", id);
621         unlink(cfg);  // Remove the config file if it exists already.
622         FILE* f = fopen(cfg, "w");
623         ASSERT_NE(f, nullptr);
624         fputs(config.c_str(), f);
625         fclose(f);
626 
627         // Invoke perfetto to start tracing.
628         char cmd[255];
629         snprintf(cmd, sizeof(cmd), "perfetto --background-wait --txt -o /dev/null -c %s", cfg);
630         FILE* proc = popen(cmd, "r");
631         ASSERT_NE(proc, nullptr);
632 
633         // Read back the PID of the background process. We will use it to kill
634         // all tracing sessions when the test ends or fails.
635         char pid_str[32]{};
636         ASSERT_NE(fgets(pid_str, sizeof(pid_str), proc), nullptr);
637         int pid = atoi(pid_str);
638         bg_process_pids.push_back(pid);
639 
640         pclose(proc);
641         unlink(cfg);
642     }
643 
644     std::vector<int> bg_process_pids;
645 };
646 
TEST_F(DumpstateTracingTest,ManyTracesInBugreport)647 TEST_F(DumpstateTracingTest, ManyTracesInBugreport) {
648     // Note the trace duration is irrelevant and is only an upper bound.
649     // Tracing is stopped as soon as the bugreport.zip creation ends.
650     StartTracing(R"(
651 buffers { size_kb: 4096 }
652 data_sources {
653   config {
654     name: "linux.ftrace"
655   }
656 }
657 
658 duration_ms: 120000
659 bugreport_filename: "sys.pftrace"
660 bugreport_score: 100
661 )");
662 
663     StartTracing(R"(
664 buffers { size_kb: 4096 }
665 data_sources {
666   config {
667     name: "linux.ftrace"
668   }
669 }
670 
671 duration_ms: 120000
672 bugreport_score: 50
673 bugreport_filename: "mem.pftrace"
674 )");
675 
676     ZippedBugreportGenerationTest::GenerateBugreport();
677     std::string zip_path = ZippedBugreportGenerationTest::getZipFilePath();
678     ZipArchiveHandle handle;
679     ASSERT_EQ(OpenArchive(zip_path.c_str(), &handle), 0);
680 
681     const char* kExpectedEntries[]{
682         "FS/data/misc/perfetto-traces/bugreport/sys.pftrace",
683         "FS/data/misc/perfetto-traces/bugreport/mem.pftrace",
684     };
685 
686     // Check that the bugreport contains both traces.
687     for (const char* file_path : kExpectedEntries) {
688         ZipEntry entry{};
689         GetEntry(handle, file_path, &entry);
690         EXPECT_GT(entry.uncompressed_length, 100);
691     }
692     CloseArchive(handle);
693 }
694 
695 }  // namespace dumpstate
696 }  // namespace os
697 }  // namespace android
698