diff options
author | Mike Ma <yanmin@google.com> | 2020-02-19 02:56:04 -0800 |
---|---|---|
committer | Mike Ma <yanmin@google.com> | 2020-02-23 08:05:42 +0000 |
commit | 5510f7c8cd3df39c85d4308d558841b9ed4aa46d (patch) | |
tree | 76161fcb381f448a3f12b54be5f4fe4c48f6476d /cmds/incidentd | |
parent | b629d69db047c40f2b110f2d73ad8ceb1b32ea68 (diff) |
Add text dumpsys section to incidentd
Enable Incidentd to dump any existing dumpsys section in plain text
(as dumpsys.proto), only in eng or userdebug build. This is for a
few dumpsys services that are prohibitively expensive to migrate to
protobuf dumpsys or will undergo a major rewrite (thus render the
previously defined proto completely useless).
Bug: 149816498
Bug: 146085372
Bug: 146086519
Test: $ incident -p EXPLICIT 4000 4001
Change-Id: I0693d9bace0055cfeb63d7c8d48995d57dc0b733
(cherry picked from commit 95ba73f9c9815da08cdb7015195939a3c1b250bd)
Diffstat (limited to 'cmds/incidentd')
-rw-r--r-- | cmds/incidentd/src/Section.cpp | 158 | ||||
-rw-r--r-- | cmds/incidentd/src/Section.h | 18 |
2 files changed, 114 insertions, 62 deletions
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 2229e1c7ca07..d79123b6a972 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -20,9 +20,9 @@ #include <dirent.h> #include <errno.h> - #include <mutex> #include <set> +#include <thread> #include <android-base/file.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/data.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" namespace android { @@ -135,7 +136,7 @@ status_t FileSection::Execute(ReportWriter* writer) const { status_t ihStatus = wait_child(pid); if (ihStatus != NO_ERROR) { ALOGW("[%s] abnormal child process: %s", this->name.string(), strerror(-ihStatus)); - return ihStatus; + return OK; // Not a fatal error. } return writer->writeSection(buffer); @@ -234,7 +235,7 @@ struct WorkerThreadData : public virtual RefBase { Fpipe pipe; // Lock protects these fields - mutex lock; + std::mutex lock; bool workerDone; 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 err = NO_ERROR; - pthread_t thread; - pthread_attr_t attr; bool workerDone = false; FdBuffer buffer; - // Data shared between this thread and the worker thread. - sp<WorkerThreadData> data = new WorkerThreadData(this); - - // Create the pipe - if (!data->pipe.init()) { + // Create shared data and pipe + WorkerThreadData data(this); + if (!data.pipe.init()) { return -errno; } - // Create the thread - err = pthread_attr_init(&attr); - if (err != 0) { - return -err; - } - // TODO: Do we need to tweak thread priority? - err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - if (err != 0) { - pthread_attr_destroy(&attr); - return -err; - } - - // The worker thread needs a reference and we can't let the count go to zero - // 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; - } + std::thread([&]() { + // Don't crash the service if writing to a closed pipe (may happen if dumping times out) + signal(SIGPIPE, sigpipe_handler); + status_t err = data.section->BlockingCall(data.pipe.writeFd()); + { + std::unique_lock<std::mutex> lock(data.lock); + data.workerDone = true; + data.workerError = err; + // unique_fd is not thread safe. If we don't lock it, reset() may pause half way while + // the other thread executes to the end, calling ~Fpipe, which is a race condition. + data.pipe.writeFd().reset(); + } + }).detach(); // 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) { 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 // our possible error -- but it's more interesting anyway). If not, then we timed out. { - unique_lock<mutex> lock(data->lock); - if (data->workerError != NO_ERROR) { - err = data->workerError; + std::unique_lock<std::mutex> lock(data.lock); + data.pipe.close(); + if (data.workerError != NO_ERROR) { + err = data.workerError; ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err)); } - workerDone = data->workerDone; + workerDone = data.workerDone; } writer->setSectionStats(buffer); @@ -473,6 +438,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const { } // ================================================================================ +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. map<log_id_t, log_time> LogSection::gLastLogsRetrieved; diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h index 0bb9da9aac5b..6162b3ae2ceb 100644 --- a/cmds/incidentd/src/Section.h +++ b/cmds/incidentd/src/Section.h @@ -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 { public: @@ -127,6 +128,21 @@ private: }; /** + * 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. */ class SystemPropertyDumpsysSection : public WorkerThreadSection { |