Merge "Add text dumpsys section to incidentd" into rvc-dev
This commit is contained in:
@@ -20,9 +20,9 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include <android-base/file.h>
|
#include <android-base/file.h>
|
||||||
#include <android-base/properties.h>
|
#include <android-base/properties.h>
|
||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "frameworks/base/core/proto/android/os/backtrace.proto.h"
|
#include "frameworks/base/core/proto/android/os/backtrace.proto.h"
|
||||||
#include "frameworks/base/core/proto/android/os/data.proto.h"
|
#include "frameworks/base/core/proto/android/os/data.proto.h"
|
||||||
#include "frameworks/base/core/proto/android/util/log.proto.h"
|
#include "frameworks/base/core/proto/android/util/log.proto.h"
|
||||||
|
#include "frameworks/base/core/proto/android/util/textdump.proto.h"
|
||||||
#include "incidentd_util.h"
|
#include "incidentd_util.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
@@ -135,7 +136,7 @@ status_t FileSection::Execute(ReportWriter* writer) const {
|
|||||||
status_t ihStatus = wait_child(pid);
|
status_t ihStatus = wait_child(pid);
|
||||||
if (ihStatus != NO_ERROR) {
|
if (ihStatus != NO_ERROR) {
|
||||||
ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
|
ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus));
|
||||||
return ihStatus;
|
return OK; // Not a fatal error.
|
||||||
}
|
}
|
||||||
|
|
||||||
return writer->writeSection(buffer);
|
return writer->writeSection(buffer);
|
||||||
@@ -234,7 +235,7 @@ struct WorkerThreadData : public virtual RefBase {
|
|||||||
Fpipe pipe;
|
Fpipe pipe;
|
||||||
|
|
||||||
// Lock protects these fields
|
// Lock protects these fields
|
||||||
mutex lock;
|
std::mutex lock;
|
||||||
bool workerDone;
|
bool workerDone;
|
||||||
status_t workerError;
|
status_t workerError;
|
||||||
|
|
||||||
@@ -261,83 +262,47 @@ void sigpipe_handler(int signum) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* worker_thread_func(void* cookie) {
|
|
||||||
// Don't crash the service if we write to a closed pipe (which can happen if
|
|
||||||
// dumping times out).
|
|
||||||
signal(SIGPIPE, sigpipe_handler);
|
|
||||||
|
|
||||||
WorkerThreadData* data = (WorkerThreadData*)cookie;
|
|
||||||
status_t err = data->section->BlockingCall(data->pipe.writeFd());
|
|
||||||
|
|
||||||
{
|
|
||||||
unique_lock<mutex> lock(data->lock);
|
|
||||||
data->workerDone = true;
|
|
||||||
data->workerError = err;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->pipe.writeFd().reset();
|
|
||||||
data->decStrong(data->section);
|
|
||||||
// data might be gone now. don't use it after this point in this thread.
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
|
status_t WorkerThreadSection::Execute(ReportWriter* writer) const {
|
||||||
status_t err = NO_ERROR;
|
status_t err = NO_ERROR;
|
||||||
pthread_t thread;
|
|
||||||
pthread_attr_t attr;
|
|
||||||
bool workerDone = false;
|
bool workerDone = false;
|
||||||
FdBuffer buffer;
|
FdBuffer buffer;
|
||||||
|
|
||||||
// Data shared between this thread and the worker thread.
|
// Create shared data and pipe
|
||||||
sp<WorkerThreadData> data = new WorkerThreadData(this);
|
WorkerThreadData data(this);
|
||||||
|
if (!data.pipe.init()) {
|
||||||
// Create the pipe
|
|
||||||
if (!data->pipe.init()) {
|
|
||||||
return -errno;
|
return -errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the thread
|
std::thread([&]() {
|
||||||
err = pthread_attr_init(&attr);
|
// Don't crash the service if writing to a closed pipe (may happen if dumping times out)
|
||||||
if (err != 0) {
|
signal(SIGPIPE, sigpipe_handler);
|
||||||
return -err;
|
status_t err = data.section->BlockingCall(data.pipe.writeFd());
|
||||||
}
|
{
|
||||||
// TODO: Do we need to tweak thread priority?
|
std::unique_lock<std::mutex> lock(data.lock);
|
||||||
err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
data.workerDone = true;
|
||||||
if (err != 0) {
|
data.workerError = err;
|
||||||
pthread_attr_destroy(&attr);
|
// unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
|
||||||
return -err;
|
// the other thread executes to the end, calling ~Fpipe, which is a race condition.
|
||||||
}
|
data.pipe.writeFd().reset();
|
||||||
|
}
|
||||||
// The worker thread needs a reference and we can't let the count go to zero
|
}).detach();
|
||||||
// if that thread is slow to start.
|
|
||||||
data->incStrong(this);
|
|
||||||
|
|
||||||
err = pthread_create(&thread, &attr, worker_thread_func, (void*)data.get());
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
if (err != 0) {
|
|
||||||
data->decStrong(this);
|
|
||||||
return -err;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop reading until either the timeout or the worker side is done (i.e. eof).
|
// Loop reading until either the timeout or the worker side is done (i.e. eof).
|
||||||
err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
|
err = buffer.read(data.pipe.readFd().get(), this->timeoutMs);
|
||||||
if (err != NO_ERROR) {
|
if (err != NO_ERROR) {
|
||||||
ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
|
ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done with the read fd. The worker thread closes the write one so
|
|
||||||
// we never race and get here first.
|
|
||||||
data->pipe.readFd().reset();
|
|
||||||
|
|
||||||
// If the worker side is finished, then return its error (which may overwrite
|
// If the worker side is finished, then return its error (which may overwrite
|
||||||
// our possible error -- but it's more interesting anyway). If not, then we timed out.
|
// our possible error -- but it's more interesting anyway). If not, then we timed out.
|
||||||
{
|
{
|
||||||
unique_lock<mutex> lock(data->lock);
|
std::unique_lock<std::mutex> lock(data.lock);
|
||||||
if (data->workerError != NO_ERROR) {
|
data.pipe.close();
|
||||||
err = data->workerError;
|
if (data.workerError != NO_ERROR) {
|
||||||
|
err = data.workerError;
|
||||||
ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
|
ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
|
||||||
}
|
}
|
||||||
workerDone = data->workerDone;
|
workerDone = data.workerDone;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer->setSectionStats(buffer);
|
writer->setSectionStats(buffer);
|
||||||
@@ -472,6 +437,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
|
|||||||
return NO_ERROR;
|
return NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================================================================================
|
||||||
|
TextDumpsysSection::TextDumpsysSection(int id, const char* service, ...)
|
||||||
|
: WorkerThreadSection(id, REMOTE_CALL_TIMEOUT_MS), mService(service) {
|
||||||
|
name = "dumpsys ";
|
||||||
|
name += service;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, service);
|
||||||
|
while (true) {
|
||||||
|
const char* arg = va_arg(args, const char*);
|
||||||
|
if (arg == NULL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mArgs.add(String16(arg));
|
||||||
|
name += " ";
|
||||||
|
name += arg;
|
||||||
|
}
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextDumpsysSection::~TextDumpsysSection() {}
|
||||||
|
|
||||||
|
status_t TextDumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const {
|
||||||
|
// checkService won't wait for the service to show up like getService will.
|
||||||
|
sp<IBinder> service = defaultServiceManager()->checkService(mService);
|
||||||
|
if (service == NULL) {
|
||||||
|
ALOGW("TextDumpsysSection: Can't lookup service: %s", String8(mService).string());
|
||||||
|
return NAME_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipe
|
||||||
|
Fpipe dumpPipe;
|
||||||
|
if (!dumpPipe.init()) {
|
||||||
|
ALOGW("[%s] failed to setup pipe", this->name.string());
|
||||||
|
return -errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run dumping thread
|
||||||
|
const uint64_t start = Nanotime();
|
||||||
|
std::thread worker([&]() {
|
||||||
|
// Don't crash the service if writing to a closed pipe (may happen if dumping times out)
|
||||||
|
signal(SIGPIPE, sigpipe_handler);
|
||||||
|
status_t err = service->dump(dumpPipe.writeFd().get(), mArgs);
|
||||||
|
if (err != OK) {
|
||||||
|
ALOGW("[%s] dump thread failed. Error: %s", this->name.string(), strerror(-err));
|
||||||
|
}
|
||||||
|
dumpPipe.writeFd().reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Collect dump content
|
||||||
|
std::string content;
|
||||||
|
bool success = ReadFdToString(dumpPipe.readFd(), &content);
|
||||||
|
worker.join(); // Wait for worker to finish
|
||||||
|
dumpPipe.readFd().reset();
|
||||||
|
if (!success) {
|
||||||
|
ALOGW("[%s] failed to read data from pipe", this->name.string());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProtoOutputStream proto;
|
||||||
|
proto.write(util::TextDumpProto::COMMAND, std::string(name.string()));
|
||||||
|
proto.write(util::TextDumpProto::CONTENT, content);
|
||||||
|
proto.write(util::TextDumpProto::DUMP_DURATION_NS, int64_t(Nanotime() - start));
|
||||||
|
|
||||||
|
if (!proto.flush(pipeWriteFd.get()) && errno == EPIPE) {
|
||||||
|
ALOGE("[%s] wrote to a broken pipe\n", this->name.string());
|
||||||
|
return EPIPE;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
// initialization only once in Section.cpp.
|
// initialization only once in Section.cpp.
|
||||||
map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
|
map<log_id_t, log_time> LogSection::gLastLogsRetrieved;
|
||||||
|
|||||||
@@ -112,7 +112,8 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Section that calls dumpsys on a system service.
|
* Section that calls protobuf dumpsys on a system service, usually
|
||||||
|
* "dumpsys [service_name] --proto".
|
||||||
*/
|
*/
|
||||||
class DumpsysSection : public WorkerThreadSection {
|
class DumpsysSection : public WorkerThreadSection {
|
||||||
public:
|
public:
|
||||||
@@ -126,6 +127,21 @@ private:
|
|||||||
Vector<String16> mArgs;
|
Vector<String16> mArgs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Section that calls text dumpsys on a system service, usually "dumpsys [service_name]".
|
||||||
|
*/
|
||||||
|
class TextDumpsysSection : public WorkerThreadSection {
|
||||||
|
public:
|
||||||
|
TextDumpsysSection(int id, const char* service, ...);
|
||||||
|
virtual ~TextDumpsysSection();
|
||||||
|
|
||||||
|
virtual status_t BlockingCall(unique_fd& pipeWriteFd) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
String16 mService;
|
||||||
|
Vector<String16> mArgs;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Section that calls dumpsys on a system service.
|
* Section that calls dumpsys on a system service.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import "frameworks/base/core/proto/android/service/sensor_service.proto";
|
|||||||
import "frameworks/base/core/proto/android/service/usb.proto";
|
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/event_log_tags.proto";
|
||||||
import "frameworks/base/core/proto/android/util/log.proto";
|
import "frameworks/base/core/proto/android/util/log.proto";
|
||||||
|
import "frameworks/base/core/proto/android/util/textdump.proto";
|
||||||
import "frameworks/base/core/proto/android/privacy.proto";
|
import "frameworks/base/core/proto/android/privacy.proto";
|
||||||
import "frameworks/base/core/proto/android/section.proto";
|
import "frameworks/base/core/proto/android/section.proto";
|
||||||
import "frameworks/base/proto/src/ipconnectivity.proto";
|
import "frameworks/base/proto/src/ipconnectivity.proto";
|
||||||
@@ -510,6 +511,17 @@ message IncidentProto {
|
|||||||
(section).args = "sensorservice --proto"
|
(section).args = "sensorservice --proto"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Dumps in text format (on userdebug and eng builds only): 4000 ~ 4999
|
||||||
|
optional android.util.TextDumpProto textdump_wifi = 4000 [
|
||||||
|
(section).type = SECTION_TEXT_DUMPSYS,
|
||||||
|
(section).args = "wifi"
|
||||||
|
];
|
||||||
|
|
||||||
|
optional android.util.TextDumpProto textdump_bluetooth = 4001 [
|
||||||
|
(section).type = SECTION_TEXT_DUMPSYS,
|
||||||
|
(section).args = "bluetooth_manager"
|
||||||
|
];
|
||||||
|
|
||||||
// Reserved for OEMs.
|
// Reserved for OEMs.
|
||||||
extensions 50000 to 100000;
|
extensions 50000 to 100000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,10 @@ enum SectionType {
|
|||||||
|
|
||||||
// incidentd calls tombstoned for annotated field
|
// incidentd calls tombstoned for annotated field
|
||||||
SECTION_TOMBSTONE = 6;
|
SECTION_TOMBSTONE = 6;
|
||||||
|
|
||||||
|
// incidentd calls legacy text dumpsys for annotated field. The section will only be generated
|
||||||
|
// on userdebug and eng builds.
|
||||||
|
SECTION_TEXT_DUMPSYS = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SectionFlags {
|
message SectionFlags {
|
||||||
|
|||||||
33
core/proto/android/util/textdump.proto
Normal file
33
core/proto/android/util/textdump.proto
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 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";
|
||||||
|
package android.util;
|
||||||
|
|
||||||
|
import "frameworks/base/core/proto/android/privacy.proto";
|
||||||
|
|
||||||
|
option java_multiple_files = true;
|
||||||
|
|
||||||
|
message TextDumpProto {
|
||||||
|
option (android.msg_privacy).dest = DEST_EXPLICIT;
|
||||||
|
|
||||||
|
// The command that was executed
|
||||||
|
optional string command = 1;
|
||||||
|
// The content that was dumped
|
||||||
|
optional string content = 2;
|
||||||
|
// The duration of the dump process
|
||||||
|
optional int64 dump_duration_ns = 3;
|
||||||
|
}
|
||||||
@@ -415,7 +415,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SectionFlags s = getSectionFlags(field);
|
const SectionFlags s = getSectionFlags(field);
|
||||||
if (s.userdebug_and_eng_only()) {
|
if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
|
||||||
printf("#if ALLOW_RESTRICTED_SECTIONS\n");
|
printf("#if ALLOW_RESTRICTED_SECTIONS\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -449,8 +449,13 @@ static bool generateSectionListCpp(Descriptor const* descriptor) {
|
|||||||
printf(" new TombstoneSection(%d, \"%s\"),\n", field->number(),
|
printf(" new TombstoneSection(%d, \"%s\"),\n", field->number(),
|
||||||
s.args().c_str());
|
s.args().c_str());
|
||||||
break;
|
break;
|
||||||
|
case SECTION_TEXT_DUMPSYS:
|
||||||
|
printf(" new TextDumpsysSection(%d, ", field->number());
|
||||||
|
splitAndPrint(s.args());
|
||||||
|
printf(" NULL),\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (s.userdebug_and_eng_only()) {
|
if (s.userdebug_and_eng_only() || s.type() == SECTION_TEXT_DUMPSYS) {
|
||||||
printf("#endif\n");
|
printf("#endif\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user