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_;
DumpstateListener(int fd,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 }
onProgressUpdated(int32_t progress)56 binder::Status onProgressUpdated(int32_t progress) override {
57 dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
58 return binder::Status::ok();
59 }
onMaxProgressUpdated(int32_t max_progress)60 binder::Status onMaxProgressUpdated(int32_t max_progress) override {
61 max_progress_ = max_progress;
62 return binder::Status::ok();
63 }
onSectionComplete(const::std::string & name,int32_t status,int32_t size_bytes,int32_t duration_ms)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 }
onAsBinder()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;
SetUpTestCase()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
getZipFilePath()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
TEST_F(ZippedBugreportGenerationTest,IsGeneratedWithoutErrors)114 TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) {
115 EXPECT_EQ(access(getZipFilePath(), F_OK), 0);
116 }
117
TEST_F(ZippedBugreportGenerationTest,Is3MBto30MBinSize)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
TEST_F(ZippedBugreportGenerationTest,TakesBetween30And150Seconds)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;
SetUp()138 void SetUp() {
139 ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0);
140 }
TearDown()141 void TearDown() {
142 CloseArchive(handle);
143 }
144
FileExists(const char * filename,uint32_t minsize,uint32_t maxsize)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
TEST_F(ZippedBugReportContentsTest,ContainsMainEntry)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
TEST_F(ZippedBugReportContentsTest,ContainsVersion)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
TEST_F(ZippedBugReportContentsTest,ContainsBoardSpecificFiles)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
TEST_F(ZippedBugReportContentsTest,ContainsSomeFileSystemFiles)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:
numMatches(const std::string & substring)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 }
SectionExists(const std::string & sectionName,int minsize)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
TEST_F(BugreportSectionTest,GeneratedWithoutErrors)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
TEST_F(BugreportSectionTest,Atleast3CriticalDumpsysSectionsGenerated)224 TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) {
225 int numSections = numMatches("DUMPSYS CRITICAL");
226 EXPECT_GE(numSections, 3);
227 }
228
TEST_F(BugreportSectionTest,Atleast2HighDumpsysSectionsGenerated)229 TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) {
230 int numSections = numMatches("DUMPSYS HIGH");
231 EXPECT_GE(numSections, 2);
232 }
233
TEST_F(BugreportSectionTest,Atleast50NormalDumpsysSectionsGenerated)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
TEST_F(BugreportSectionTest,Atleast1ProtoDumpsysSectionGenerated)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.
TEST_F(BugreportSectionTest,CriticalSurfaceFlingerSectionGenerated)250 TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) {
251 SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000);
252 }
253
TEST_F(BugreportSectionTest,ActivitySectionsGenerated)254 TEST_F(BugreportSectionTest, ActivitySectionsGenerated) {
255 SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000);
256 SectionExists("DUMPSYS - activity", /* bytes= */ 10000);
257 }
258
TEST_F(BugreportSectionTest,CpuinfoSectionGenerated)259 TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) {
260 SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000);
261 }
262
TEST_F(BugreportSectionTest,WindowSectionGenerated)263 TEST_F(BugreportSectionTest, WindowSectionGenerated) {
264 SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000);
265 }
266
TEST_F(BugreportSectionTest,ConnectivitySectionsGenerated)267 TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) {
268 SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000);
269 SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000);
270 }
271
TEST_F(BugreportSectionTest,MeminfoSectionGenerated)272 TEST_F(BugreportSectionTest, MeminfoSectionGenerated) {
273 SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000);
274 }
275
TEST_F(BugreportSectionTest,BatteryStatsSectionGenerated)276 TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) {
277 SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000);
278 }
279
TEST_F(BugreportSectionTest,WifiSectionGenerated)280 TEST_F(BugreportSectionTest, WifiSectionGenerated) {
281 SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
282 }
283
284 } // namespace dumpstate
285 } // namespace os
286 } // namespace android
287