diff options
| author | 2018-02-09 16:47:47 -0800 | |
|---|---|---|
| committer | 2018-02-14 16:12:57 -0800 | |
| commit | 329130b7e40d27f660aa275ef6905bd7ee91f64c (patch) | |
| tree | 8d16329f3554839009f698533591cfbde22e1507 | |
| parent | 85a6db68f1860bbaacc1cc21e29c4f61aabe0abb (diff) | |
Put metadata or stats into each dropbox incident report.
Bug: 65451198
Test: atest incidentd_test
Change-Id: Ib406b177ad7f1b4bda7fef2e606fc66a9836e060
| -rw-r--r-- | cmds/incidentd/Android.mk | 1 | ||||
| -rw-r--r-- | cmds/incidentd/src/Privacy.cpp | 7 | ||||
| -rw-r--r-- | cmds/incidentd/src/Privacy.h | 1 | ||||
| -rw-r--r-- | cmds/incidentd/src/Reporter.cpp | 44 | ||||
| -rw-r--r-- | cmds/incidentd/src/Reporter.h | 16 | ||||
| -rw-r--r-- | cmds/incidentd/src/Section.cpp | 42 | ||||
| -rw-r--r-- | cmds/incidentd/src/Section.h | 12 | ||||
| -rw-r--r-- | cmds/incidentd/tests/Reporter_test.cpp | 15 | ||||
| -rw-r--r-- | cmds/incidentd/tests/Section_test.cpp | 29 | ||||
| -rw-r--r-- | core/proto/android/os/incident.proto | 8 | ||||
| -rw-r--r-- | libs/incident/Android.mk | 1 | ||||
| -rw-r--r-- | libs/incident/proto/android/os/metadata.proto | 63 |
12 files changed, 208 insertions, 31 deletions
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk index 2b00d9e6a596..23cd2af512b5 100644 --- a/cmds/incidentd/Android.mk +++ b/cmds/incidentd/Android.mk @@ -56,6 +56,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libincident \ liblog \ + libprotobuf-cpp-lite \ libprotoutil \ libselinux \ libservices \ diff --git a/cmds/incidentd/src/Privacy.cpp b/cmds/incidentd/src/Privacy.cpp index 3f0e331c8b55..c5078f0f7909 100644 --- a/cmds/incidentd/src/Privacy.cpp +++ b/cmds/incidentd/src/Privacy.cpp @@ -73,11 +73,6 @@ PrivacySpec PrivacySpec::new_spec(int dest) case android::os::DEST_LOCAL: return PrivacySpec(dest); default: - return PrivacySpec(); + return PrivacySpec(android::os::DEST_AUTOMATIC); } } - -PrivacySpec PrivacySpec::get_default_dropbox_spec() -{ - return PrivacySpec(android::os::DEST_AUTOMATIC); -} diff --git a/cmds/incidentd/src/Privacy.h b/cmds/incidentd/src/Privacy.h index 4f3db678f765..ce1b8e96529c 100644 --- a/cmds/incidentd/src/Privacy.h +++ b/cmds/incidentd/src/Privacy.h @@ -75,7 +75,6 @@ public: // Constructs spec using static methods below. static PrivacySpec new_spec(int dest); - static PrivacySpec get_default_dropbox_spec(); private: PrivacySpec(uint8_t dest) : dest(dest) {} }; diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp index b9f479bd683f..06baebaf3a4c 100644 --- a/cmds/incidentd/src/Reporter.cpp +++ b/cmds/incidentd/src/Reporter.cpp @@ -18,6 +18,7 @@ #include "Reporter.h" +#include "Privacy.h" #include "report_directory.h" #include "section_list.h" @@ -65,7 +66,9 @@ ReportRequestSet::ReportRequestSet() :mRequests(), mSections(), mMainFd(-1), - mMainDest(-1) + mMainDest(-1), + mMetadata(), + mSectionStats() { } @@ -79,18 +82,32 @@ ReportRequestSet::add(const sp<ReportRequest>& request) { mRequests.push_back(request); mSections.merge(request->args); + mMetadata.set_request_size(mMetadata.request_size() + 1); } void ReportRequestSet::setMainFd(int fd) { mMainFd = fd; + mMetadata.set_use_dropbox(fd > 0); } void ReportRequestSet::setMainDest(int dest) { mMainDest = dest; + PrivacySpec spec = PrivacySpec::new_spec(dest); + switch (spec.dest) { + case android::os::DEST_AUTOMATIC: + mMetadata.set_dest(IncidentMetadata_Destination_AUTOMATIC); + break; + case android::os::DEST_EXPLICIT: + mMetadata.set_dest(IncidentMetadata_Destination_EXPLICIT); + break; + case android::os::DEST_LOCAL: + mMetadata.set_dest(IncidentMetadata_Destination_LOCAL); + break; + } } bool @@ -98,6 +115,16 @@ ReportRequestSet::containsSection(int id) { return mSections.containsSection(id); } +IncidentMetadata::SectionStats* +ReportRequestSet::sectionStats(int id) { + if (mSectionStats.find(id) == mSectionStats.end()) { + auto stats = mMetadata.add_sections(); + stats->set_id(id); + mSectionStats[id] = stats; + } + return mSectionStats[id]; +} + // ================================================================================ Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; }; @@ -128,12 +155,12 @@ Reporter::~Reporter() Reporter::run_report_status_t Reporter::runReport() { - status_t err = NO_ERROR; bool needMainFd = false; int mainFd = -1; int mainDest = -1; HeaderSection headers; + MetadataSection metadataSection; // See if we need the main file for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { @@ -182,7 +209,7 @@ Reporter::runReport() const int id = (*section)->id; if (this->batch.containsSection(id)) { ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string()); - // Notify listener of starting + // Notify listener of starting. for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { (*it)->listener->onReportSectionStatus(id, @@ -191,14 +218,20 @@ Reporter::runReport() } // Execute - go get the data and write it into the file descriptors. + auto stats = batch.sectionStats(id); + int64_t startTime = uptimeMillis(); err = (*section)->Execute(&batch); + int64_t endTime = uptimeMillis(); + + stats->set_success(err == NO_ERROR); + stats->set_exec_duration_ms(endTime - startTime); if (err != NO_ERROR) { ALOGW("Incident section %s (%d) failed: %s. Stopping report.", (*section)->name.string(), id, strerror(-err)); goto DONE; } - // Notify listener of starting + // Notify listener of ending. for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) { if ((*it)->listener != NULL && (*it)->args.containsSection(id)) { (*it)->listener->onReportSectionStatus(id, @@ -210,6 +243,9 @@ Reporter::runReport() } DONE: + // Reports the metdadata when taking the incident report. + if (!isTest) metadataSection.Execute(&batch); + // Close the file. if (mainFd >= 0) { close(mainFd); diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h index f30ecf0dd648..6058068be331 100644 --- a/cmds/incidentd/src/Reporter.h +++ b/cmds/incidentd/src/Reporter.h @@ -17,9 +17,12 @@ #ifndef REPORTER_H #define REPORTER_H +#include "frameworks/base/libs/incident/proto/android/os/metadata.pb.h" + #include <android/os/IIncidentReportStatusListener.h> #include <android/os/IncidentReportArgs.h> +#include <map> #include <string> #include <vector> @@ -61,13 +64,20 @@ public: iterator end() { return mRequests.end(); } int mainFd() { return mMainFd; } - bool containsSection(int id); int mainDest() { return mMainDest; } + IncidentMetadata& metadata() { return mMetadata; } + + bool containsSection(int id); + IncidentMetadata::SectionStats* sectionStats(int id); + private: vector<sp<ReportRequest>> mRequests; IncidentReportArgs mSections; int mMainFd; int mMainDest; + + IncidentMetadata mMetadata; + map<int, IncidentMetadata::SectionStats*> mSectionStats; }; // ================================================================================ @@ -81,8 +91,8 @@ public: ReportRequestSet batch; - Reporter(); - Reporter(const char* directory); + Reporter(); // PROD must use this constructor. + Reporter(const char* directory); // For testing purpose only. virtual ~Reporter(); // Run the report as described in the batch and args parameters. diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index faeab87f8178..3c76298c284c 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -45,6 +45,7 @@ using namespace std; // special section ids const int FIELD_ID_INCIDENT_HEADER = 1; +const int FIELD_ID_INCIDENT_METADATA = 2; // incident section parameters const int WAIT_MAX = 5; @@ -149,6 +150,12 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re EncodedBuffer::iterator data = buffer.data(); PrivacyBuffer privacyBuffer(get_privacy_of_section(id), data); int writeable = 0; + auto stats = requests->sectionStats(id); + + stats->set_dump_size_bytes(data.size()); + stats->set_dump_duration_ms(buffer.durationMs()); + stats->set_timed_out(buffer.timedOut()); + stats->set_is_truncated(buffer.truncated()); // The streaming ones, group requests by spec in order to save unnecessary strip operations map<PrivacySpec, vector<sp<ReportRequest>>> requestsBySpec; @@ -182,9 +189,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re // The dropbox file if (requests->mainFd() >= 0) { - PrivacySpec spec = requests->mainDest() < 0 ? - PrivacySpec::get_default_dropbox_spec() : - PrivacySpec::new_spec(requests->mainDest()); + PrivacySpec spec = PrivacySpec::new_spec(requests->mainDest()); err = privacyBuffer.strip(spec); if (err != NO_ERROR) return err; // the buffer data is corrupted. if (privacyBuffer.size() == 0) goto DONE; @@ -196,6 +201,7 @@ write_report_requests(const int id, const FdBuffer& buffer, ReportRequestSet* re writeable++; ALOGD("Section %d flushed %zu bytes to dropbox %d with spec %d", id, privacyBuffer.size(), requests->mainFd(), spec.dest); + stats->set_report_size_bytes(privacyBuffer.size()); } DONE: @@ -236,7 +242,7 @@ HeaderSection::Execute(ReportRequestSet* requests) const // So the idea is only requests with negative fd are written to dropbox file. int fd = request->fd >= 0 ? request->fd : requests->mainFd(); - write_section_header(fd, FIELD_ID_INCIDENT_HEADER, buf->size()); + write_section_header(fd, id, buf->size()); write_all(fd, (uint8_t const*)buf->data(), buf->size()); // If there was an error now, there will be an error later and we will remove // it from the list then. @@ -244,7 +250,35 @@ HeaderSection::Execute(ReportRequestSet* requests) const } return NO_ERROR; } +// ================================================================================ +MetadataSection::MetadataSection() + :Section(FIELD_ID_INCIDENT_METADATA, 0) +{ +} +MetadataSection::~MetadataSection() +{ +} + +status_t +MetadataSection::Execute(ReportRequestSet* requests) const +{ + std::string metadataBuf; + requests->metadata().SerializeToString(&metadataBuf); + for (ReportRequestSet::iterator it=requests->begin(); it!=requests->end(); it++) { + const sp<ReportRequest> request = *it; + if (metadataBuf.empty() || request->fd < 0 || request->err != NO_ERROR) { + continue; + } + write_section_header(request->fd, id, metadataBuf.size()); + write_all(request->fd, (uint8_t const*)metadataBuf.data(), metadataBuf.size()); + } + if (requests->mainFd() >= 0 && !metadataBuf.empty()) { + write_section_header(requests->mainFd(), id, metadataBuf.size()); + write_all(requests->mainFd(), (uint8_t const*)metadataBuf.data(), metadataBuf.size()); + } + return NO_ERROR; +} // ================================================================================ FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs) :Section(id, timeoutMs), diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index d440ee92601c..80cc033d5ca0 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -59,6 +59,18 @@ public: }; /** + * Section that generates incident metadata. + */ +class MetadataSection : public Section +{ +public: + MetadataSection(); + virtual ~MetadataSection(); + + virtual status_t Execute(ReportRequestSet* requests) const; +}; + +/** * Section that reads in a file. */ class FileSection : public Section diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp index c494bd646b0b..a1e3c3430595 100644 --- a/cmds/incidentd/tests/Reporter_test.cpp +++ b/cmds/incidentd/tests/Reporter_test.cpp @@ -180,3 +180,18 @@ TEST_F(ReporterTest, RunReportToGivenDirectory) { ASSERT_EQ((int)results.size(), 1); EXPECT_EQ(results[0], "\n\x2" "\b\f\n\x6" "\x12\x4" "abcd"); } + +TEST_F(ReporterTest, ReportMetadata) { + IncidentReportArgs args; + args.addSection(1); + args.setDest(android::os::DEST_EXPLICIT); + sp<ReportRequest> r = new ReportRequest(args, l, -1); + reporter->batch.add(r); + + ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport()); + auto metadata = reporter->batch.metadata(); + EXPECT_EQ(IncidentMetadata_Destination_EXPLICIT, metadata.dest()); + EXPECT_EQ(1, metadata.request_size()); + EXPECT_TRUE(metadata.use_dropbox()); + EXPECT_EQ(0, metadata.sections_size()); +}
\ No newline at end of file diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index 2cfd7df6be84..5752a670c6a2 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -18,6 +18,7 @@ #include <android-base/file.h> #include <android-base/test_utils.h> +#include <android/os/IncidentReportArgs.h> #include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -89,6 +90,18 @@ TEST(SectionTest, HeaderSection) { EXPECT_THAT(content, StrEq("\n\x05\x12\x03pup")); } +TEST(SectionTest, MetadataSection) { + MetadataSection ms; + ReportRequestSet requests; + + requests.setMainFd(STDOUT_FILENO); + requests.sectionStats(1)->set_success(true); + + CaptureStdout(); + ASSERT_EQ(NO_ERROR, ms.Execute(&requests)); + EXPECT_THAT(GetCapturedStdout(), StrEq("\x12\b\x18\x1\"\x4\b\x1\x10\x1")); +} + TEST(SectionTest, FileSection) { TemporaryFile tf; FileSection fs(REVERSE_PARSER, tf.path); @@ -243,8 +256,9 @@ TEST(SectionTest, TestMultipleRequests) { IncidentReportArgs args1, args2, args3; args1.setAll(true); - args1.setDest(0); // LOCAL - args2.setAll(true); // default to explicit + args1.setDest(android::os::DEST_LOCAL); + args2.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); sp<SimpleListener> l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -283,10 +297,12 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { ASSERT_TRUE(WriteStringToFile(VARINT_FIELD_1 + STRING_FIELD_2 + FIX64_FIELD_3, input.path)); - IncidentReportArgs args1, args2, args3, args4; + IncidentReportArgs args1, args2, args3; args1.setAll(true); + args1.setDest(android::os::DEST_EXPLICIT); args2.setAll(true); - args4.setAll(true); + args2.setDest(android::os::DEST_EXPLICIT); + args3.setAll(true); sp<SimpleListener> l = new SimpleListener(); requests.add(new ReportRequest(args1, l, output1.fd)); requests.add(new ReportRequest(args2, l, output2.fd)); @@ -307,7 +323,8 @@ TEST(SectionTest, TestMultipleRequestsBySpec) { EXPECT_TRUE(ReadFileToString(output2.path, &content)); EXPECT_THAT(content, StrEq(string("\x02") + c + expect)); - // because args3 doesn't set section, so it should receive nothing + // output3 has only auto field + c = (char) STRING_FIELD_2.size(); EXPECT_TRUE(ReadFileToString(output3.path, &content)); - EXPECT_THAT(content, StrEq("")); + EXPECT_THAT(content, StrEq(string("\x02") + c + STRING_FIELD_2)); }
\ No newline at end of file diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 698f394385ab..f9dd8822bdef 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -46,18 +46,12 @@ import "frameworks/base/core/proto/android/service/usb.proto"; import "frameworks/base/core/proto/android/util/event_log_tags.proto"; import "frameworks/base/core/proto/android/util/log.proto"; import "frameworks/base/libs/incident/proto/android/os/header.proto"; +import "frameworks/base/libs/incident/proto/android/os/metadata.proto"; import "frameworks/base/libs/incident/proto/android/privacy.proto"; import "frameworks/base/libs/incident/proto/android/section.proto"; package android.os; -// This field contains internal metadata associated with an incident report, -// such as the section ids and privacy policy specs from caller as well as how long -// and how many bytes a section takes, etc. -message IncidentMetadata { - -} - // privacy field options must not be set at this level because all // the sections are able to be controlled and configured by section ids. // Instead privacy field options need to be configured in each section proto message. diff --git a/libs/incident/Android.mk b/libs/incident/Android.mk index b63400f14609..08c834699f40 100644 --- a/libs/incident/Android.mk +++ b/libs/incident/Android.mk @@ -33,6 +33,7 @@ LOCAL_SRC_FILES := \ ../../core/java/android/os/IIncidentManager.aidl \ ../../core/java/android/os/IIncidentReportStatusListener.aidl \ proto/android/os/header.proto \ + proto/android/os/metadata.proto \ src/IncidentReportArgs.cpp LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include diff --git a/libs/incident/proto/android/os/metadata.proto b/libs/incident/proto/android/os/metadata.proto new file mode 100644 index 000000000000..a1e89b0dbf98 --- /dev/null +++ b/libs/incident/proto/android/os/metadata.proto @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; +option java_multiple_files = true; + +package android.os; + +// This field contains internal metadata associated with an incident report, +// such as the section ids and privacy policy specs from caller as well as how long +// and how many bytes a section takes, etc. +message IncidentMetadata { + + // privacy level of the incident report. + enum Destination { + AUTOMATIC = 0; + EXPLICIT = 1; + LOCAL = 2; + } + optional Destination dest = 1; + + optional int32 request_size = 2; + + optional bool use_dropbox = 3; + + // stats of each section taken in this incident report. + message SectionStats { + // section id. + optional int32 id = 1; + // if the section is successfully taken. + optional bool success = 2; + // number of bytes in the report after filtering. + optional int32 report_size_bytes = 3; + // the total duration to execute the section. + optional int64 exec_duration_ms = 4; + + // number of bytes dumped from the section directly. + optional int32 dump_size_bytes = 5; + // duration of the dump takes. + optional int64 dump_duration_ms = 6; + // true if the section timed out. + optional bool timed_out = 7; + // true if the section is truncated. + optional bool is_truncated = 8; + + // Next Tag: 9 + } + repeated SectionStats sections = 4; +} + |