Merge "Add an API to dump incident report for dumpstate"

am: a11beeca12

Change-Id: If5ca98de9a5a85ae6e2aff90224a80e925ea419c
This commit is contained in:
Mike Ma
2019-09-23 21:17:34 -07:00
committed by android-build-merger
5 changed files with 91 additions and 62 deletions

View File

@@ -197,6 +197,26 @@ parse_receiver_arg(const string& arg, string* pkg, string* cls)
return true;
}
// ================================================================================
static int
stream_output(const int read_fd, const int write_fd) {
while (true) {
uint8_t buf[4096];
ssize_t amt = TEMP_FAILURE_RETRY(read(read_fd, buf, sizeof(buf)));
if (amt < 0) {
break;
} else if (amt == 0) {
break;
}
ssize_t wamt = TEMP_FAILURE_RETRY(write(write_fd, buf, amt));
if (wamt != amt) {
return errno;
}
}
return 0;
}
// ================================================================================
static void
usage(FILE* out)
@@ -208,11 +228,13 @@ usage(FILE* out)
fprintf(out, "OPTIONS\n");
fprintf(out, " -l list available sections\n");
fprintf(out, " -p privacy spec, LOCAL, EXPLICIT or AUTOMATIC. Default AUTOMATIC.\n");
fprintf(out, " -r REASON human readable description of why the report is taken.\n");
fprintf(out, "\n");
fprintf(out, "and one of these destinations:\n");
fprintf(out, " -b (default) print the report to stdout (in proto format)\n");
fprintf(out, " -d send the report into dropbox\n");
fprintf(out, " -r REASON human readable description of why the report is taken.\n");
fprintf(out, " -u print a full report to stdout for dumpstate to zip as a bug\n");
fprintf(out, " report. SECTION is ignored. Should only be called by dumpstate.\n");
fprintf(out, " -s PKG/CLS send broadcast to the broadcast receiver.\n");
fprintf(out, "\n");
fprintf(out, " SECTION the field numbers of the incident report fields to include\n");
@@ -224,14 +246,14 @@ main(int argc, char** argv)
{
Status status;
IncidentReportArgs args;
enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST } destination = DEST_UNSET;
enum { DEST_UNSET, DEST_DROPBOX, DEST_STDOUT, DEST_BROADCAST, DEST_DUMPSTATE } destination = DEST_UNSET;
int privacyPolicy = PRIVACY_POLICY_AUTOMATIC;
string reason;
string receiverArg;
// Parse the args
int opt;
while ((opt = getopt(argc, argv, "bhdlp:r:s:")) != -1) {
while ((opt = getopt(argc, argv, "bhdlp:r:s:u")) != -1) {
switch (opt) {
case 'h':
usage(stdout);
@@ -253,6 +275,13 @@ main(int argc, char** argv)
}
destination = DEST_DROPBOX;
break;
case 'u':
if (!(destination == DEST_UNSET || destination == DEST_DUMPSTATE)) {
usage(stderr);
return 1;
}
destination = DEST_DUMPSTATE;
break;
case 'p':
privacyPolicy = get_privacy_policy(optarg);
break;
@@ -355,21 +384,16 @@ main(int argc, char** argv)
// Wait for the result and print out the data they send.
//IPCThreadState::self()->joinThreadPool();
while (true) {
uint8_t buf[4096];
ssize_t amt = TEMP_FAILURE_RETRY(read(fds[0], buf, sizeof(buf)));
if (amt < 0) {
break;
} else if (amt == 0) {
break;
}
ssize_t wamt = TEMP_FAILURE_RETRY(write(STDOUT_FILENO, buf, amt));
if (wamt != amt) {
return errno;
}
return stream_output(fds[0], STDOUT_FILENO);
} else if (destination == DEST_DUMPSTATE) {
// Call into the service
sp<StatusListener> listener(new StatusListener());
status = service->reportIncidentToDumpstate(writeEnd, listener);
if (!status.isOk()) {
fprintf(stderr, "reportIncident returned \"%s\"\n", status.toString8().string());
return 1;
}
return stream_output(fds[0], STDOUT_FILENO);
} else {
status = service->reportIncident(args);
if (!status.isOk()) {

View File

@@ -46,12 +46,11 @@ enum {
#define DEFAULT_BYTES_SIZE_LIMIT (96 * 1024 * 1024) // 96MB
#define DEFAULT_REFACTORY_PERIOD_MS (24 * 60 * 60 * 1000) // 1 Day
// Skip these sections for dumpstate only. Dumpstate allows 10s max for each service to dump.
// Skip these sections (for dumpstate only)
// Skip logs (1100 - 1108) and traces (1200 - 1202) because they are already in the bug report.
// Skip 3018 because it takes too long.
#define SKIPPED_SECTIONS { 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
1200, 1201, 1202, /* Native, hal, java traces */ \
3018 /* "meminfo -a --proto" */ }
#define SKIPPED_DUMPSTATE_SECTIONS { \
1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
1200, 1201, 1202, /* Native, hal, java traces */ }
namespace android {
namespace os {
@@ -307,6 +306,39 @@ Status IncidentService::reportIncidentToStream(const IncidentReportArgs& args,
return Status::ok();
}
Status IncidentService::reportIncidentToDumpstate(const unique_fd& stream,
const sp<IIncidentReportStatusListener>& listener) {
uid_t caller = IPCThreadState::self()->getCallingUid();
if (caller != AID_ROOT && caller != AID_SHELL) {
ALOGW("Calling uid %d does not have permission: only ROOT or SHELL allowed", caller);
return Status::fromExceptionCode(Status::EX_SECURITY, "Only ROOT or SHELL allowed");
}
ALOGD("Stream incident report to dumpstate");
IncidentReportArgs incidentArgs;
// Privacy policy for dumpstate incident reports is always EXPLICIT.
incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
int skipped[] = SKIPPED_DUMPSTATE_SECTIONS;
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
&& !section_requires_specific_mention(id)) {
incidentArgs.addSection(id);
}
}
// The ReportRequest takes ownership of the fd, so we need to dup it.
int fd = dup(stream.get());
if (fd < 0) {
return Status::fromStatusT(-errno);
}
mHandler->scheduleStreamingReport(incidentArgs, listener, fd);
return Status::ok();
}
Status IncidentService::systemRunning() {
if (IPCThreadState::self()->getCallingUid() != AID_SYSTEM) {
return Status::fromExceptionCode(Status::EX_SECURITY,
@@ -551,43 +583,6 @@ status_t IncidentService::cmd_privacy(FILE* in, FILE* out, FILE* err, Vector<Str
return NO_ERROR;
}
status_t IncidentService::dump(int fd, const Vector<String16>& args) {
if (std::find(args.begin(), args.end(), String16("--proto")) == args.end()) {
ALOGD("Skip dumping incident. Only proto format is supported.");
dprintf(fd, "Incident dump only supports proto version.\n");
return NO_ERROR;
}
ALOGD("Dump incident proto");
IncidentReportArgs incidentArgs;
incidentArgs.setPrivacyPolicy(PRIVACY_POLICY_EXPLICIT);
int skipped[] = SKIPPED_SECTIONS;
for (const Section** section = SECTION_LIST; *section; section++) {
const int id = (*section)->id;
if (std::find(std::begin(skipped), std::end(skipped), id) == std::end(skipped)
&& !section_requires_specific_mention(id)) {
incidentArgs.addSection(id);
}
}
if (!checkIncidentPermissions(incidentArgs).isOk()) {
return PERMISSION_DENIED;
}
// The ReportRequest takes ownership of the fd, so we need to dup it.
int fd1 = dup(fd);
if (fd1 < 0) {
return -errno;
}
// TODO: Remove this. Someone even dumpstate, wanting to get an incident report
// should use the API. That will take making dumpstated call the API, which is a
// good thing. It also means it won't be subject to the timeout.
mHandler->scheduleStreamingReport(incidentArgs, NULL, fd1);
return NO_ERROR;
}
} // namespace incidentd
} // namespace os
} // namespace android

View File

@@ -123,6 +123,9 @@ public:
const sp<IIncidentReportStatusListener>& listener,
const unique_fd& stream);
virtual Status reportIncidentToDumpstate(const unique_fd& stream,
const sp<IIncidentReportStatusListener>& listener);
virtual Status systemRunning();
virtual Status getIncidentReportList(const String16& pkg, const String16& cls,
@@ -140,7 +143,6 @@ public:
virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
uint32_t flags) override;
virtual status_t command(FILE* in, FILE* out, FILE* err, Vector<String8>& args);
virtual status_t dump(int fd, const Vector<String16>& args);
private:
sp<WorkDirectory> mWorkDirectory;

View File

@@ -45,8 +45,7 @@ int main(int /*argc*/, char** /*argv*/) {
// Create the service
sp<IncidentService> service = new IncidentService(looper);
if (defaultServiceManager()->addService(String16("incident"), service, false,
IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO) != 0) {
if (defaultServiceManager()->addService(String16("incident"), service) != 0) {
ALOGE("Failed to add service");
return -1;
}

View File

@@ -42,6 +42,15 @@ interface IIncidentManager {
@nullable IIncidentReportStatusListener listener,
FileDescriptor stream);
/**
* Takes a report with the given args, reporting status to the optional listener.
* This should only be callable by dumpstate (enforced by callee).
*
* When the report is completed, the system report listener will be notified.
*/
oneway void reportIncidentToDumpstate(FileDescriptor stream,
@nullable IIncidentReportStatusListener listener);
/**
* Tell the incident daemon that the android system server is up and running.
*/