1// Copyright (C) 2017 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14#define DEBUG false 15#include "Log.h" 16 17#include "Section.h" 18 19#include <android-base/file.h> 20#include <android-base/test_utils.h> 21#include <android/os/IncidentReportArgs.h> 22#include <android/util/protobuf.h> 23#include <frameworks/base/libs/incident/proto/android/os/header.pb.h> 24#include <gmock/gmock.h> 25#include <gtest/gtest.h> 26#include <string.h> 27 28using namespace android; 29using namespace android::base; 30using namespace android::binder; 31using namespace android::os; 32using namespace android::os::incidentd; 33using namespace android::util; 34using ::testing::StrEq; 35using ::testing::Test; 36using ::testing::internal::CaptureStdout; 37using ::testing::internal::GetCapturedStdout; 38 39const int TIMEOUT_PARSER = -1; 40const int NOOP_PARSER = 0; 41const int REVERSE_PARSER = 1; 42 43const int QUICK_TIMEOUT_MS = 100; 44 45const std::string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 46const std::string STRING_FIELD_2 = "\x12\vandroidwins"; 47const std::string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 48 49// NOTICE: this test requires /system/bin/incident_helper is installed. 50class SectionTest : public Test { 51public: 52 virtual void SetUp() override { ASSERT_NE(tf.fd, -1); } 53 54 void printDebugString(std::string s) { 55 fprintf(stderr, "size: %zu\n", s.length()); 56 for (size_t i = 0; i < s.length(); i++) { 57 char c = s[i]; 58 fprintf(stderr, "\\x%x", c); 59 } 60 fprintf(stderr, "\n"); 61 } 62 63protected: 64 TemporaryFile tf; 65 ReportRequestSet requests; 66 67 const std::string kTestPath = GetExecutableDirectory(); 68 const std::string kTestDataPath = kTestPath + "/testdata/"; 69}; 70 71class SimpleListener : public IIncidentReportStatusListener { 72public: 73 SimpleListener(){}; 74 virtual ~SimpleListener(){}; 75 76 virtual Status onReportStarted() { return Status::ok(); }; 77 virtual Status onReportSectionStatus(int /*section*/, int /*status*/) { return Status::ok(); }; 78 virtual Status onReportFinished() { return Status::ok(); }; 79 virtual Status onReportFailed() { return Status::ok(); }; 80 81protected: 82 virtual IBinder* onAsBinder() override { return nullptr; }; 83}; 84 85TEST_F(SectionTest, HeaderSection) { 86 HeaderSection hs; 87 88 IncidentReportArgs args1, args2; 89 args1.addSection(1); 90 args1.addSection(2); 91 args2.setAll(true); 92 93 IncidentHeaderProto head1, head2; 94 head1.set_reason("axe"); 95 head2.set_reason("pup"); 96 97 args1.addHeader(head1); 98 args1.addHeader(head2); 99 args2.addHeader(head2); 100 101 requests.add(new ReportRequest(args1, new SimpleListener(), -1)); 102 requests.add(new ReportRequest(args2, new SimpleListener(), tf.fd)); 103 requests.setMainFd(STDOUT_FILENO); 104 105 std::string content; 106 CaptureStdout(); 107 ASSERT_EQ(NO_ERROR, hs.Execute(&requests)); 108 EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" 109 "\x12\x3" 110 "axe\n\x05\x12\x03pup")); 111 112 EXPECT_TRUE(ReadFileToString(tf.path, &content)); 113 EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); 114} 115 116TEST_F(SectionTest, MetadataSection) { 117 MetadataSection ms; 118 const std::string testFile = kTestDataPath + "metadata.txt"; 119 std::string expect; 120 ASSERT_TRUE(ReadFileToString(testFile, &expect)); 121 122 requests.setMainFd(STDOUT_FILENO); 123 requests.setMainDest(android::os::DEST_LOCAL); 124 requests.sectionStats(1)->set_success(true); 125 126 CaptureStdout(); 127 ASSERT_EQ(NO_ERROR, ms.Execute(&requests)); 128 // Notice message_lite.h ParseFromString doesn't work so we just match the bytes directly. 129 EXPECT_THAT(GetCapturedStdout(), StrEq(expect)); 130} 131 132TEST_F(SectionTest, FileSection) { 133 FileSection fs(REVERSE_PARSER, tf.path); 134 135 ASSERT_TRUE(WriteStringToFile("iamtestdata", tf.path)); 136 137 requests.setMainFd(STDOUT_FILENO); 138 139 CaptureStdout(); 140 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 141 // The input string is reversed in incident helper 142 // The length is 11, in 128Varint it is "0000 1011" -> \v 143 EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\vatadtsetmai")); 144} 145 146TEST_F(SectionTest, FileSectionNotExist) { 147 FileSection fs1(NOOP_PARSER, "notexist", false, QUICK_TIMEOUT_MS); 148 ASSERT_EQ(NAME_NOT_FOUND, fs1.Execute(&requests)); 149 150 FileSection fs2(NOOP_PARSER, "notexist", true, QUICK_TIMEOUT_MS); 151 ASSERT_EQ(NO_ERROR, fs2.Execute(&requests)); 152} 153 154TEST_F(SectionTest, FileSectionTimeout) { 155 FileSection fs(TIMEOUT_PARSER, tf.path, false, QUICK_TIMEOUT_MS); 156 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 157 ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); 158} 159 160TEST_F(SectionTest, GZipSection) { 161 const std::string testFile = kTestDataPath + "kmsg.txt"; 162 const std::string testGzFile = testFile + ".gz"; 163 GZipSection gs(NOOP_PARSER, "/tmp/nonexist", testFile.c_str(), NULL); 164 165 requests.setMainFd(tf.fd); 166 requests.setMainDest(android::os::DEST_LOCAL); 167 168 ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); 169 std::string expected, gzFile, actual; 170 ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile)); 171 ASSERT_TRUE(ReadFileToString(tf.path, &actual)); 172 // generates the expected protobuf result. 173 size_t fileLen = testFile.size(); 174 size_t totalLen = 1 + get_varint_size(fileLen) + fileLen + 3 + gzFile.size(); 175 uint8_t header[20]; 176 header[0] = '\x2'; // header 0 << 3 + 2 177 uint8_t* ptr = write_raw_varint(header + 1, totalLen); 178 *ptr = '\n'; // header 1 << 3 + 2 179 ptr = write_raw_varint(++ptr, fileLen); 180 expected.assign((const char*)header, ptr - header); 181 expected += testFile + "\x12\x9F\x6" + gzFile; 182 EXPECT_THAT(actual, StrEq(expected)); 183} 184 185TEST_F(SectionTest, GZipSectionNoFileFound) { 186 GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL); 187 requests.setMainFd(STDOUT_FILENO); 188 ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); 189} 190 191TEST_F(SectionTest, CommandSectionConstructor) { 192 CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL); 193 CommandSection cs2(2, "single_command", NULL); 194 CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL); 195 CommandSection cs4(2, 43214, "single_command", NULL); 196 197 EXPECT_THAT(cs1.name.string(), StrEq("cmd echo \"this is a test\" ooo")); 198 EXPECT_THAT(cs2.name.string(), StrEq("cmd single_command")); 199 EXPECT_EQ(3123, cs3.timeoutMs); 200 EXPECT_EQ(43214, cs4.timeoutMs); 201 EXPECT_THAT(cs3.name.string(), StrEq("cmd echo \"this is a test\" ooo")); 202 EXPECT_THAT(cs4.name.string(), StrEq("cmd single_command")); 203} 204 205TEST_F(SectionTest, CommandSectionEcho) { 206 CommandSection cs(REVERSE_PARSER, "/system/bin/echo", "about", NULL); 207 requests.setMainFd(STDOUT_FILENO); 208 CaptureStdout(); 209 ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); 210 EXPECT_THAT(GetCapturedStdout(), StrEq("\xa\x06\ntuoba")); 211} 212 213TEST_F(SectionTest, CommandSectionCommandTimeout) { 214 CommandSection cs(NOOP_PARSER, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL); 215 ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); 216 ASSERT_TRUE(requests.sectionStats(NOOP_PARSER)->timed_out()); 217} 218 219TEST_F(SectionTest, CommandSectionIncidentHelperTimeout) { 220 CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL); 221 requests.setMainFd(STDOUT_FILENO); 222 ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); 223 ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); 224} 225 226TEST_F(SectionTest, CommandSectionBadCommand) { 227 CommandSection cs(NOOP_PARSER, "echoo", "about", NULL); 228 ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests)); 229} 230 231TEST_F(SectionTest, CommandSectionBadCommandAndTimeout) { 232 CommandSection cs(TIMEOUT_PARSER, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL); 233 // timeout will return first 234 ASSERT_EQ(NO_ERROR, cs.Execute(&requests)); 235 ASSERT_TRUE(requests.sectionStats(TIMEOUT_PARSER)->timed_out()); 236} 237 238TEST_F(SectionTest, LogSectionBinary) { 239 LogSection ls(1, LOG_ID_EVENTS); 240 requests.setMainFd(STDOUT_FILENO); 241 CaptureStdout(); 242 ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); 243 std::string results = GetCapturedStdout(); 244 EXPECT_FALSE(results.empty()); 245} 246 247TEST_F(SectionTest, LogSectionSystem) { 248 LogSection ls(1, LOG_ID_SYSTEM); 249 requests.setMainFd(STDOUT_FILENO); 250 CaptureStdout(); 251 ASSERT_EQ(NO_ERROR, ls.Execute(&requests)); 252 std::string results = GetCapturedStdout(); 253 EXPECT_FALSE(results.empty()); 254} 255 256TEST_F(SectionTest, TestFilterPiiTaggedFields) { 257 FileSection fs(NOOP_PARSER, tf.path); 258 259 ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); 260 261 requests.setMainFd(STDOUT_FILENO); 262 263 CaptureStdout(); 264 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 265 EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); 266} 267 268TEST_F(SectionTest, TestBadFdRequest) { 269 FileSection fs(NOOP_PARSER, tf.path); 270 ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); 271 272 IncidentReportArgs args; 273 args.setAll(true); 274 args.setDest(0); 275 sp<ReportRequest> badFdRequest = new ReportRequest(args, new SimpleListener(), 1234567); 276 requests.add(badFdRequest); 277 requests.setMainFd(STDOUT_FILENO); 278 279 CaptureStdout(); 280 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 281 EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); 282 EXPECT_EQ(badFdRequest->err, -EBADF); 283} 284 285TEST_F(SectionTest, TestBadRequests) { 286 FileSection fs(NOOP_PARSER, tf.path); 287 ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); 288 289 IncidentReportArgs args; 290 args.setAll(true); 291 args.setDest(0); 292 requests.add(new ReportRequest(args, new SimpleListener(), -1)); 293 EXPECT_EQ(fs.Execute(&requests), -EBADF); 294} 295 296TEST_F(SectionTest, TestMultipleRequests) { 297 TemporaryFile output1, output2, output3; 298 FileSection fs(NOOP_PARSER, tf.path); 299 300 ASSERT_TRUE(output1.fd != -1); 301 ASSERT_TRUE(output2.fd != -1); 302 ASSERT_TRUE(output3.fd != -1); 303 ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); 304 305 IncidentReportArgs args1, args2, args3; 306 args1.setAll(true); 307 args1.setDest(android::os::DEST_LOCAL); 308 args2.setAll(true); 309 args2.setDest(android::os::DEST_EXPLICIT); 310 sp<SimpleListener> l = new SimpleListener(); 311 requests.add(new ReportRequest(args1, l, output1.fd)); 312 requests.add(new ReportRequest(args2, l, output2.fd)); 313 requests.add(new ReportRequest(args3, l, output3.fd)); 314 requests.setMainFd(STDOUT_FILENO); 315 316 CaptureStdout(); 317 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 318 EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); 319 320 std::string content, expect; 321 expect = VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3; 322 char c = (char)expect.size(); 323 EXPECT_TRUE(ReadFileToString(output1.path, &content)); 324 EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); 325 326 expect = STRING_FIELD_2 + FIX64_FIELD_3; 327 c = (char)expect.size(); 328 EXPECT_TRUE(ReadFileToString(output2.path, &content)); 329 EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); 330 331 // because args3 doesn't set section, so it should receive nothing 332 EXPECT_TRUE(ReadFileToString(output3.path, &content)); 333 EXPECT_THAT(content, StrEq("")); 334} 335 336TEST_F(SectionTest, TestMultipleRequestsBySpec) { 337 TemporaryFile output1, output2, output3; 338 FileSection fs(NOOP_PARSER, tf.path); 339 340 ASSERT_TRUE(output1.fd != -1); 341 ASSERT_TRUE(output2.fd != -1); 342 ASSERT_TRUE(output3.fd != -1); 343 344 ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, tf.path)); 345 346 IncidentReportArgs args1, args2, args3; 347 args1.setAll(true); 348 args1.setDest(android::os::DEST_EXPLICIT); 349 args2.setAll(true); 350 args2.setDest(android::os::DEST_EXPLICIT); 351 args3.setAll(true); 352 sp<SimpleListener> l = new SimpleListener(); 353 requests.add(new ReportRequest(args1, l, output1.fd)); 354 requests.add(new ReportRequest(args2, l, output2.fd)); 355 requests.add(new ReportRequest(args3, l, output3.fd)); 356 requests.setMainFd(STDOUT_FILENO); 357 358 CaptureStdout(); 359 ASSERT_EQ(NO_ERROR, fs.Execute(&requests)); 360 EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\r" + STRING_FIELD_2)); 361 362 std::string content, expect; 363 expect = STRING_FIELD_2 + FIX64_FIELD_3; 364 char c = (char)expect.size(); 365 366 // output1 and output2 are the same 367 EXPECT_TRUE(ReadFileToString(output1.path, &content)); 368 EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); 369 EXPECT_TRUE(ReadFileToString(output2.path, &content)); 370 EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); 371 372 // output3 has only auto field 373 c = (char)STRING_FIELD_2.size(); 374 EXPECT_TRUE(ReadFileToString(output3.path, &content)); 375 EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); 376} 377