Loading cmds/incidentd/src/Section.cpp +97 −61 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -472,6 +437,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const { 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. map<log_id_t, log_time> LogSection::gLastLogsRetrieved; Loading cmds/incidentd/src/Section.h +17 −1 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -126,6 +127,21 @@ private: 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. */ Loading core/proto/android/os/incident.proto +12 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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; } core/proto/android/section.proto +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading core/proto/android/util/textdump.proto 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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; } Loading
cmds/incidentd/src/Section.cpp +97 −61 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -472,6 +437,77 @@ status_t DumpsysSection::BlockingCall(unique_fd& pipeWriteFd) const { 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. map<log_id_t, log_time> LogSection::gLastLogsRetrieved; Loading
cmds/incidentd/src/Section.h +17 −1 Original line number Diff line number Diff line Loading @@ -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: Loading @@ -126,6 +127,21 @@ private: 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. */ Loading
core/proto/android/os/incident.proto +12 −0 Original line number Diff line number Diff line Loading @@ -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"; Loading Loading @@ -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; }
core/proto/android/section.proto +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading
core/proto/android/util/textdump.proto 0 → 100644 +33 −0 Original line number Diff line number Diff line /* * 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; }