diff options
-rw-r--r-- | cmds/incidentd/src/Section.cpp | 158 | ||||
-rw-r--r-- | cmds/incidentd/src/Section.h | 18 | ||||
-rw-r--r-- | core/proto/android/os/incident.proto | 12 | ||||
-rw-r--r-- | core/proto/android/section.proto | 4 | ||||
-rw-r--r-- | core/proto/android/util/textdump.proto | 33 | ||||
-rw-r--r-- | tools/incident_section_gen/main.cpp | 9 |
6 files changed, 170 insertions, 64 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 { diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 7d1cf5d1190e..d6687f08b542 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -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/util/event_log_tags.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/section.proto"; import "frameworks/base/proto/src/ipconnectivity.proto"; @@ -510,6 +511,17 @@ message IncidentProto { (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. extensions 50000 to 100000; } diff --git a/core/proto/android/section.proto b/core/proto/android/section.proto index 5afe22a3095f..299d6f9a1c62 100644 --- a/core/proto/android/section.proto +++ b/core/proto/android/section.proto @@ -46,6 +46,10 @@ enum SectionType { // incidentd calls tombstoned for annotated field 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 { diff --git a/core/proto/android/util/textdump.proto b/core/proto/android/util/textdump.proto new file mode 100644 index 000000000000..6118487b1387 --- /dev/null +++ b/core/proto/android/util/textdump.proto @@ -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; +} diff --git a/tools/incident_section_gen/main.cpp b/tools/incident_section_gen/main.cpp index ded4b916c452..786223a430ae 100644 --- a/tools/incident_section_gen/main.cpp +++ b/tools/incident_section_gen/main.cpp @@ -415,7 +415,7 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { } 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"); } @@ -449,8 +449,13 @@ static bool generateSectionListCpp(Descriptor const* descriptor) { printf(" new TombstoneSection(%d, \"%s\"),\n", field->number(), s.args().c_str()); 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"); } } |