diff options
Diffstat (limited to 'cmds')
38 files changed, 2244 insertions, 631 deletions
diff --git a/cmds/atrace/Android.bp b/cmds/atrace/Android.bp index b850390d93..bb84a18070 100644 --- a/cmds/atrace/Android.bp +++ b/cmds/atrace/Android.bp @@ -18,8 +18,6 @@ cc_binary { "libcutils", "libz", "libbase", - ], - static_libs: [ "libpdx_default_transport", ], diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 5d21f6a559..2561e0acd7 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -127,6 +127,7 @@ static const TracingCategory k_categories[] = { { OPT, "events/sched/sched_waking/enable" }, { OPT, "events/sched/sched_blocked_reason/enable" }, { OPT, "events/sched/sched_cpu_hotplug/enable" }, + { OPT, "events/sched/sched_pi_setprio/enable" }, { OPT, "events/cgroup/enable" }, } }, { "irq", "IRQ Events", 0, { @@ -187,7 +188,10 @@ static const TracingCategory k_categories[] = { { REQ, "events/cpufreq_interactive/enable" }, } }, { "sync", "Synchronization", 0, { - { REQ, "events/sync/enable" }, + // before linux kernel 4.9 + { OPT, "events/sync/enable" }, + // starting in linux kernel 4.9 + { OPT, "events/fence/enable" }, } }, { "workq", "Kernel Workqueues", 0, { { REQ, "events/workqueue/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index 406f90990f..579cfaf57d 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -1,6 +1,6 @@ ## Permissions to allow system-wide tracing to the kernel trace buffer. ## -on post-fs +on late-init # Allow writing to the kernel trace log. chmod 0222 /sys/kernel/debug/tracing/trace_marker @@ -29,6 +29,8 @@ on post-fs chmod 0666 /sys/kernel/tracing/events/sched/sched_blocked_reason/enable chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_cpu_hotplug/enable chmod 0666 /sys/kernel/tracing/events/sched/sched_cpu_hotplug/enable + chmod 0666 /sys/kernel/debug/tracing/events/sched/sched_pi_setprio/enable + chmod 0666 /sys/kernel/tracing/events/sched/sched_pi_setprio/enable chmod 0666 /sys/kernel/debug/tracing/events/cgroup/enable chmod 0666 /sys/kernel/tracing/events/cgroup/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency/enable @@ -119,6 +121,12 @@ on post-fs chmod 0666 /sys/kernel/tracing/events/block/block_rq_complete/enable chmod 0666 /sys/kernel/debug/tracing/events/block/block_rq_complete/enable + # graphics + chmod 0666 /sys/kernel/tracing/events/sde/enable + chmod 0666 /sys/kernel/debug/tracing/events/sde/enable + chmod 0666 /sys/kernel/tracing/events/mdss/enable + chmod 0666 /sys/kernel/debug/tracing/events/mdss/enable + # Tracing disabled by default write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp index 022d3dd6ba..423853175b 100644 --- a/cmds/cmd/cmd.cpp +++ b/cmds/cmd/cmd.cpp @@ -61,7 +61,8 @@ class MyShellCallback : public BnShellCallback public: bool mActive = true; - virtual int openOutputFile(const String16& path, const String16& seLinuxContext) { + virtual int openFile(const String16& path, const String16& seLinuxContext, + const String16& mode) { String8 path8(path); char cwd[256]; getcwd(cwd, 256); @@ -71,7 +72,32 @@ public: aerr << "Open attempt after active for: " << fullPath << endl; return -EPERM; } - int fd = open(fullPath.string(), O_WRONLY|O_CREAT|O_TRUNC, S_IRWXU|S_IRWXG); +#if DEBUG + ALOGD("openFile: %s, full=%s", path8.string(), fullPath.string()); +#endif + int flags = 0; + bool checkRead = false; + bool checkWrite = false; + if (mode == String16("w")) { + flags = O_WRONLY|O_CREAT|O_TRUNC; + checkWrite = true; + } else if (mode == String16("w+")) { + flags = O_RDWR|O_CREAT|O_TRUNC; + checkRead = checkWrite = true; + } else if (mode == String16("r")) { + flags = O_RDONLY; + checkRead = true; + } else if (mode == String16("r+")) { + flags = O_RDWR; + checkRead = checkWrite = true; + } else { + aerr << "Invalid mode requested: " << mode.string() << endl; + return -EINVAL; + } + int fd = open(fullPath.string(), flags, S_IRWXU|S_IRWXG); +#if DEBUG + ALOGD("openFile: fd=%d", fd); +#endif if (fd < 0) { return fd; } @@ -80,14 +106,33 @@ public: security_context_t tmp = NULL; getfilecon(fullPath.string(), &tmp); Unique_SecurityContext context(tmp); - int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), - "file", "write", NULL); - if (accessGranted != 0) { - close(fd); - aerr << "System server has no access to file context " << context.get() - << " (from path " << fullPath.string() << ", context " - << seLinuxContext8.string() << ")" << endl; - return -EPERM; + if (checkWrite) { + int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), + "file", "write", NULL); + if (accessGranted != 0) { +#if DEBUG + ALOGD("openFile: failed selinux write check!"); +#endif + close(fd); + aerr << "System server has no access to write file context " << context.get() + << " (from path " << fullPath.string() << ", context " + << seLinuxContext8.string() << ")" << endl; + return -EPERM; + } + } + if (checkRead) { + int accessGranted = selinux_check_access(seLinuxContext8.string(), context.get(), + "file", "read", NULL); + if (accessGranted != 0) { +#if DEBUG + ALOGD("openFile: failed selinux read check!"); +#endif + close(fd); + aerr << "System server has no access to read file context " << context.get() + << " (from path " << fullPath.string() << ", context " + << seLinuxContext8.string() << ")" << endl; + return -EPERM; + } } } return fd; @@ -122,15 +167,11 @@ int main(int argc, char* const argv[]) { signal(SIGPIPE, SIG_IGN); sp<ProcessState> proc = ProcessState::self(); - // setThreadPoolMaxThreadCount(0) actually tells the kernel it's - // not allowed to spawn any additional threads, but we still spawn - // a binder thread from userspace when we call startThreadPool(). - // This is safe because we only have 2 callbacks, neither of which - // block. - // See b/36066697 for rationale - proc->setThreadPoolMaxThreadCount(0); proc->startThreadPool(); +#if DEBUG + ALOGD("cmd: starting"); +#endif sp<IServiceManager> sm = defaultServiceManager(); fflush(stdout); if (sm == NULL) { diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index ce3a6aad7a..b04543bd40 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -14,7 +14,7 @@ // limitations under the License. cc_defaults { - name: "dumpstate_defaults", + name: "dumpstate_cflag_defaults", cflags: [ "-Wall", "-Werror", @@ -26,7 +26,7 @@ cc_defaults { cc_library_shared { name: "libdumpstateutil", - defaults: ["dumpstate_defaults"], + defaults: ["dumpstate_cflag_defaults"], vendor_available: true, vndk: { enabled: true, @@ -47,7 +47,7 @@ cc_library_shared { cc_library_shared { name: "libdumpstateaidl", - defaults: ["dumpstate_defaults"], + defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "libbinder", "libutils", @@ -63,9 +63,9 @@ cc_library_shared { ], } -cc_binary { - name: "dumpstate", - defaults: ["dumpstate_defaults"], +cc_defaults { + name: "dumpstate_defaults", + defaults: ["dumpstate_cflag_defaults"], shared_libs: [ "android.hardware.dumpstate@1.0", "libziparchive", @@ -76,16 +76,29 @@ cc_binary { "libdebuggerd_client", "libdumpstateaidl", "libdumpstateutil", + "libdumputils", "libhidlbase", "libhidltransport", "liblog", "libutils", ], srcs: [ - "DumpstateInternal.cpp", + "DumpstateSectionReporter.cpp", "DumpstateService.cpp", "utils.cpp", + ], + static_libs: [ + "libdumpsys", + "libserviceutils" + ], +} + +cc_binary { + name: "dumpstate", + defaults: ["dumpstate_defaults"], + srcs: [ "dumpstate.cpp", + "main.cpp", ], init_rc: ["dumpstate.rc"], } @@ -93,24 +106,18 @@ cc_binary { cc_test { name: "dumpstate_test", defaults: ["dumpstate_defaults"], - shared_libs: [ - "libziparchive", - "libbase", - "libbinder", - "libcutils", - "libdebuggerd_client", - "libdumpstateaidl", - "libdumpstateutil", - "libhidlbase", - "libhidltransport", - "liblog", - "libutils", - ], srcs: [ - "DumpstateInternal.cpp", - "DumpstateService.cpp", - "utils.cpp", "tests/dumpstate_test.cpp", ], static_libs: ["libgmock"], } + +cc_test { + name: "dumpstate_smoke_test", + defaults: ["dumpstate_defaults"], + srcs: [ + "dumpstate.cpp", + "tests/dumpstate_smoke_test.cpp", + ], + static_libs: ["libgmock"], +} diff --git a/cmds/dumpstate/DumpstateSectionReporter.cpp b/cmds/dumpstate/DumpstateSectionReporter.cpp new file mode 100644 index 0000000000..f814bde26d --- /dev/null +++ b/cmds/dumpstate/DumpstateSectionReporter.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 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. + */ + +#define LOG_TAG "dumpstate" + +#include "DumpstateSectionReporter.h" + +namespace android { +namespace os { +namespace dumpstate { + +DumpstateSectionReporter::DumpstateSectionReporter(const std::string& title, + sp<android::os::IDumpstateListener> listener, + bool sendReport) + : title_(title), listener_(listener), sendReport_(sendReport), status_(OK), size_(-1) { + started_ = std::chrono::steady_clock::now(); +} + +DumpstateSectionReporter::~DumpstateSectionReporter() { + if ((listener_ != nullptr) && (sendReport_)) { + auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - started_); + listener_->onSectionComplete(title_, status_, size_, (int32_t)elapsed.count()); + } +} + +} // namespace dumpstate +} // namespace os +} // namespace android diff --git a/cmds/dumpstate/DumpstateSectionReporter.h b/cmds/dumpstate/DumpstateSectionReporter.h new file mode 100644 index 0000000000..e971de84c5 --- /dev/null +++ b/cmds/dumpstate/DumpstateSectionReporter.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2018 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. + */ + +#ifndef ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ +#define ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ + +#include <android/os/IDumpstateListener.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace os { +namespace dumpstate { + + +/* + * Helper class used to report per section details to a listener. + * + * Typical usage: + * + * DumpstateSectionReporter sectionReporter(title, listener, sendReport); + * sectionReporter.setSize(5000); + * + */ +class DumpstateSectionReporter { + public: + DumpstateSectionReporter(const std::string& title, sp<android::os::IDumpstateListener> listener, + bool sendReport); + + ~DumpstateSectionReporter(); + + void setStatus(status_t status) { + status_ = status; + } + + void setSize(int size) { + size_ = size; + } + + private: + std::string title_; + android::sp<android::os::IDumpstateListener> listener_; + bool sendReport_; + status_t status_; + int size_; + std::chrono::time_point<std::chrono::steady_clock> started_; +}; + +} // namespace dumpstate +} // namespace os +} // namespace android + +#endif // ANDROID_OS_DUMPSTATESECTIONREPORTER_H_ diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index efe0466d07..49a78e751b 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -52,6 +52,7 @@ status_t DumpstateService::Start() { binder::Status DumpstateService::setListener(const std::string& name, const sp<IDumpstateListener>& listener, + bool getSectionDetails, sp<IDumpstateToken>* returned_token) { *returned_token = nullptr; if (name.empty()) { @@ -70,6 +71,7 @@ binder::Status DumpstateService::setListener(const std::string& name, ds_.listener_name_ = name; ds_.listener_ = listener; + ds_.report_section_ = getSectionDetails; *returned_token = new DumpstateToken(); return binder::Status::ok(); diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h index 4352d3dacf..7bca24ae33 100644 --- a/cmds/dumpstate/DumpstateService.h +++ b/cmds/dumpstate/DumpstateService.h @@ -38,6 +38,7 @@ class DumpstateService : public BinderService<DumpstateService>, public BnDumpst status_t dump(int fd, const Vector<String16>& args) override; binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener, + bool getSectionDetails, sp<IDumpstateToken>* returned_token) override; private: diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index ede4254a9b..85eb464104 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -43,7 +43,7 @@ namespace { static constexpr const char* kSuPath = "/system/xbin/su"; -static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) { +static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { sigset_t child_mask, old_mask; sigemptyset(&child_mask); sigaddset(&child_mask, SIGCHLD); @@ -54,10 +54,11 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) { } timespec ts; - ts.tv_sec = timeout_seconds; - ts.tv_nsec = 0; + ts.tv_sec = MSEC_TO_SEC(timeout_ms); + ts.tv_nsec = (timeout_ms % 1000) * 1000000; int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, NULL, &ts)); int saved_errno = errno; + // Set the signals back the way they were. if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) { printf("*** sigprocmask failed: %s\n", strerror(errno)); @@ -91,7 +92,7 @@ static bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) { CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build(); CommandOptions CommandOptions::AS_ROOT = CommandOptions::WithTimeout(10).AsRoot().Build(); -CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) { +CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout_ms) : values(timeout_ms) { } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() { @@ -130,8 +131,8 @@ CommandOptions CommandOptions::CommandOptionsBuilder::Build() { return CommandOptions(values); } -CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout) - : timeout_(timeout), +CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout_ms) + : timeout_ms_(timeout_ms), always_(false), account_mode_(DONT_DROP_ROOT), output_mode_(NORMAL_OUTPUT), @@ -142,7 +143,11 @@ CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(valu } int64_t CommandOptions::Timeout() const { - return values.timeout_; + return MSEC_TO_SEC(values.timeout_ms_); +} + +int64_t CommandOptions::TimeoutInMs() const { + return values.timeout_ms_; } bool CommandOptions::Always() const { @@ -161,8 +166,12 @@ std::string CommandOptions::LoggingMessage() const { return values.logging_message_; } -CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) { - return CommandOptions::CommandOptionsBuilder(timeout); +CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout_sec) { + return CommandOptions::CommandOptionsBuilder(SEC_TO_MSEC(timeout_sec)); +} + +CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t timeout_ms) { + return CommandOptions::CommandOptionsBuilder(timeout_ms); } std::string PropertiesHelper::build_type_ = ""; @@ -314,7 +323,7 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri /* handle parent case */ int status; - bool ret = waitpid_with_timeout(pid, options.Timeout(), &status); + bool ret = waitpid_with_timeout(pid, options.TimeoutInMs(), &status); fsync(fd); uint64_t elapsed = Nanotime() - start; @@ -333,9 +342,9 @@ int RunCommandToFd(int fd, const std::string& title, const std::vector<std::stri static_cast<float>(elapsed) / NANOS_PER_SEC, pid); } kill(pid, SIGTERM); - if (!waitpid_with_timeout(pid, 5, nullptr)) { + if (!waitpid_with_timeout(pid, 5000, nullptr)) { kill(pid, SIGKILL); - if (!waitpid_with_timeout(pid, 5, nullptr)) { + if (!waitpid_with_timeout(pid, 5000, nullptr)) { if (!silent) dprintf(fd, "could not kill command '%s' (pid %d) even with SIGKILL.\n", command, pid); diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 698ceffcc4..8342099821 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -19,6 +19,16 @@ #include <cstdint> #include <string> +/* + * Converts seconds to milliseconds. + */ +#define SEC_TO_MSEC(second) (second * 1000) + +/* + * Converts milliseconds to seconds. + */ +#define MSEC_TO_SEC(millisecond) (millisecond / 1000) + namespace android { namespace os { namespace dumpstate { @@ -66,9 +76,9 @@ class CommandOptions { private: class CommandOptionsValues { private: - CommandOptionsValues(int64_t timeout); + CommandOptionsValues(int64_t timeout_ms); - int64_t timeout_; + int64_t timeout_ms_; bool always_; PrivilegeMode account_mode_; OutputMode output_mode_; @@ -102,13 +112,15 @@ class CommandOptions { CommandOptions Build(); private: - CommandOptionsBuilder(int64_t timeout); + CommandOptionsBuilder(int64_t timeout_ms); CommandOptionsValues values; friend class CommandOptions; }; - /** Gets the command timeout, in seconds. */ + /** Gets the command timeout in seconds. */ int64_t Timeout() const; + /** Gets the command timeout in milliseconds. */ + int64_t TimeoutInMs() const; /* Checks whether the command should always be run, even on dry-run mode. */ bool Always() const; /** Gets the PrivilegeMode of the command. */ @@ -118,8 +130,11 @@ class CommandOptions { /** Gets the logging message header, it any. */ std::string LoggingMessage() const; - /** Creates a builder with the requied timeout. */ - static CommandOptionsBuilder WithTimeout(int64_t timeout); + /** Creates a builder with the requied timeout in seconds. */ + static CommandOptionsBuilder WithTimeout(int64_t timeout_sec); + + /** Creates a builder with the requied timeout in milliseconds. */ + static CommandOptionsBuilder WithTimeoutInMs(int64_t timeout_ms); // Common options. static CommandOptions DEFAULT; diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index 4becccfc6d..9b11b960c5 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -30,6 +30,9 @@ interface IDumpstate { * * Returns a token used to monitor dumpstate death, or `nullptr` if the listener was already * set (the listener behaves like a Highlander: There Can be Only One). + * Set {@code getSectionDetails} to true in order to receive callbacks with per section + * progress details */ - IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener); + IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener, + boolean getSectionDetails); } diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index 32717f4f87..030d69d16e 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl @@ -24,4 +24,16 @@ package android.os; interface IDumpstateListener { void onProgressUpdated(int progress); void onMaxProgressUpdated(int maxProgress); + + /** + * Called after every section is complete. + * @param name section name + * @param status values from status_t + * {@code OK} section completed successfully + * {@code TIMEOUT} dump timed out + * {@code != OK} error + * @param size size in bytes, may be invalid if status != OK + * @param durationMs duration in ms + */ + void onSectionComplete(@utf8InCpp String name, int status, int size, int durationMs); } diff --git a/cmds/dumpstate/bugreport-format.md b/cmds/dumpstate/bugreport-format.md index b995b808c1..39e70d131b 100644 --- a/cmds/dumpstate/bugreport-format.md +++ b/cmds/dumpstate/bugreport-format.md @@ -56,8 +56,37 @@ files upon the end user’s request: - `description.txt`: whose value is a multi-line, detailed description of the problem. ## Android O versions -On _Android O (OhMightyAndroidWhatsYourNextReleaseName?)_, the following changes were made: -- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-1`). +On _Android O (Oreo)_, the following changes were made: +- The ANR traces are added to the `FS` folder, typically under `FS/data/anr` (version `2.0-dev-split-anr`). + +## Version 2.0 (Android P) +On _Android P_, the following changes were made: +- Framework services are dumped by priority. Supported priorities can be specified + when registering the service. If a service does not specify its priority, its + assumed to be NORMAL. + Supported priorities: + - CRITICAL - services that must dump first, and fast (under 100ms). Ex: cpuinfo. + - HIGH - services that also must dump first, but can take longer (under 250ms) + to dump. Ex: meminfo. + - NORMAL - services that have no rush to dump and can take a long time (under 10s). + + Format changes: + - Two additional dumpsys sections are generated. The two new sections can be + identified by their HEADER `DUMPSYS CRITICAL` and `DUMPSYS HIGH`. + - Services in the new sections will have a new header containing the + priority. + `DUMP OF SERVICE CRITICAL <servicename>` and + `DUMP OF SERVICE HIGH <servicename>`. + For example, cpuinfo will now move to `DUMPSYS CRITICAL` and will have a + header `DUMP OF SERVICE CRITICAL CPUINFO`. + +- Bug report will contain proto dumps from all supporting services. Support can be + specified when registering framework services. + Format changes: + - All protos will be generated into separate files per service, per priority. The files + will be stored in `proto/<servicename>(_CRITICAL|_HIGH|).proto` + +- ANR trace feature has been pushed to version `3.0-dev-split-anr` ## Intermediate versions During development, the versions will be suffixed with _-devX_ or diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index d94c649b11..2b6241566c 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -25,41 +25,68 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/poll.h> #include <sys/prctl.h> #include <sys/resource.h> #include <sys/stat.h> #include <sys/time.h> #include <sys/wait.h> #include <unistd.h> + +#include <chrono> +#include <functional> +#include <future> #include <memory> #include <regex> #include <set> #include <string> +#include <utility> #include <vector> #include <android-base/file.h> #include <android-base/properties.h> +#include <android-base/scopeguard.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> #include <android/hardware/dumpstate/1.0/IDumpstateDevice.h> +#include <android/hidl/manager/1.0/IServiceManager.h> #include <cutils/native_handle.h> #include <cutils/properties.h> +#include <dumpsys.h> +#include <hidl/ServiceManagement.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> - +#include <serviceutils/PriorityDumper.h> +#include <utils/StrongPointer.h> #include "DumpstateInternal.h" +#include "DumpstateSectionReporter.h" #include "DumpstateService.h" #include "dumpstate.h" using ::android::hardware::dumpstate::V1_0::IDumpstateDevice; +using ::std::literals::chrono_literals::operator""ms; +using ::std::literals::chrono_literals::operator""s; // TODO: remove once moved to namespace +using android::defaultServiceManager; +using android::Dumpsys; +using android::INVALID_OPERATION; +using android::IServiceManager; +using android::OK; +using android::sp; +using android::status_t; +using android::String16; +using android::String8; +using android::TIMED_OUT; +using android::UNKNOWN_ERROR; +using android::Vector; using android::os::dumpstate::CommandOptions; using android::os::dumpstate::DumpFileToFd; -using android::os::dumpstate::PropertiesHelper; +using android::os::dumpstate::DumpstateSectionReporter; using android::os::dumpstate::GetPidByName; +using android::os::dumpstate::PropertiesHelper; /* read before root is shed */ static char cmdline_buf[16384] = "(unknown)"; @@ -82,6 +109,7 @@ void add_mountinfo(); #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" #define WLUTIL "/vendor/xbin/wlutil" +#define WMTRACE_DATA_DIR "/data/misc/wmtrace" // TODO(narayan): Since this information has to be kept in sync // with tombstoned, we should just put it in a common header. @@ -100,8 +128,8 @@ static int RunCommand(const std::string& title, const std::vector<std::string>& } static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs, const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS, - long dumpsysTimeout = 0) { - return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout); + long dumpsysTimeoutMs = 0) { + return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeoutMs); } static int DumpFile(const std::string& title, const std::string& path) { return ds.DumpFile(title, path); @@ -112,14 +140,14 @@ static const std::string ZIP_ROOT_DIR = "FS"; // Must be hardcoded because dumpstate HAL implementation need SELinux access to it static const std::string kDumpstateBoardPath = "/bugreports/"; +static const std::string kProtoPath = "proto/"; +static const std::string kProtoExt = ".proto"; static const std::string kDumpstateBoardFiles[] = { "dumpstate_board.txt", "dumpstate_board.bin" }; static const int NUM_OF_DUMPS = arraysize(kDumpstateBoardFiles); -static const std::string kLsHalDebugPath = "/bugreports/dumpstate_lshal.txt"; - static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options"; static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id"; static constexpr char PROPERTY_VERSION[] = "dumpstate.version"; @@ -214,7 +242,7 @@ static bool AddDumps(const std::vector<DumpData>::const_iterator start, } if (ds.IsZipping() && add_to_zip) { - if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) { + if (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd, /* timeout = */ 0ms) != OK) { MYLOGE("Unable to add %s to zip file, addZipEntryFromFd failed\n", name.c_str()); } } else { @@ -644,12 +672,18 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f return 0; } -/* timeout in ms */ -static unsigned long logcat_timeout(const char *name) { - log_id_t id = android_name_to_log_id(name); - unsigned long property_size = __android_logger_get_buffer_size(id); - /* Engineering margin is ten-fold our guess */ - return 10 * (property_size + worst_write_perf) / worst_write_perf; +static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000; + +/* timeout in ms to read a list of buffers */ +static unsigned long logcat_timeout(const std::vector<std::string>& buffers) { + unsigned long timeout_ms = 0; + for (const auto& buffer : buffers) { + log_id_t id = android_name_to_log_id(buffer.c_str()); + unsigned long property_size = __android_logger_get_buffer_size(id); + /* Engineering margin is ten-fold our guess */ + timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf; + } + return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } void Dumpstate::PrintHeader() const { @@ -678,7 +712,9 @@ void Dumpstate::PrintHeader() const { printf("Kernel: "); DumpFileToFd(STDOUT_FILENO, "", "/proc/version"); printf("Command line: %s\n", strtok(cmdline_buf, "\n")); - ds.RunCommand("UPTIME", {"uptime"}, CommandOptions::DEFAULT); + printf("Uptime: "); + RunCommandToFd(STDOUT_FILENO, "", {"uptime", "-p"}, + CommandOptions::WithTimeout(1).Always().Build()); printf("Bugreport format version: %s\n", version_.c_str()); printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_, PropertiesHelper::IsDryRun(), args_.c_str(), extra_options_.c_str()); @@ -693,11 +729,12 @@ static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = { ".shb", ".sys", ".vb", ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh" }; -bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { +status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, + std::chrono::milliseconds timeout = 0ms) { if (!IsZipping()) { MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n", entry_name.c_str()); - return false; + return INVALID_OPERATION; } std::string valid_name = entry_name; @@ -719,32 +756,55 @@ bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) { if (err != 0) { MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(), ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } + auto start = std::chrono::steady_clock::now(); + auto end = start + timeout; + struct pollfd pfd = {fd, POLLIN}; std::vector<uint8_t> buffer(65536); while (1) { + if (timeout.count() > 0) { + // lambda to recalculate the timeout. + auto time_left_ms = [end]() { + auto now = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); + return std::max(diff.count(), 0LL); + }; + + int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); + if (rc < 0) { + MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(), + strerror(errno)); + return -errno; + } else if (rc == 0) { + MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms", + entry_name.c_str(), strerror(errno), timeout.count()); + return TIMED_OUT; + } + } + ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size())); if (bytes_read == 0) { break; } else if (bytes_read == -1) { MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno)); - return false; + return -errno; } err = zip_writer_->WriteBytes(buffer.data(), bytes_read); if (err) { MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } } err = zip_writer_->FinishEntry(); if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); - return false; + return UNKNOWN_ERROR; } - return true; + return OK; } bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) { @@ -755,12 +815,12 @@ bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& en return false; } - return AddZipEntryFromFd(entry_name, fd.get()); + return (AddZipEntryFromFd(entry_name, fd.get()) == OK); } /* adds a file to the existing zipped bugreport */ static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) { - return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1; + return (ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) == OK) ? 0 : 1; } void Dumpstate::AddDir(const std::string& dir, bool recursive) { @@ -816,41 +876,43 @@ static void DoKmsg() { } } +static void DoKernelLogcat() { + unsigned long timeout_ms = logcat_timeout({"kernel"}); + RunCommand( + "KERNEL LOG", + {"logcat", "-b", "kernel", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); +} + static void DoLogcat() { - unsigned long timeout; + unsigned long timeout_ms; // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags"); // calculate timeout - timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash"); - if (timeout < 20000) { - timeout = 20000; - } + timeout_ms = logcat_timeout({"main", "system", "crash"}); RunCommand("SYSTEM LOG", - {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", - "-d", "*:v"}, - CommandOptions::WithTimeout(timeout / 1000).Build()); - timeout = logcat_timeout("events"); - if (timeout < 20000) { - timeout = 20000; - } - RunCommand("EVENT LOG", - {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", - "-d", "*:v"}, - CommandOptions::WithTimeout(timeout / 1000).Build()); - timeout = logcat_timeout("radio"); - if (timeout < 20000) { - timeout = 20000; - } - RunCommand("RADIO LOG", - {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", - "-d", "*:v"}, - CommandOptions::WithTimeout(timeout / 1000).Build()); + {"logcat", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + timeout_ms = logcat_timeout({"events"}); + RunCommand( + "EVENT LOG", + {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + timeout_ms = logcat_timeout({"stats"}); + RunCommand( + "STATS LOG", + {"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); + timeout_ms = logcat_timeout({"radio"}); + RunCommand( + "RADIO LOG", + {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"}, + CommandOptions::WithTimeoutInMs(timeout_ms).Build()); RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"}); /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */ - RunCommand("LAST LOGCAT", - {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-v", "uid", - "-d", "*:v"}); + RunCommand("LAST LOGCAT", {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", + "-v", "uid", "-d", "*:v"}); } static void DumpIpTablesAsRoot() { @@ -968,11 +1030,191 @@ static void DumpIpAddrAndRules() { RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } +static void RunDumpsysTextByPriority(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + auto start = std::chrono::steady_clock::now(); + sp<android::IServiceManager> sm = defaultServiceManager(); + Dumpsys dumpsys(sm.get()); + Vector<String16> args; + Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); + Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false); + for (const String16& service : services) { + std::string path(title); + path.append(" - ").append(String8(service).c_str()); + DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); + size_t bytes_written = 0; + status_t status = dumpsys.startDumpThread(service, args); + if (status == OK) { + dumpsys.writeDumpHeader(STDOUT_FILENO, service, priority); + std::chrono::duration<double> elapsed_seconds; + status = dumpsys.writeDump(STDOUT_FILENO, service, service_timeout, + /* as_proto = */ false, elapsed_seconds, bytes_written); + section_reporter.setSize(bytes_written); + dumpsys.writeDumpFooter(STDOUT_FILENO, service, elapsed_seconds); + bool dump_complete = (status == OK); + dumpsys.stopDumpThread(dump_complete); + } + section_reporter.setStatus(status); + + auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start); + if (elapsed_duration > timeout) { + MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), + elapsed_duration.count()); + break; + } + } +} + +static void RunDumpsysText(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + DurationReporter duration_reporter(title); + dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); + fsync(STDOUT_FILENO); + RunDumpsysTextByPriority(title, priority, timeout, service_timeout); +} + +/* Dump all services registered with Normal or Default priority. */ +static void RunDumpsysTextNormalPriority(const std::string& title, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + DurationReporter duration_reporter(title); + dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); + fsync(STDOUT_FILENO); + RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout, + service_timeout); + RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, + service_timeout); +} + +static void RunDumpsysProto(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { + if (!ds.IsZipping()) { + MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); + return; + } + sp<android::IServiceManager> sm = defaultServiceManager(); + Dumpsys dumpsys(sm.get()); + Vector<String16> args; + Dumpsys::setServiceArgs(args, /* asProto = */ true, priority); + DurationReporter duration_reporter(title); + + auto start = std::chrono::steady_clock::now(); + Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true); + for (const String16& service : services) { + std::string path(kProtoPath); + path.append(String8(service).c_str()); + if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { + path.append("_CRITICAL"); + } else if (priority == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) { + path.append("_HIGH"); + } + path.append(kProtoExt); + DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); + status_t status = dumpsys.startDumpThread(service, args); + if (status == OK) { + status = ds.AddZipEntryFromFd(path, dumpsys.getDumpFd(), service_timeout); + bool dumpTerminated = (status == OK); + dumpsys.stopDumpThread(dumpTerminated); + } + ZipWriter::FileEntry file_entry; + ds.zip_writer_->GetLastEntry(&file_entry); + section_reporter.setSize(file_entry.compressed_size); + section_reporter.setStatus(status); + + auto elapsed_duration = std::chrono::duration_cast<std::chrono::milliseconds>( + std::chrono::steady_clock::now() - start); + if (elapsed_duration > timeout) { + MYLOGE("*** command '%s' timed out after %llums\n", title.c_str(), + elapsed_duration.count()); + break; + } + } +} + +// Runs dumpsys on services that must dump first and and will take less than 100ms to dump. +static void RunDumpsysCritical() { + RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); + RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); +} + +// Runs dumpsys on services that must dump first but can take up to 250ms to dump. +static void RunDumpsysHigh() { + // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both + // high priority. Reduce timeout once they are able to dump in a shorter time or + // moved to a parallel task. + RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 90s, /* service_timeout= */ 30s); + RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 5s, /* service_timeout= */ 1s); +} + +// Runs dumpsys on services that must dump but can take up to 10s to dump. +static void RunDumpsysNormal() { + RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s); + RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); +} + +static void DumpHals() { + using android::hidl::manager::V1_0::IServiceManager; + using android::hardware::defaultServiceManager; + + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == nullptr) { + MYLOGE("Could not retrieve hwservicemanager to dump hals.\n"); + return; + } + + auto ret = sm->list([&](const auto& interfaces) { + for (const std::string& interface : interfaces) { + std::string cleanName = interface; + std::replace_if(cleanName.begin(), + cleanName.end(), + [](char c) { + return !isalnum(c) && + std::string("@-_:.").find(c) == std::string::npos; + }, '_'); + const std::string path = kDumpstateBoardPath + "lshal_debug_" + cleanName; + + { + auto fd = android::base::unique_fd( + TEMP_FAILURE_RETRY(open(path.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (fd < 0) { + MYLOGE("Could not open %s to dump additional hal information.\n", path.c_str()); + continue; + } + RunCommandToFd(fd, + "", + {"lshal", "debug", "-E", interface}, + CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); + + bool empty = 0 == lseek(fd, 0, SEEK_END); + if (!empty) { + ds.AddZipEntry("lshal-debug/" + cleanName + ".txt", path); + } + } + + unlink(path.c_str()); + } + }); + + if (!ret.isOk()) { + MYLOGE("Could not list hals from hwservicemanager.\n"); + } +} + static void dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); - /* TODO: Remove duplicate uptime call when tools use it from header */ RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd"); @@ -993,22 +1235,14 @@ static void dumpstate() { DumpFile("KERNEL SYNC", "/d/sync"); RunCommand("PROCESSES AND THREADS", - {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"}); + {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT); if (ds.IsZipping()) { - RunCommand( - "HARDWARE HALS", - {"lshal", std::string("--debug=") + kLsHalDebugPath}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - - ds.AddZipEntry("lshal-debug.txt", kLsHalDebugPath); - - unlink(kLsHalDebugPath.c_str()); + RunCommand("HARDWARE HALS", {"lshal"}, CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); + DumpHals(); } else { - RunCommand( - "HARDWARE HALS", {"lshal", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + RunCommand("HARDWARE HALS", {"lshal", "--debug"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); } RunCommand("PRINTENV", {"printenv"}); @@ -1020,7 +1254,12 @@ static void dumpstate() { RunCommand("LSMOD", {"lsmod"}); } - do_dmesg(); + if (__android_logger_property_get_bool( + "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { + DoKernelLogcat(); + } else { + do_dmesg(); + } RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); @@ -1059,15 +1298,11 @@ static void dumpstate() { RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); - RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, - CommandOptions::WithTimeout(10).Build()); + RunDumpsysHigh(); RunCommand("SYSTEM PROPERTIES", {"getprop"}); - RunCommand("VOLD DUMP", {"vdc", "dump"}); - RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"}); - - RunCommand("STORAGED TASKIOINFO", {"storaged", "-u"}, CommandOptions::WithTimeout(10).Build()); + RunCommand("STORAGED IO INFO", {"storaged", "-u", "-p"}); RunCommand("FILESYSTEMS & FREE SPACE", {"df"}); @@ -1080,6 +1315,11 @@ static void dumpstate() { DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + /* Add window and surface trace files. */ + if (!PropertiesHelper::IsUserBuild()) { + ds.AddDir(WMTRACE_DATA_DIR, false); + } + ds.DumpstateBoard(); /* Migrate the ril_dumpstate to a device specific dumpstate? */ @@ -1100,8 +1340,7 @@ static void dumpstate() { printf("== Android Framework Services\n"); printf("========================================================\n"); - RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(), - 10); + RunDumpsysNormal(); printf("========================================================\n"); printf("== Checkins\n"); @@ -1118,19 +1357,40 @@ static void dumpstate() { printf("== Running Application Activities\n"); printf("========================================================\n"); - RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}); + // The following dumpsys internally collects output from running apps, so it can take a long + // time. So let's extend the timeout. + + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); + + RunDumpsys("APP ACTIVITIES", {"activity", "-v", "all"}, DUMPSYS_COMPONENTS_OPTIONS); + + printf("========================================================\n"); + printf("== Running Application Services (platform)\n"); + printf("========================================================\n"); + + RunDumpsys("APP SERVICES PLATFORM", {"activity", "service", "all-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); - printf("== Running Application Services\n"); + printf("== Running Application Services (non-platform)\n"); printf("========================================================\n"); - RunDumpsys("APP SERVICES", {"activity", "service", "all"}); + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); - printf("== Running Application Providers\n"); + printf("== Running Application Providers (platform)\n"); printf("========================================================\n"); - RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"}); + RunDumpsys("APP PROVIDERS PLATFORM", {"activity", "provider", "all-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); + + printf("========================================================\n"); + printf("== Running Application Providers (non-platform)\n"); + printf("========================================================\n"); + + RunDumpsys("APP PROVIDERS NON-PLATFORM", {"activity", "provider", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); printf("========================================================\n"); printf("== Dropbox crashes\n"); @@ -1147,10 +1407,8 @@ static void dumpstate() { printf("========================================================\n"); } -// This method collects dumpsys for telephony debugging only -static void DumpstateTelephonyOnly() { - DurationReporter duration_reporter("DUMPSTATE"); - +// This method collects common dumpsys for telephony and wifi +static void DumpstateRadioCommon() { DumpIpTablesAsRoot(); if (!DropRootUser()) { @@ -1166,6 +1424,14 @@ static void DumpstateTelephonyOnly() { RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"}, CommandOptions::WithTimeout(10).Build()); +} + +// This method collects dumpsys for telephony debugging only +static void DumpstateTelephonyOnly() { + DurationReporter duration_reporter("DUMPSTATE"); + const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build(); + + DumpstateRadioCommon(); RunCommand("SYSTEM PROPERTIES", {"getprop"}); @@ -1173,8 +1439,14 @@ static void DumpstateTelephonyOnly() { printf("== Android Framework Services\n"); printf("========================================================\n"); - RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), 10); - RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), 10); + RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); printf("========================================================\n"); printf("== Running Application Services\n"); @@ -1183,6 +1455,33 @@ static void DumpstateTelephonyOnly() { RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"}); printf("========================================================\n"); + printf("== Running Application Services (non-platform)\n"); + printf("========================================================\n"); + + RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"}, + DUMPSYS_COMPONENTS_OPTIONS); + + printf("========================================================\n"); + printf("== dumpstate: done (id %d)\n", ds.id_); + printf("========================================================\n"); +} + +// This method collects dumpsys for wifi debugging only +static void DumpstateWifiOnly() { + DurationReporter duration_reporter("DUMPSTATE"); + + DumpstateRadioCommon(); + + printf("========================================================\n"); + printf("== Android Framework Services\n"); + printf("========================================================\n"); + + RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), + SEC_TO_MSEC(10)); + + printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } @@ -1193,77 +1492,112 @@ void Dumpstate::DumpstateBoard() { printf("== Board\n"); printf("========================================================\n"); - ::android::sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService()); - if (dumpstate_device == nullptr) { - MYLOGE("No IDumpstateDevice implementation\n"); - return; - } - if (!IsZipping()) { MYLOGD("Not dumping board info because it's not a zipped bugreport\n"); return; } - std::string path[NUM_OF_DUMPS]; - android::base::unique_fd fd[NUM_OF_DUMPS]; - int numFds = 0; - + std::vector<std::string> paths; + std::vector<android::base::ScopeGuard<std::function<void()>>> remover; for (int i = 0; i < NUM_OF_DUMPS; i++) { - path[i] = kDumpstateBoardPath + kDumpstateBoardFiles[i]; - MYLOGI("Calling IDumpstateDevice implementation using path %s\n", path[i].c_str()); - - fd[i] = android::base::unique_fd( - TEMP_FAILURE_RETRY(open(path[i].c_str(), - O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, - S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); - if (fd[i] < 0) { - MYLOGE("Could not open file %s: %s\n", path[i].c_str(), strerror(errno)); - return; - } else { - numFds++; - } + paths.emplace_back(kDumpstateBoardPath + kDumpstateBoardFiles[i]); + remover.emplace_back(android::base::make_scope_guard(std::bind( + [](std::string path) { + if (remove(path.c_str()) != 0 && errno != ENOENT) { + MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno)); + } + }, + paths[i]))); } - native_handle_t *handle = native_handle_create(numFds, 0); + sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService()); + if (dumpstate_device == nullptr) { + MYLOGE("No IDumpstateDevice implementation\n"); + return; + } + + using ScopedNativeHandle = + std::unique_ptr<native_handle_t, std::function<void(native_handle_t*)>>; + ScopedNativeHandle handle(native_handle_create(static_cast<int>(paths.size()), 0), + [](native_handle_t* handle) { + native_handle_close(handle); + native_handle_delete(handle); + }); if (handle == nullptr) { MYLOGE("Could not create native_handle\n"); return; } - for (int i = 0; i < numFds; i++) { - handle->data[i] = fd[i].release(); - } + for (size_t i = 0; i < paths.size(); i++) { + MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); - // TODO: need a timeout mechanism so dumpstate does not hang on device implementation call. - android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle); - if (!status.isOk()) { - MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); - native_handle_close(handle); - native_handle_delete(handle); - return; + android::base::unique_fd fd(TEMP_FAILURE_RETRY( + open(paths[i].c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH))); + if (fd < 0) { + MYLOGE("Could not open file %s: %s\n", paths[i].c_str(), strerror(errno)); + return; + } + handle.get()->data[i] = fd.release(); + } + + // Given that bugreport is required to diagnose failures, it's better to + // set an arbitrary amount of timeout for IDumpstateDevice than to block the + // rest of bugreport. In the timeout case, we will kill dumpstate board HAL + // and grab whatever dumped + std::packaged_task<bool()> + dumpstate_task([paths, dumpstate_device, &handle]() -> bool { + android::hardware::Return<void> status = dumpstate_device->dumpstateBoard(handle.get()); + if (!status.isOk()) { + MYLOGE("dumpstateBoard failed: %s\n", status.description().c_str()); + return false; + } + return true; + }); + + auto result = dumpstate_task.get_future(); + std::thread(std::move(dumpstate_task)).detach(); + + constexpr size_t timeout_sec = 30; + if (result.wait_for(std::chrono::seconds(timeout_sec)) != std::future_status::ready) { + MYLOGE("dumpstateBoard timed out after %zus, killing dumpstate vendor HAL\n", timeout_sec); + if (!android::base::SetProperty("ctl.interface_restart", + android::base::StringPrintf("%s/default", + IDumpstateDevice::descriptor))) { + MYLOGE("Couldn't restart dumpstate HAL\n"); + } + } + // Wait some time for init to kill dumpstate vendor HAL + constexpr size_t killing_timeout_sec = 10; + if (result.wait_for(std::chrono::seconds(killing_timeout_sec)) != std::future_status::ready) { + MYLOGE("killing dumpstateBoard timed out after %zus, continue and " + "there might be racing in content\n", killing_timeout_sec); } - for (int i = 0; i < numFds; i++) { + auto file_sizes = std::make_unique<ssize_t[]>(paths.size()); + for (size_t i = 0; i < paths.size(); i++) { struct stat s; - if (fstat(handle->data[i], &s) == -1) { - MYLOGE("Failed to fstat %s: %d\n", kDumpstateBoardFiles[i].c_str(), errno); - } else if (s.st_size > 0) { - AddZipEntry(kDumpstateBoardFiles[i], path[i]); - } else { - MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); + if (fstat(handle.get()->data[i], &s) == -1) { + MYLOGE("Failed to fstat %s: %s\n", kDumpstateBoardFiles[i].c_str(), + strerror(errno)); + file_sizes[i] = -1; + continue; } + file_sizes[i] = s.st_size; } - printf("*** See dumpstate-board.txt entry ***\n"); - - native_handle_close(handle); - native_handle_delete(handle); - - for (int i = 0; i < numFds; i++) { - if (remove(path[i].c_str()) != 0) { - MYLOGE("Could not remove(%s): %s\n", path[i].c_str(), strerror(errno)); + for (size_t i = 0; i < paths.size(); i++) { + if (file_sizes[i] == -1) { + continue; } + if (file_sizes[i] == 0) { + MYLOGE("Ignoring empty %s\n", kDumpstateBoardFiles[i].c_str()); + continue; + } + AddZipEntry(kDumpstateBoardFiles[i], paths[i]); } + + printf("*** See dumpstate-board.txt entry ***\n"); } static void ShowUsageAndExit(int exitCode = 1) { @@ -1294,20 +1628,8 @@ static void ExitOnInvalidArgs() { ShowUsageAndExit(); } -static void sig_handler(int) { - _exit(EXIT_FAILURE); -} - static void register_sig_handler() { - struct sigaction sa; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sa.sa_handler = sig_handler; - sigaction(SIGPIPE, &sa, NULL); // broken pipe - sigaction(SIGSEGV, &sa, NULL); // segment fault - sigaction(SIGINT, &sa, NULL); // ctrl-c - sigaction(SIGTERM, &sa, NULL); // killed - sigaction(SIGQUIT, &sa, NULL); // quit + signal(SIGPIPE, SIG_IGN); } bool Dumpstate::FinishZipFile() { @@ -1419,7 +1741,8 @@ static void Vibrate(int duration_ms) { // clang-format on } -int main(int argc, char *argv[]) { +/** Main entry point for dumpstate. */ +int run_main(int argc, char* argv[]) { int do_add_date = 0; int do_zip_file = 0; int do_vibrate = 1; @@ -1432,6 +1755,9 @@ int main(int argc, char *argv[]) { bool show_header_only = false; bool do_start_service = false; bool telephony_only = false; + bool wifi_only = false; + int dup_stdout_fd; + int dup_stderr_fd; /* set as high priority, and protect from OOM killer */ setpriority(PRIO_PROCESS, 0, -20); @@ -1500,8 +1826,12 @@ int main(int argc, char *argv[]) { } else if (ds.extra_options_ == "bugreportwear") { do_start_service = true; ds.update_progress_ = true; + do_zip_file = 1; } else if (ds.extra_options_ == "bugreporttelephony") { telephony_only = true; + } else if (ds.extra_options_ == "bugreportwifi") { + wifi_only = true; + do_zip_file = 1; } else { MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str()); } @@ -1620,6 +1950,8 @@ int main(int argc, char *argv[]) { if (telephony_only) { ds.base_name_ += "-telephony"; + } else if (wifi_only) { + ds.base_name_ += "-wifi"; } if (do_fb) { @@ -1701,11 +2033,13 @@ int main(int argc, char *argv[]) { } if (is_redirecting) { + TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str())); if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", ds.log_path_.c_str(), strerror(errno)); } + TEMP_FAILURE_RETRY(dup_stdout_fd = dup(fileno(stdout))); /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ @@ -1727,6 +2061,8 @@ int main(int argc, char *argv[]) { if (telephony_only) { DumpstateTelephonyOnly(); ds.DumpstateBoard(); + } else if (wifi_only) { + DumpstateWifiOnly(); } else { // Dumps systrace right away, otherwise it will be filled with unnecessary events. // First try to dump anrd trace if the daemon is running. Otherwise, dump @@ -1737,10 +2073,7 @@ int main(int argc, char *argv[]) { // Invoking the following dumpsys calls before dump_traces() to try and // keep the system stats as close to its initial state as possible. - RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"}, - CommandOptions::WithTimeout(90).DropRoot().Build()); - RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"}, - CommandOptions::WithTimeout(10).DropRoot().Build()); + RunDumpsysCritical(); // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed. dump_raft(); @@ -1771,6 +2104,9 @@ int main(int argc, char *argv[]) { RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); + // Run iotop as root to show top 100 IO threads + RunCommand("IOTOP", {"iotop", "-n", "1", "-m", "100"}); + if (!DropRootUser()) { return -1; } @@ -1780,7 +2116,7 @@ int main(int argc, char *argv[]) { /* close output if needed */ if (is_redirecting) { - fclose(stdout); + TEMP_FAILURE_RETRY(dup2(dup_stdout_fd, fileno(stdout))); } /* rename or zip the (now complete) .tmp file to its final location */ @@ -1913,7 +2249,7 @@ int main(int argc, char *argv[]) { MYLOGI("done (id %d)\n", ds.id_); if (is_redirecting) { - fclose(stderr); + TEMP_FAILURE_RETRY(dup2(dup_stderr_fd, fileno(stderr))); } if (use_control_socket && ds.control_socket_fd_ != -1) { diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 69b0a5e014..b220013f17 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -146,13 +146,13 @@ class Progress { * * See bugreport-format.md for more info. */ -static std::string VERSION_CURRENT = "1.0"; +static std::string VERSION_CURRENT = "2.0"; /* * Temporary version that adds a anr-traces.txt entry. Once tools support it, the current version - * will be bumped to 2.0-dev-1. + * will be bumped to 3.0. */ -static std::string VERSION_SPLIT_ANR = "2.0-dev-1"; +static std::string VERSION_SPLIT_ANR = "3.0-dev-split-anr"; /* * "Alias" for the current version. @@ -205,19 +205,19 @@ class Dumpstate { /* * Runs `dumpsys` with the given arguments, automatically setting its timeout - * (`-t` argument) + * (`-T` argument) * according to the command options. * * |title| description of the command printed on `stdout` (or empty to skip * description). * |dumpsys_args| `dumpsys` arguments (except `-t`). * |options| optional argument defining the command's behavior. - * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -t` (otherwise it uses the + * |dumpsys_timeout| when > 0, defines the value passed to `dumpsys -T` (otherwise it uses the * timeout from `options`) */ void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, const android::os::dumpstate::CommandOptions& options = DEFAULT_DUMPSYS, - long dumpsys_timeout = 0); + long dumpsys_timeout_ms = 0); /* * Prints the contents of a file. @@ -235,8 +235,14 @@ class Dumpstate { /* * Adds a new entry to the existing zip file. + * + * |entry_name| destination path of the new entry. + * |fd| file descriptor to read from. + * |timeout| timeout to terminate the read if not completed. Set + * value of 0s (default) to disable timeout. */ - bool AddZipEntryFromFd(const std::string& entry_name, int fd); + android::status_t AddZipEntryFromFd(const std::string& entry_name, int fd, + std::chrono::milliseconds timeout); /* * Adds a text entry entry to the existing zip file. @@ -281,6 +287,9 @@ class Dumpstate { /* Gets the path of a bugreport file with the given suffix. */ std::string GetPath(const std::string& suffix) const; + /* Returns true if the current version supports priority dump feature. */ + bool CurrentVersionSupportsPriorityDumps() const; + // TODO: initialize fields on constructor // dumpstate id - unique after each device reboot. @@ -347,9 +356,10 @@ class Dumpstate { // Pointer to the zip structure. std::unique_ptr<ZipWriter> zip_writer_; - // Binder object listing to progress. + // Binder object listening to progress. android::sp<android::os::IDumpstateListener> listener_; std::string listener_name_; + bool report_section_; // Notification title and description std::string notification_title; @@ -445,6 +455,9 @@ void dump_emmc_ecsd(const char *ext_csd_path); /** Gets command-line arguments. */ void format_args(int argc, const char *argv[], std::string *args); +/** Main entry point for dumpstate. */ +int run_main(int argc, char* argv[]); + #ifdef __cplusplus } #endif diff --git a/cmds/dumpstate/main.cpp b/cmds/dumpstate/main.cpp new file mode 100644 index 0000000000..78aad1137b --- /dev/null +++ b/cmds/dumpstate/main.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 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. + */ + +#include "dumpstate.h" + +int main(int argc, char* argv[]) { + return run_main(argc, argv); +} diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp new file mode 100644 index 0000000000..61a5ef5b7d --- /dev/null +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2018 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. + */ + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <fcntl.h> +#include <libgen.h> + +#include <android-base/file.h> +#include <cutils/properties.h> +#include <ziparchive/zip_archive.h> + +#include "dumpstate.h" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + +namespace android { +namespace os { +namespace dumpstate { + +using ::testing::Test; +using ::std::literals::chrono_literals::operator""s; + +struct SectionInfo { + std::string name; + status_t status; + int32_t size_bytes; + int32_t duration_ms; +}; + +/** + * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the + * section details generated by dumpstate are added to a vector to be used by Tests later. + */ +class DumpstateListener : public IDumpstateListener { + public: + int outFd_, max_progress_; + std::shared_ptr<std::vector<SectionInfo>> sections_; + DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections) + : outFd_(fd), max_progress_(5000), sections_(sections) { + } + binder::Status onProgressUpdated(int32_t progress) override { + dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_); + return binder::Status::ok(); + } + binder::Status onMaxProgressUpdated(int32_t max_progress) override { + max_progress_ = max_progress; + return binder::Status::ok(); + } + binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, + int32_t duration_ms) override { + sections_->push_back({name, status, size_bytes, duration_ms}); + return binder::Status::ok(); + } + IBinder* onAsBinder() override { + return nullptr; + } +}; + +/** + * Generates bug report and provide access to the bug report file and other info for other tests. + * Since bug report generation is slow, the bugreport is only generated once. + */ +class ZippedBugreportGenerationTest : public Test { + public: + static std::shared_ptr<std::vector<SectionInfo>> sections; + static Dumpstate& ds; + static std::chrono::milliseconds duration; + static void SetUpTestCase() { + property_set("dumpstate.options", "bugreportplus"); + // clang-format off + char* argv[] = { + (char*)"dumpstate", + (char*)"-d", + (char*)"-z", + (char*)"-B", + (char*)"-o", + (char*)dirname(android::base::GetExecutablePath().c_str()) + }; + // clang-format on + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)), sections)); + ds.listener_ = listener; + ds.listener_name_ = "Smokey"; + ds.report_section_ = true; + auto start = std::chrono::steady_clock::now(); + run_main(ARRAY_SIZE(argv), argv); + auto end = std::chrono::steady_clock::now(); + duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); + } + + static const char* getZipFilePath() { + return ds.GetPath(".zip").c_str(); + } +}; +std::shared_ptr<std::vector<SectionInfo>> ZippedBugreportGenerationTest::sections = + std::make_shared<std::vector<SectionInfo>>(); +Dumpstate& ZippedBugreportGenerationTest::ds = Dumpstate::GetInstance(); +std::chrono::milliseconds ZippedBugreportGenerationTest::duration = 0s; + +TEST_F(ZippedBugreportGenerationTest, IsGeneratedWithoutErrors) { + EXPECT_EQ(access(getZipFilePath(), F_OK), 0); +} + +TEST_F(ZippedBugreportGenerationTest, Is3MBto30MBinSize) { + struct stat st; + EXPECT_EQ(stat(getZipFilePath(), &st), 0); + EXPECT_GE(st.st_size, 3000000 /* 3MB */); + EXPECT_LE(st.st_size, 30000000 /* 30MB */); +} + +TEST_F(ZippedBugreportGenerationTest, TakesBetween30And150Seconds) { + EXPECT_GE(duration, 30s) << "Expected completion in more than 30s. Actual time " + << duration.count() << " s."; + EXPECT_LE(duration, 150s) << "Expected completion in less than 150s. Actual time " + << duration.count() << " s."; +} + +/** + * Run tests on contents of zipped bug report. + */ +class ZippedBugReportContentsTest : public Test { + public: + ZipArchiveHandle handle; + void SetUp() { + ASSERT_EQ(OpenArchive(ZippedBugreportGenerationTest::getZipFilePath(), &handle), 0); + } + void TearDown() { + CloseArchive(handle); + } + + void FileExists(const char* filename, uint32_t minsize, uint32_t maxsize) { + ZipEntry entry; + EXPECT_EQ(FindEntry(handle, ZipString(filename), &entry), 0); + EXPECT_GT(entry.uncompressed_length, minsize); + EXPECT_LT(entry.uncompressed_length, maxsize); + } +}; + +TEST_F(ZippedBugReportContentsTest, ContainsMainEntry) { + ZipEntry mainEntryLoc; + // contains main entry name file + EXPECT_EQ(FindEntry(handle, ZipString("main_entry.txt"), &mainEntryLoc), 0); + + char* buf = new char[mainEntryLoc.uncompressed_length]; + ExtractToMemory(handle, &mainEntryLoc, (uint8_t*)buf, mainEntryLoc.uncompressed_length); + delete[] buf; + + // contains main entry file + FileExists(buf, 1000000U, 50000000U); +} + +TEST_F(ZippedBugReportContentsTest, ContainsVersion) { + ZipEntry entry; + // contains main entry name file + EXPECT_EQ(FindEntry(handle, ZipString("version.txt"), &entry), 0); + + char* buf = new char[entry.uncompressed_length + 1]; + ExtractToMemory(handle, &entry, (uint8_t*)buf, entry.uncompressed_length); + buf[entry.uncompressed_length] = 0; + EXPECT_STREQ(buf, ZippedBugreportGenerationTest::ds.version_.c_str()); + delete[] buf; +} + +TEST_F(ZippedBugReportContentsTest, ContainsBoardSpecificFiles) { + FileExists("dumpstate_board.bin", 1000000U, 80000000U); + FileExists("dumpstate_board.txt", 100000U, 1000000U); +} + +// Spot check on some files pulled from the file system +TEST_F(ZippedBugReportContentsTest, ContainsSomeFileSystemFiles) { + // FS/proc/*/mountinfo size > 0 + FileExists("FS/proc/1/mountinfo", 0U, 100000U); + + // FS/data/misc/profiles/cur/0/*/primary.prof size > 0 + FileExists("FS/data/misc/profiles/cur/0/com.android.phone/primary.prof", 0U, 100000U); +} + +/** + * Runs tests on section data generated by dumpstate and captured by DumpstateListener. + */ +class BugreportSectionTest : public Test { + public: + int numMatches(const std::string& substring) { + int matches = 0; + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + if (section.name.find(substring) != std::string::npos) { + matches++; + } + } + return matches; + } + void SectionExists(const std::string& sectionName, int minsize) { + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + if (sectionName == section.name) { + EXPECT_GE(section.size_bytes, minsize); + return; + } + } + FAIL() << sectionName << " not found."; + } +}; + +// Test all sections are generated without timeouts or errors +TEST_F(BugreportSectionTest, GeneratedWithoutErrors) { + for (auto const& section : *ZippedBugreportGenerationTest::sections) { + EXPECT_EQ(section.status, 0) << section.name << " failed with status " << section.status; + } +} + +TEST_F(BugreportSectionTest, Atleast3CriticalDumpsysSectionsGenerated) { + int numSections = numMatches("DUMPSYS CRITICAL"); + EXPECT_GE(numSections, 3); +} + +TEST_F(BugreportSectionTest, Atleast2HighDumpsysSectionsGenerated) { + int numSections = numMatches("DUMPSYS HIGH"); + EXPECT_GE(numSections, 2); +} + +TEST_F(BugreportSectionTest, Atleast50NormalDumpsysSectionsGenerated) { + int allSections = numMatches("DUMPSYS"); + int criticalSections = numMatches("DUMPSYS CRITICAL"); + int highSections = numMatches("DUMPSYS HIGH"); + int normalSections = allSections - criticalSections - highSections; + + EXPECT_GE(normalSections, 50) << "Total sections less than 50 (Critical:" << criticalSections + << "High:" << highSections << "Normal:" << normalSections << ")"; +} + +TEST_F(BugreportSectionTest, Atleast1ProtoDumpsysSectionGenerated) { + int numSections = numMatches("proto/"); + EXPECT_GE(numSections, 1); +} + +// Test if some critical sections are being generated. +TEST_F(BugreportSectionTest, CriticalSurfaceFlingerSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - SurfaceFlinger", /* bytes= */ 10000); +} + +TEST_F(BugreportSectionTest, ActivitySectionsGenerated) { + SectionExists("DUMPSYS CRITICAL - activity", /* bytes= */ 5000); + SectionExists("DUMPSYS - activity", /* bytes= */ 10000); +} + +TEST_F(BugreportSectionTest, CpuinfoSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - cpuinfo", /* bytes= */ 1000); +} + +TEST_F(BugreportSectionTest, WindowSectionGenerated) { + SectionExists("DUMPSYS CRITICAL - window", /* bytes= */ 20000); +} + +TEST_F(BugreportSectionTest, ConnectivitySectionsGenerated) { + SectionExists("DUMPSYS HIGH - connectivity", /* bytes= */ 5000); + SectionExists("DUMPSYS - connectivity", /* bytes= */ 5000); +} + +TEST_F(BugreportSectionTest, MeminfoSectionGenerated) { + SectionExists("DUMPSYS HIGH - meminfo", /* bytes= */ 100000); +} + +TEST_F(BugreportSectionTest, BatteryStatsSectionGenerated) { + SectionExists("DUMPSYS - batterystats", /* bytes= */ 1000); +} + +TEST_F(BugreportSectionTest, WifiSectionGenerated) { + SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); +} + +} // namespace dumpstate +} // namespace os +} // namespace android diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index 92b0c0d8bc..838b385b1b 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -58,6 +58,8 @@ class DumpstateListenerMock : public IDumpstateListener { public: MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress)); MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress)); + MOCK_METHOD4(onSectionComplete, binder::Status(const ::std::string& name, int32_t status, + int32_t size, int32_t durationMs)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); @@ -601,27 +603,43 @@ class DumpstateServiceTest : public DumpstateBaseTest { TEST_F(DumpstateServiceTest, SetListenerNoName) { sp<DumpstateListenerMock> listener(new DumpstateListenerMock()); sp<IDumpstateToken> token; - EXPECT_TRUE(dss.setListener("", listener, &token).isOk()); + EXPECT_TRUE(dss.setListener("", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerNoPointer) { sp<IDumpstateToken> token; - EXPECT_TRUE(dss.setListener("whatever", nullptr, &token).isOk()); + EXPECT_TRUE( + dss.setListener("whatever", nullptr, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); } TEST_F(DumpstateServiceTest, SetListenerTwice) { sp<DumpstateListenerMock> listener(new DumpstateListenerMock()); sp<IDumpstateToken> token; - EXPECT_TRUE(dss.setListener("whatever", listener, &token).isOk()); + EXPECT_TRUE( + dss.setListener("whatever", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, NotNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); + EXPECT_FALSE(Dumpstate::GetInstance().report_section_); token.clear(); - EXPECT_TRUE(dss.setListener("whatsoever", listener, &token).isOk()); + EXPECT_TRUE( + dss.setListener("whatsoever", listener, /* getSectionDetails = */ false, &token).isOk()); ASSERT_THAT(token, IsNull()); EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); + EXPECT_FALSE(Dumpstate::GetInstance().report_section_); +} + +TEST_F(DumpstateServiceTest, SetListenerWithSectionDetails) { + sp<DumpstateListenerMock> listener(new DumpstateListenerMock()); + sp<IDumpstateToken> token; + Dumpstate::GetInstance().listener_ = nullptr; + EXPECT_TRUE( + dss.setListener("whatever", listener, /* getSectionDetails = */ true, &token).isOk()); + ASSERT_THAT(token, NotNull()); + EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever")); + EXPECT_TRUE(Dumpstate::GetInstance().report_section_); } class ProgressTest : public DumpstateBaseTest { @@ -1001,7 +1019,7 @@ TEST_F(DumpstateUtilTest, RunCommandCrashes) { err, StartsWith("stderr\n*** command '" + kSimpleCommand + " --crash' failed: exit code")); } -TEST_F(DumpstateUtilTest, RunCommandTimesout) { +TEST_F(DumpstateUtilTest, RunCommandTimesoutWithSec) { CreateFd("RunCommandTimesout.txt"); EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"}, CommandOptions::WithTimeout(1).Build())); @@ -1011,6 +1029,17 @@ TEST_F(DumpstateUtilTest, RunCommandTimesout) { " --sleep 2' timed out after 1")); } +TEST_F(DumpstateUtilTest, RunCommandTimesoutWithMsec) { + CreateFd("RunCommandTimesout.txt"); + EXPECT_EQ(-1, RunCommand("", {kSimpleCommand, "--sleep", "2"}, + CommandOptions::WithTimeoutInMs(1000).Build())); + EXPECT_THAT(out, StartsWith("stdout line1\n*** command '" + kSimpleCommand + + " --sleep 2' timed out after 1")); + EXPECT_THAT(err, StartsWith("sleeping for 2s\n*** command '" + kSimpleCommand + + " --sleep 2' timed out after 1")); +} + + TEST_F(DumpstateUtilTest, RunCommandIsKilled) { CreateFd("RunCommandIsKilled.txt"); CaptureStderr(); diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 6b808e36ad..9beff989df 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -48,10 +48,10 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> -#include <android/hidl/manager/1.0/IServiceManager.h> #include <cutils/properties.h> #include <cutils/sockets.h> #include <debuggerd/client.h> +#include <dumputils/dump_utils.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -76,36 +76,6 @@ static int RunCommand(const std::string& title, const std::vector<std::string>& return ds.RunCommand(title, full_command, options); } -/* list of native processes to include in the native dumps */ -// This matches the /proc/pid/exe link instead of /proc/pid/cmdline. -static const char* native_processes_to_dump[] = { - "/system/bin/audioserver", - "/system/bin/cameraserver", - "/system/bin/drmserver", - "/system/bin/mediadrmserver", - "/system/bin/mediaextractor", // media.extractor - "/system/bin/mediametrics", // media.metrics - "/system/bin/mediaserver", - "/system/bin/sdcard", - "/system/bin/statsd", - "/system/bin/surfaceflinger", - "/system/bin/vehicle_network_service", - "/vendor/bin/hw/android.hardware.media.omx@1.0-service", // media.codec - NULL, -}; - -/* list of hal interface to dump containing process during native dumps */ -static const char* hal_interfaces_to_dump[] { - "android.hardware.audio@2.0::IDevicesFactory", - "android.hardware.bluetooth@1.0::IBluetoothHci", - "android.hardware.camera.provider@2.4::ICameraProvider", - "android.hardware.graphics.composer@2.1::IComposer", - "android.hardware.media.omx@1.0::IOmx", - "android.hardware.sensors@1.0::ISensors", - "android.hardware.vr@1.0::IVr", - NULL, -}; - // Reasonable value for max stats. static const int STATS_MAX_N_RUNS = 1000; static const long STATS_MAX_AVERAGE = 100000; @@ -217,10 +187,10 @@ int32_t Progress::Get() const { return progress_; } -bool Progress::Inc(int32_t delta) { +bool Progress::Inc(int32_t delta_sec) { bool changed = false; - if (delta >= 0) { - progress_ += delta; + if (delta_sec >= 0) { + progress_ += delta_sec; if (progress_ > max_) { int32_t old_max = max_; max_ = floor((float)progress_ * growth_factor_); @@ -721,9 +691,9 @@ int Dumpstate::RunCommand(const std::string& title, const std::vector<std::strin } void Dumpstate::RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args, - const CommandOptions& options, long dumpsysTimeout) { - long timeout = dumpsysTimeout > 0 ? dumpsysTimeout : options.Timeout(); - std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-t", std::to_string(timeout)}; + const CommandOptions& options, long dumpsysTimeoutMs) { + long timeout_ms = dumpsysTimeoutMs > 0 ? dumpsysTimeoutMs : options.TimeoutInMs(); + std::vector<std::string> dumpsys = {"/system/bin/dumpsys", "-T", std::to_string(timeout_ms)}; dumpsys.insert(dumpsys.end(), dumpsys_args.begin(), dumpsys_args.end()); RunCommand(title, dumpsys, options); } @@ -809,71 +779,11 @@ void redirect_to_existing_file(FILE *redirect, char *path) { _redirect_to_file(redirect, path, O_APPEND); } -static bool should_dump_hal_interface(const char* interface) { - for (const char** i = hal_interfaces_to_dump; *i; i++) { - if (!strcmp(*i, interface)) { - return true; - } - } - return false; -} - -static bool should_dump_native_traces(const char* path) { - for (const char** p = native_processes_to_dump; *p; p++) { - if (!strcmp(*p, path)) { - return true; - } - } - return false; -} - -std::set<int> get_interesting_hal_pids() { - using android::hidl::manager::V1_0::IServiceManager; - using android::sp; - using android::hardware::Return; - - sp<IServiceManager> manager = IServiceManager::getService(); - std::set<int> pids; - - Return<void> ret = manager->debugDump([&](auto& hals) { - for (const auto &info : hals) { - if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) { - continue; - } - - if (!should_dump_hal_interface(info.interfaceName.c_str())) { - continue; - } - - pids.insert(info.pid); - } - }); - - if (!ret.isOk()) { - MYLOGE("Could not get list of HAL PIDs: %s\n", ret.description().c_str()); - } - - return pids; // whether it was okay or not -} - -static bool IsZygote(int pid) { - static const std::string kZygotePrefix = "zygote"; - - std::string cmdline; - if (!android::base::ReadFileToString(android::base::StringPrintf("/proc/%d/cmdline", pid), - &cmdline)) { - return true; - } - - return (cmdline.find(kZygotePrefix) == 0); -} - // Dump Dalvik and native stack traces, return the trace file location (nullptr if none). const char* dump_traces() { DurationReporter duration_reporter("DUMP TRACES"); const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX"; - const size_t buf_size = temp_file_pattern.length() + 1; std::unique_ptr<char[]> file_name_buf(new char[buf_size]); memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size); @@ -996,14 +906,14 @@ void dump_route_tables() { } // TODO: make this function thread safe if sections are generated in parallel. -void Dumpstate::UpdateProgress(int32_t delta) { +void Dumpstate::UpdateProgress(int32_t delta_sec) { if (progress_ == nullptr) { MYLOGE("UpdateProgress: progress_ not set\n"); return; } // Always update progess so stats can be tuned... - bool max_changed = progress_->Inc(delta); + bool max_changed = progress_->Inc(delta_sec); // ...but only notifiy listeners when necessary. if (!update_progress_) return; diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index 34769644d5..f68b862f24 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp @@ -17,6 +17,10 @@ cc_defaults { "libbinder", ], + static_libs: [ + "libserviceutils", + ], + clang: true, } diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index 32277499a6..5412d4df7b 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -25,6 +25,7 @@ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/TextOutput.h> +#include <serviceutils/PriorityDumper.h> #include <utils/Log.h> #include <utils/Vector.h> @@ -42,9 +43,11 @@ #include "dumpsys.h" using namespace android; -using android::base::StringPrintf; -using android::base::unique_fd; -using android::base::WriteFully; +using ::android::base::StringAppendF; +using ::android::base::StringPrintf; +using ::android::base::unique_fd; +using ::android::base::WriteFully; +using ::android::base::WriteStringToFd; static int sort_func(const String16* lhs, const String16* rhs) { @@ -53,13 +56,19 @@ static int sort_func(const String16* lhs, const String16* rhs) static void usage() { fprintf(stderr, - "usage: dumpsys\n" + "usage: dumpsys\n" " To dump all services.\n" "or:\n" - " dumpsys [-t TIMEOUT] [--help | -l | --skip SERVICES | SERVICE [ARGS]]\n" + " dumpsys [-t TIMEOUT] [--priority LEVEL] [--help | -l | --skip SERVICES | " + "SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\n" - " -t TIMEOUT: TIMEOUT to use in seconds instead of default 10 seconds\n" + " -t TIMEOUT_SEC: TIMEOUT to use in seconds instead of default 10 seconds\n" + " -T TIMEOUT_MS: TIMEOUT to use in milliseconds instead of default 10 seconds\n" + " --proto: filter services that support dumping data in proto format. Dumps" + " will be in proto format.\n" + " --priority LEVEL: filter services based on specified priority\n" + " LEVEL must be one of CRITICAL | HIGH | NORMAL\n" " --skip SERVICES: dumps all services but SERVICES (comma-separated list)\n" " SERVICE [ARGS]: dumps only service SERVICE, optionally passing ARGS to it\n"); } @@ -73,18 +82,51 @@ static bool IsSkipped(const Vector<String16>& skipped, const String16& service) return false; } +static bool ConvertPriorityTypeToBitmask(const String16& type, int& bitmask) { + if (type == PriorityDumper::PRIORITY_ARG_CRITICAL) { + bitmask = IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL; + return true; + } + if (type == PriorityDumper::PRIORITY_ARG_HIGH) { + bitmask = IServiceManager::DUMP_FLAG_PRIORITY_HIGH; + return true; + } + if (type == PriorityDumper::PRIORITY_ARG_NORMAL) { + bitmask = IServiceManager::DUMP_FLAG_PRIORITY_NORMAL; + return true; + } + return false; +} + +String16 ConvertBitmaskToPriorityType(int bitmask) { + if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { + return String16(PriorityDumper::PRIORITY_ARG_CRITICAL); + } + if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) { + return String16(PriorityDumper::PRIORITY_ARG_HIGH); + } + if (bitmask == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) { + return String16(PriorityDumper::PRIORITY_ARG_NORMAL); + } + return String16(""); +} + int Dumpsys::main(int argc, char* const argv[]) { Vector<String16> services; Vector<String16> args; + String16 priorityType; Vector<String16> skippedServices; + Vector<String16> protoServices; bool showListOnly = false; bool skipServices = false; - int timeoutArg = 10; - static struct option longOptions[] = { - {"skip", no_argument, 0, 0 }, - {"help", no_argument, 0, 0 }, - { 0, 0, 0, 0 } - }; + bool asProto = false; + int timeoutArgMs = 10000; + int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; + static struct option longOptions[] = {{"priority", required_argument, 0, 0}, + {"proto", no_argument, 0, 0}, + {"skip", no_argument, 0, 0}, + {"help", no_argument, 0, 0}, + {0, 0, 0, 0}}; // Must reset optind, otherwise subsequent calls will fail (wouldn't happen on main.cpp, but // happens on test cases). @@ -93,7 +135,7 @@ int Dumpsys::main(int argc, char* const argv[]) { int c; int optionIndex = 0; - c = getopt_long(argc, argv, "+t:l", longOptions, &optionIndex); + c = getopt_long(argc, argv, "+t:T:l", longOptions, &optionIndex); if (c == -1) { break; @@ -103,18 +145,39 @@ int Dumpsys::main(int argc, char* const argv[]) { case 0: if (!strcmp(longOptions[optionIndex].name, "skip")) { skipServices = true; + } else if (!strcmp(longOptions[optionIndex].name, "proto")) { + asProto = true; } else if (!strcmp(longOptions[optionIndex].name, "help")) { usage(); return 0; + } else if (!strcmp(longOptions[optionIndex].name, "priority")) { + priorityType = String16(String8(optarg)); + if (!ConvertPriorityTypeToBitmask(priorityType, priorityFlags)) { + fprintf(stderr, "\n"); + usage(); + return -1; + } } break; case 't': { - char *endptr; - timeoutArg = strtol(optarg, &endptr, 10); - if (*endptr != '\0' || timeoutArg <= 0) { - fprintf(stderr, "Error: invalid timeout number: '%s'\n", optarg); + char* endptr; + timeoutArgMs = strtol(optarg, &endptr, 10); + timeoutArgMs = timeoutArgMs * 1000; + if (*endptr != '\0' || timeoutArgMs <= 0) { + fprintf(stderr, "Error: invalid timeout(seconds) number: '%s'\n", optarg); + return -1; + } + } + break; + + case 'T': + { + char* endptr; + timeoutArgMs = strtol(optarg, &endptr, 10); + if (*endptr != '\0' || timeoutArgMs <= 0) { + fprintf(stderr, "Error: invalid timeout(milliseconds) number: '%s'\n", optarg); return -1; } } @@ -150,14 +213,11 @@ int Dumpsys::main(int argc, char* const argv[]) { } if (services.empty() || showListOnly) { - // gets all services - services = sm_->listServices(); - services.sort(sort_func); - args.add(String16("-a")); + services = listServices(priorityFlags, asProto); + setServiceArgs(args, asProto, priorityFlags); } const size_t N = services.size(); - if (N > 1) { // first print a list of the current services aout << "Currently running services:" << endl; @@ -177,125 +237,214 @@ int Dumpsys::main(int argc, char* const argv[]) { } for (size_t i = 0; i < N; i++) { - const String16& service_name = std::move(services[i]); - if (IsSkipped(skippedServices, service_name)) continue; - - sp<IBinder> service = sm_->checkService(service_name); - if (service != nullptr) { - int sfd[2]; + const String16& serviceName = services[i]; + if (IsSkipped(skippedServices, serviceName)) continue; - if (pipe(sfd) != 0) { - aerr << "Failed to create pipe to dump service info for " << service_name - << ": " << strerror(errno) << endl; - continue; + if (startDumpThread(serviceName, args) == OK) { + bool addSeparator = (N > 1); + if (addSeparator) { + writeDumpHeader(STDOUT_FILENO, serviceName, priorityFlags); } + std::chrono::duration<double> elapsedDuration; + size_t bytesWritten = 0; + status_t status = + writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(timeoutArgMs), + asProto, elapsedDuration, bytesWritten); - unique_fd local_end(sfd[0]); - unique_fd remote_end(sfd[1]); - sfd[0] = sfd[1] = -1; + if (status == TIMED_OUT) { + aout << endl + << "*** SERVICE '" << serviceName << "' DUMP TIMEOUT (" << timeoutArgMs + << "ms) EXPIRED ***" << endl + << endl; + } - if (N > 1) { - aout << "------------------------------------------------------------" - "-------------------" << endl; - aout << "DUMP OF SERVICE " << service_name << ":" << endl; + if (addSeparator) { + writeDumpFooter(STDOUT_FILENO, serviceName, elapsedDuration); } + bool dumpComplete = (status == OK); + stopDumpThread(dumpComplete); + } + } - // dump blocks until completion, so spawn a thread.. - std::thread dump_thread([=, remote_end { std::move(remote_end) }]() mutable { - int err = service->dump(remote_end.get(), args); + return 0; +} - // It'd be nice to be able to close the remote end of the socketpair before the dump - // call returns, to terminate our reads if the other end closes their copy of the - // file descriptor, but then hangs for some reason. There doesn't seem to be a good - // way to do this, though. - remote_end.reset(); +Vector<String16> Dumpsys::listServices(int priorityFilterFlags, bool filterByProto) const { + Vector<String16> services = sm_->listServices(priorityFilterFlags); + services.sort(sort_func); + if (filterByProto) { + Vector<String16> protoServices = sm_->listServices(IServiceManager::DUMP_FLAG_PROTO); + protoServices.sort(sort_func); + Vector<String16> intersection; + std::set_intersection(services.begin(), services.end(), protoServices.begin(), + protoServices.end(), std::back_inserter(intersection)); + services = std::move(intersection); + } + return services; +} - if (err != 0) { - aerr << "Error dumping service info: (" << strerror(err) << ") " << service_name - << endl; - } - }); - - auto timeout = std::chrono::seconds(timeoutArg); - auto start = std::chrono::steady_clock::now(); - auto end = start + timeout; - - struct pollfd pfd = { - .fd = local_end.get(), - .events = POLLIN - }; - - bool timed_out = false; - bool error = false; - while (true) { - // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout. - auto time_left_ms = [end]() { - auto now = std::chrono::steady_clock::now(); - auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); - return std::max(diff.count(), 0ll); - }; - - int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); - if (rc < 0) { - aerr << "Error in poll while dumping service " << service_name << " : " - << strerror(errno) << endl; - error = true; - break; - } else if (rc == 0) { - timed_out = true; - break; - } +void Dumpsys::setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags) { + // Add proto flag if dumping service as proto. + if (asProto) { + args.insertAt(String16(PriorityDumper::PROTO_ARG), 0); + } - char buf[4096]; - rc = TEMP_FAILURE_RETRY(read(local_end.get(), buf, sizeof(buf))); - if (rc < 0) { - aerr << "Failed to read while dumping service " << service_name << ": " - << strerror(errno) << endl; - error = true; - break; - } else if (rc == 0) { - // EOF. - break; - } + // Add -a (dump all) flag if dumping all services, dumping normal services or + // services not explicitly registered to a priority bucket (default services). + if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL) || + (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL) || + (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT)) { + args.insertAt(String16("-a"), 0); + } - if (!WriteFully(STDOUT_FILENO, buf, rc)) { - aerr << "Failed to write while dumping service " << service_name << ": " - << strerror(errno) << endl; - error = true; - break; - } - } + // Add priority flags when dumping services registered to a specific priority bucket. + if ((priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) || + (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_HIGH) || + (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL)) { + String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags); + args.insertAt(String16(PriorityDumper::PRIORITY_ARG), 0); + args.insertAt(priorityType, 1); + } +} - if (timed_out) { - aout << endl - << "*** SERVICE '" << service_name << "' DUMP TIMEOUT (" << timeoutArg - << "s) EXPIRED ***" << endl - << endl; - } +status_t Dumpsys::startDumpThread(const String16& serviceName, const Vector<String16>& args) { + sp<IBinder> service = sm_->checkService(serviceName); + if (service == nullptr) { + aerr << "Can't find service: " << serviceName << endl; + return NAME_NOT_FOUND; + } - if (timed_out || error) { - dump_thread.detach(); - } else { - dump_thread.join(); - } + int sfd[2]; + if (pipe(sfd) != 0) { + aerr << "Failed to create pipe to dump service info for " << serviceName << ": " + << strerror(errno) << endl; + return -errno; + } - if (N > 1) { - std::chrono::duration<double> elapsed_seconds = - std::chrono::steady_clock::now() - start; - aout << StringPrintf("--------- %.3fs ", elapsed_seconds.count()).c_str() - << "was the duration of dumpsys " << service_name; - - using std::chrono::system_clock; - const auto finish = system_clock::to_time_t(system_clock::now()); - std::tm finish_tm; - localtime_r(&finish, &finish_tm); - aout << ", ending at: " << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S") - << endl; - } - } else { - aerr << "Can't find service: " << service_name << endl; + redirectFd_ = unique_fd(sfd[0]); + unique_fd remote_end(sfd[1]); + sfd[0] = sfd[1] = -1; + + // dump blocks until completion, so spawn a thread.. + activeThread_ = std::thread([=, remote_end{std::move(remote_end)}]() mutable { + int err = service->dump(remote_end.get(), args); + + // It'd be nice to be able to close the remote end of the socketpair before the dump + // call returns, to terminate our reads if the other end closes their copy of the + // file descriptor, but then hangs for some reason. There doesn't seem to be a good + // way to do this, though. + remote_end.reset(); + + if (err != 0) { + aerr << "Error dumping service info: (" << strerror(err) << ") " + << serviceName << endl; } + }); + return OK; +} + +void Dumpsys::stopDumpThread(bool dumpComplete) { + if (dumpComplete) { + activeThread_.join(); + } else { + activeThread_.detach(); } + /* close read end of the dump output redirection pipe */ + redirectFd_.reset(); +} - return 0; +void Dumpsys::writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const { + std::string msg( + "----------------------------------------" + "---------------------------------------\n"); + if (priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_ALL || + priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_NORMAL || + priorityFlags == IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT) { + StringAppendF(&msg, "DUMP OF SERVICE %s:\n", String8(serviceName).c_str()); + } else { + String16 priorityType = ConvertBitmaskToPriorityType(priorityFlags); + StringAppendF(&msg, "DUMP OF SERVICE %s %s:\n", String8(priorityType).c_str(), + String8(serviceName).c_str()); + } + WriteStringToFd(msg, fd); +} + +status_t Dumpsys::writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout, + bool asProto, std::chrono::duration<double>& elapsedDuration, + size_t& bytesWritten) const { + status_t status = OK; + size_t totalBytes = 0; + auto start = std::chrono::steady_clock::now(); + auto end = start + timeout; + + int serviceDumpFd = redirectFd_.get(); + if (serviceDumpFd == -1) { + return INVALID_OPERATION; + } + + struct pollfd pfd = {.fd = serviceDumpFd, .events = POLLIN}; + + while (true) { + // Wrap this in a lambda so that TEMP_FAILURE_RETRY recalculates the timeout. + auto time_left_ms = [end]() { + auto now = std::chrono::steady_clock::now(); + auto diff = std::chrono::duration_cast<std::chrono::milliseconds>(end - now); + return std::max(diff.count(), 0ll); + }; + + int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); + if (rc < 0) { + aerr << "Error in poll while dumping service " << serviceName << " : " + << strerror(errno) << endl; + status = -errno; + break; + } else if (rc == 0) { + status = TIMED_OUT; + break; + } + + char buf[4096]; + rc = TEMP_FAILURE_RETRY(read(redirectFd_.get(), buf, sizeof(buf))); + if (rc < 0) { + aerr << "Failed to read while dumping service " << serviceName << ": " + << strerror(errno) << endl; + status = -errno; + break; + } else if (rc == 0) { + // EOF. + break; + } + + if (!WriteFully(fd, buf, rc)) { + aerr << "Failed to write while dumping service " << serviceName << ": " + << strerror(errno) << endl; + status = -errno; + break; + } + totalBytes += rc; + } + + if ((status == TIMED_OUT) && (!asProto)) { + std::string msg = StringPrintf("\n*** SERVICE '%s' DUMP TIMEOUT (%llums) EXPIRED ***\n\n", + String8(serviceName).string(), timeout.count()); + WriteStringToFd(msg, fd); + } + + elapsedDuration = std::chrono::steady_clock::now() - start; + bytesWritten = totalBytes; + return status; +} + +void Dumpsys::writeDumpFooter(int fd, const String16& serviceName, + const std::chrono::duration<double>& elapsedDuration) const { + using std::chrono::system_clock; + const auto finish = system_clock::to_time_t(system_clock::now()); + std::tm finish_tm; + localtime_r(&finish, &finish_tm); + std::stringstream oss; + oss << std::put_time(&finish_tm, "%Y-%m-%d %H:%M:%S"); + std::string msg = + StringPrintf("--------- %.3fs was the duration of dumpsys %s, ending at: %s\n", + elapsedDuration.count(), String8(serviceName).string(), oss.str().c_str()); + WriteStringToFd(msg, fd); } diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 2534dded13..84f3b0236e 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -17,6 +17,9 @@ #ifndef FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ #define FRAMEWORK_NATIVE_CMD_DUMPSYS_H_ +#include <thread> + +#include <android-base/unique_fd.h> #include <binder/IServiceManager.h> namespace android { @@ -25,10 +28,97 @@ class Dumpsys { public: Dumpsys(android::IServiceManager* sm) : sm_(sm) { } + /** + * Main entry point into dumpsys. + */ int main(int argc, char* const argv[]); + /** + * Returns a list of services. + * @param priorityFlags filter services by specified priorities + * @param supportsProto filter services that support proto dumps + * @return list of services + */ + Vector<String16> listServices(int priorityFlags, bool supportsProto) const; + + /** + * Modifies @{code args} to add additional arguments to indicate if the service + * must dump as proto or dump to a certian priority bucket. + * @param args initial list of arguments to pass to service dump method. + * @param asProto dump service as proto by passing an additional --proto arg + * @param priorityFlags indicates priority of dump by passing additional priority args + * to the service + */ + static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); + + /** + * Starts a thread to connect to a service and get its dump output. The thread redirects + * the output to a pipe. Thread must be stopped by a subsequent callto {@code + * stopDumpThread}. + * @param serviceName + * @param args list of arguments to pass to service dump method. + * @return {@code OK} thread is started successfully. + * {@code NAME_NOT_FOUND} service could not be found. + * {@code != OK} error + */ + status_t startDumpThread(const String16& serviceName, const Vector<String16>& args); + + /** + * Writes a section header to a file descriptor. + * @param fd file descriptor to write data + * @param serviceName + * @param priorityFlags dump priority specified + */ + void writeDumpHeader(int fd, const String16& serviceName, int priorityFlags) const; + + /** + * Redirects service dump to a file descriptor. This requires + * {@code startDumpThread} to be called successfully otherwise the function will + * return {@code INVALID_OPERATION}. + * @param fd file descriptor to write data + * @param serviceName + * @param timeout timeout to terminate the dump if not completed + * @param asProto used to supresses additional output to the fd such as timeout + * error messages + * @param elapsedDuration returns elapsed time in seconds + * @param bytesWritten returns number of bytes written + * @return {@code OK} if successful + * {@code TIMED_OUT} dump timed out + * {@code INVALID_OPERATION} invalid state + * {@code != OK} error + */ + status_t writeDump(int fd, const String16& serviceName, std::chrono::milliseconds timeout, + bool asProto, std::chrono::duration<double>& elapsedDuration, + size_t& bytesWritten) const; + + /** + * Writes a section footer to a file descriptor with duration info. + * @param fd file descriptor to write data + * @param serviceName + * @param elapsedDuration duration of dump + */ + void writeDumpFooter(int fd, const String16& serviceName, + const std::chrono::duration<double>& elapsedDuration) const; + + /** + * Terminates dump thread. + * @param dumpComplete If {@code true}, indicates the dump was successfully completed and + * tries to join the thread. Otherwise thread is detached. + */ + void stopDumpThread(bool dumpComplete); + + /** + * Returns file descriptor of the pipe used to dump service data. This assumes + * {@code startDumpThread} was called successfully. + */ + int getDumpFd() const { + return redirectFd_.get(); + } + private: android::IServiceManager* sm_; + std::thread activeThread_; + mutable android::base::unique_fd redirectFd_; }; } diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp index 39fcb80631..e182b9d287 100644 --- a/cmds/dumpsys/tests/Android.bp +++ b/cmds/dumpsys/tests/Android.bp @@ -15,6 +15,7 @@ cc_test { static_libs: [ "libdumpsys", "libgmock", + "libserviceutils", ], clang: true, diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 16fefe64ba..502935259a 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -22,6 +22,7 @@ #include <gtest/gtest.h> #include <android-base/file.h> +#include <serviceutils/PriorityDumper.h> #include <utils/String16.h> #include <utils/String8.h> #include <utils/Vector.h> @@ -50,8 +51,8 @@ class ServiceManagerMock : public IServiceManager { public: MOCK_CONST_METHOD1(getService, sp<IBinder>(const String16&)); MOCK_CONST_METHOD1(checkService, sp<IBinder>(const String16&)); - MOCK_METHOD3(addService, status_t(const String16&, const sp<IBinder>&, bool)); - MOCK_METHOD0(listServices, Vector<String16>()); + MOCK_METHOD4(addService, status_t(const String16&, const sp<IBinder>&, bool, int)); + MOCK_METHOD1(listServices, Vector<String16>(int)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); @@ -131,7 +132,16 @@ class DumpsysTest : public Test { for (auto& service : services) { services16.add(String16(service.c_str())); } - EXPECT_CALL(sm_, listServices()).WillRepeatedly(Return(services16)); + EXPECT_CALL(sm_, listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL)) + .WillRepeatedly(Return(services16)); + } + + void ExpectListServicesWithPriority(std::vector<std::string> services, int dumpFlags) { + Vector<String16> services16; + for (auto& service : services) { + services16.add(String16(service.c_str())); + } + EXPECT_CALL(sm_, listServices(dumpFlags)).WillRepeatedly(Return(services16)); } sp<BinderMock> ExpectCheckService(const char* name, bool running = true) { @@ -178,8 +188,27 @@ class DumpsysTest : public Test { EXPECT_THAT(status, Eq(0)); } + void CallSingleService(const String16& serviceName, Vector<String16>& args, int priorityFlags, + bool supportsProto, std::chrono::duration<double>& elapsedDuration, + size_t& bytesWritten) { + CaptureStdout(); + CaptureStderr(); + dump_.setServiceArgs(args, supportsProto, priorityFlags); + status_t status = dump_.startDumpThread(serviceName, args); + EXPECT_THAT(status, Eq(0)); + status = dump_.writeDump(STDOUT_FILENO, serviceName, std::chrono::milliseconds(500), false, + elapsedDuration, bytesWritten); + EXPECT_THAT(status, Eq(0)); + dump_.stopDumpThread(/* dumpCompleted = */ true); + stdout_ = GetCapturedStdout(); + stderr_ = GetCapturedStderr(); + } + void AssertRunningServices(const std::vector<std::string>& services) { - std::string expected("Currently running services:\n"); + std::string expected; + if (services.size() > 1) { + expected.append("Currently running services:\n"); + } for (const std::string& service : services) { expected.append(" ").append(service).append("\n"); } @@ -196,6 +225,15 @@ class DumpsysTest : public Test { void AssertDumped(const std::string& service, const std::string& dump) { EXPECT_THAT(stdout_, HasSubstr("DUMP OF SERVICE " + service + ":\n" + dump)); + EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: ")); + } + + void AssertDumpedWithPriority(const std::string& service, const std::string& dump, + const char16_t* priorityType) { + std::string priority = String8(priorityType).c_str(); + EXPECT_THAT(stdout_, + HasSubstr("DUMP OF SERVICE " + priority + " " + service + ":\n" + dump)); + EXPECT_THAT(stdout_, HasSubstr("was the duration of dumpsys " + service + ", ending at: ")); } void AssertNotDumped(const std::string& dump) { @@ -236,6 +274,39 @@ TEST_F(DumpsysTest, ListRunningServices) { AssertNotDumped({"Valet"}); } +// Tests 'dumpsys -l --priority HIGH' +TEST_F(DumpsysTest, ListAllServicesWithPriority) { + ExpectListServicesWithPriority({"Locksmith", "Valet"}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"-l", "--priority", "HIGH"}); + + AssertRunningServices({"Locksmith", "Valet"}); +} + +// Tests 'dumpsys -l --priority HIGH' with and empty list +TEST_F(DumpsysTest, ListEmptyServicesWithPriority) { + ExpectListServicesWithPriority({}, IServiceManager::DUMP_FLAG_PRIORITY_HIGH); + + CallMain({"-l", "--priority", "HIGH"}); + + AssertRunningServices({}); +} + +// Tests 'dumpsys -l --proto' +TEST_F(DumpsysTest, ListAllServicesWithProto) { + ExpectListServicesWithPriority({"Locksmith", "Valet", "Car"}, + IServiceManager::DUMP_FLAG_PRIORITY_ALL); + ExpectListServicesWithPriority({"Valet", "Car"}, IServiceManager::DUMP_FLAG_PROTO); + ExpectCheckService("Car"); + ExpectCheckService("Valet"); + + CallMain({"-l", "--proto"}); + + AssertRunningServices({"Car", "Valet"}); +} + // Tests 'dumpsys service_name' on a service is running TEST_F(DumpsysTest, DumpRunningService) { ExpectDump("Valet", "Here's your car"); @@ -246,12 +317,25 @@ TEST_F(DumpsysTest, DumpRunningService) { } // Tests 'dumpsys -t 1 service_name' on a service that times out after 2s -TEST_F(DumpsysTest, DumpRunningServiceTimeout) { +TEST_F(DumpsysTest, DumpRunningServiceTimeoutInSec) { sp<BinderMock> binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); CallMain({"-t", "1", "Valet"}); - AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1s) EXPIRED"); + AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (1000ms) EXPIRED"); + AssertNotDumped("Here's your car"); + + // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp + Mock::AllowLeak(binder_mock.get()); +} + +// Tests 'dumpsys -T 500 service_name' on a service that times out after 2s +TEST_F(DumpsysTest, DumpRunningServiceTimeoutInMs) { + sp<BinderMock> binder_mock = ExpectDumpAndHang("Valet", 2, "Here's your car"); + + CallMain({"-T", "500", "Valet"}); + + AssertOutputContains("SERVICE 'Valet' DUMP TIMEOUT (500ms) EXPIRED"); AssertNotDumped("Here's your car"); // TODO(b/65056227): BinderMock is not destructed because thread is detached on dumpsys.cpp @@ -267,6 +351,65 @@ TEST_F(DumpsysTest, DumpWithArgsRunningService) { AssertOutput("I DO!"); } +// Tests dumpsys passes the -a flag when called on all services +TEST_F(DumpsysTest, PassAllFlagsToServices) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + ExpectDumpWithArgs("Locksmith", {"-a"}, "dumped1"); + ExpectDumpWithArgs("Valet", {"-a"}, "dumped2"); + + CallMain({"-T", "500"}); + + AssertDumped("Locksmith", "dumped1"); + AssertDumped("Valet", "dumped2"); +} + +// Tests dumpsys passes the -a flag when called on NORMAL priority services +TEST_F(DumpsysTest, PassAllFlagsToNormalServices) { + ExpectListServicesWithPriority({"Locksmith", "Valet"}, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + ExpectDumpWithArgs("Locksmith", {"-a", "--dump-priority", "NORMAL"}, "dump1"); + ExpectDumpWithArgs("Valet", {"-a", "--dump-priority", "NORMAL"}, "dump2"); + + CallMain({"--priority", "NORMAL"}); + + AssertDumped("Locksmith", "dump1"); + AssertDumped("Valet", "dump2"); +} + +// Tests dumpsys passes only priority flags when called on CRITICAL priority services +TEST_F(DumpsysTest, PassPriorityFlagsToCriticalServices) { + ExpectListServicesWithPriority({"Locksmith", "Valet"}, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + ExpectDumpWithArgs("Locksmith", {"--dump-priority", "CRITICAL"}, "dump1"); + ExpectDumpWithArgs("Valet", {"--dump-priority", "CRITICAL"}, "dump2"); + + CallMain({"--priority", "CRITICAL"}); + + AssertDumpedWithPriority("Locksmith", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); + AssertDumpedWithPriority("Valet", "dump2", PriorityDumper::PRIORITY_ARG_CRITICAL); +} + +// Tests dumpsys passes only priority flags when called on HIGH priority services +TEST_F(DumpsysTest, PassPriorityFlagsToHighServices) { + ExpectListServicesWithPriority({"Locksmith", "Valet"}, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + ExpectDumpWithArgs("Locksmith", {"--dump-priority", "HIGH"}, "dump1"); + ExpectDumpWithArgs("Valet", {"--dump-priority", "HIGH"}, "dump2"); + + CallMain({"--priority", "HIGH"}); + + AssertDumpedWithPriority("Locksmith", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); + AssertDumpedWithPriority("Valet", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); +} + // Tests 'dumpsys' with no arguments TEST_F(DumpsysTest, DumpMultipleServices) { ExpectListServices({"running1", "stopped2", "running3"}); @@ -300,3 +443,124 @@ TEST_F(DumpsysTest, DumpWithSkip) { AssertNotDumped("dump3"); AssertNotDumped("dump5"); } + +// Tests 'dumpsys --skip skipped3 skipped5 --priority CRITICAL', which should skip these services +TEST_F(DumpsysTest, DumpWithSkipAndPriority) { + ExpectListServicesWithPriority({"running1", "stopped2", "skipped3", "running4", "skipped5"}, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); + ExpectDump("running1", "dump1"); + ExpectCheckService("stopped2", false); + ExpectDump("skipped3", "dump3"); + ExpectDump("running4", "dump4"); + ExpectDump("skipped5", "dump5"); + + CallMain({"--priority", "CRITICAL", "--skip", "skipped3", "skipped5"}); + + AssertRunningServices({"running1", "running4", "skipped3 (skipped)", "skipped5 (skipped)"}); + AssertDumpedWithPriority("running1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); + AssertDumpedWithPriority("running4", "dump4", PriorityDumper::PRIORITY_ARG_CRITICAL); + AssertStopped("stopped2"); + AssertNotDumped("dump3"); + AssertNotDumped("dump5"); +} + +// Tests 'dumpsys --priority CRITICAL' +TEST_F(DumpsysTest, DumpWithPriorityCritical) { + ExpectListServicesWithPriority({"runningcritical1", "runningcritical2"}, + IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL); + ExpectDump("runningcritical1", "dump1"); + ExpectDump("runningcritical2", "dump2"); + + CallMain({"--priority", "CRITICAL"}); + + AssertRunningServices({"runningcritical1", "runningcritical2"}); + AssertDumpedWithPriority("runningcritical1", "dump1", PriorityDumper::PRIORITY_ARG_CRITICAL); + AssertDumpedWithPriority("runningcritical2", "dump2", PriorityDumper::PRIORITY_ARG_CRITICAL); +} + +// Tests 'dumpsys --priority HIGH' +TEST_F(DumpsysTest, DumpWithPriorityHigh) { + ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"}, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH); + ExpectDump("runninghigh1", "dump1"); + ExpectDump("runninghigh2", "dump2"); + + CallMain({"--priority", "HIGH"}); + + AssertRunningServices({"runninghigh1", "runninghigh2"}); + AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); + AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); +} + +// Tests 'dumpsys --priority NORMAL' +TEST_F(DumpsysTest, DumpWithPriorityNormal) { + ExpectListServicesWithPriority({"runningnormal1", "runningnormal2"}, + IServiceManager::DUMP_FLAG_PRIORITY_NORMAL); + ExpectDump("runningnormal1", "dump1"); + ExpectDump("runningnormal2", "dump2"); + + CallMain({"--priority", "NORMAL"}); + + AssertRunningServices({"runningnormal1", "runningnormal2"}); + AssertDumped("runningnormal1", "dump1"); + AssertDumped("runningnormal2", "dump2"); +} + +// Tests 'dumpsys --proto' +TEST_F(DumpsysTest, DumpWithProto) { + ExpectListServicesWithPriority({"run8", "run1", "run2", "run5"}, + IServiceManager::DUMP_FLAG_PRIORITY_ALL); + ExpectListServicesWithPriority({"run3", "run2", "run4", "run8"}, + IServiceManager::DUMP_FLAG_PROTO); + ExpectDump("run2", "dump1"); + ExpectDump("run8", "dump2"); + + CallMain({"--proto"}); + + AssertRunningServices({"run2", "run8"}); + AssertDumped("run2", "dump1"); + AssertDumped("run8", "dump2"); +} + +// Tests 'dumpsys --priority HIGH --proto' +TEST_F(DumpsysTest, DumpWithPriorityHighAndProto) { + ExpectListServicesWithPriority({"runninghigh1", "runninghigh2"}, + IServiceManager::DUMP_FLAG_PRIORITY_HIGH); + ExpectListServicesWithPriority({"runninghigh1", "runninghigh2", "runninghigh3"}, + IServiceManager::DUMP_FLAG_PROTO); + + ExpectDump("runninghigh1", "dump1"); + ExpectDump("runninghigh2", "dump2"); + + CallMain({"--priority", "HIGH", "--proto"}); + + AssertRunningServices({"runninghigh1", "runninghigh2"}); + AssertDumpedWithPriority("runninghigh1", "dump1", PriorityDumper::PRIORITY_ARG_HIGH); + AssertDumpedWithPriority("runninghigh2", "dump2", PriorityDumper::PRIORITY_ARG_HIGH); +} + +TEST_F(DumpsysTest, GetBytesWritten) { + const char* serviceName = "service2"; + const char* dumpContents = "dump1"; + ExpectDump(serviceName, dumpContents); + + String16 service(serviceName); + Vector<String16> args; + std::chrono::duration<double> elapsedDuration; + size_t bytesWritten; + + CallSingleService(service, args, IServiceManager::DUMP_FLAG_PRIORITY_ALL, + /* as_proto = */ false, elapsedDuration, bytesWritten); + + AssertOutput(dumpContents); + EXPECT_THAT(bytesWritten, Eq(strlen(dumpContents))); +} + +TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) { + std::chrono::duration<double> elapsedDuration; + size_t bytesWritten; + status_t status = + dump_.writeDump(STDOUT_FILENO, String16("service"), std::chrono::milliseconds(500), + /* as_proto = */ false, elapsedDuration, bytesWritten); + EXPECT_THAT(status, Eq(INVALID_OPERATION)); +}
\ No newline at end of file diff --git a/cmds/flatland/GLHelper.cpp b/cmds/flatland/GLHelper.cpp index dfc3e581fd..d5b3372f96 100644 --- a/cmds/flatland/GLHelper.cpp +++ b/cmds/flatland/GLHelper.cpp @@ -269,24 +269,10 @@ bool GLHelper::createWindowSurface(uint32_t w, uint32_t h, return false; } - SurfaceComposerClient::openGlobalTransaction(); - err = sc->setLayer(0x7FFFFFFF); - if (err != NO_ERROR) { - fprintf(stderr, "SurfaceComposer::setLayer error: %#x\n", err); - return false; - } - err = sc->setMatrix(scale, 0.0f, 0.0f, scale); - if (err != NO_ERROR) { - fprintf(stderr, "SurfaceComposer::setMatrix error: %#x\n", err); - return false; - } - - err = sc->show(); - if (err != NO_ERROR) { - fprintf(stderr, "SurfaceComposer::show error: %#x\n", err); - return false; - } - SurfaceComposerClient::closeGlobalTransaction(); + SurfaceComposerClient::Transaction{}.setLayer(sc, 0x7FFFFFFF) + .setMatrix(sc, scale, 0.0f, 0.0f, scale) + .show(sc) + .apply(); sp<ANativeWindow> anw = sc->getSurface(); EGLSurface s = eglCreateWindowSurface(mDisplay, mConfig, anw.get(), NULL); diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 1d8b52c6c9..860a68b273 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -18,17 +18,21 @@ #define ATRACE_TAG ATRACE_TAG_PACKAGE_MANAGER +#include <algorithm> #include <errno.h> -#include <inttypes.h> #include <fstream> #include <fts.h> +#include <functional> +#include <inttypes.h> #include <regex> #include <stdlib.h> #include <string.h> #include <sys/capability.h> #include <sys/file.h> -#include <sys/resource.h> +#include <sys/ioctl.h> +#include <sys/mman.h> #include <sys/quota.h> +#include <sys/resource.h> #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/types.h> @@ -41,6 +45,7 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> #include <android-base/unique_fd.h> +#include <cutils/ashmem.h> #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> @@ -72,6 +77,7 @@ namespace installd { static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; +static constexpr const char* kPropHasReserved = "vold.has_reserved"; static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M @@ -83,6 +89,10 @@ static constexpr const char *kIdMapPath = "/system/bin/idmap"; static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/"; static constexpr const char* IDMAP_SUFFIX = "@idmap"; +// fsverity assumes the page size is always 4096. If not, the feature can not be +// enabled. +static constexpr int kVerityPageSize = 4096; +static constexpr size_t kSha256Size = 32; static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode"; // NOTE: keep in sync with Installer @@ -162,6 +172,35 @@ binder::Status checkArgumentPackageName(const std::string& packageName) { } } +binder::Status checkArgumentPath(const std::string& path) { + if (path.empty()) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Missing path"); + } + if (path[0] != '/') { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Path %s is relative", path.c_str())); + } + if ((path + '/').find("/../") != std::string::npos) { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Path %s is shady", path.c_str())); + } + for (const char& c : path) { + if (c == '\0' || c == '\n') { + return exception(binder::Status::EX_ILLEGAL_ARGUMENT, + StringPrintf("Path %s is malformed", path.c_str())); + } + } + return ok(); +} + +binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) { + if (path) { + return checkArgumentPath(*path); + } else { + return ok(); + } +} + #define ENFORCE_UID(uid) { \ binder::Status status = checkUid((uid)); \ if (!status.isOk()) { \ @@ -184,6 +223,19 @@ binder::Status checkArgumentPackageName(const std::string& packageName) { } \ } +#define CHECK_ARGUMENT_PATH(path) { \ + binder::Status status = checkArgumentPath((path)); \ + if (!status.isOk()) { \ + return status; \ + } \ +} + +#define ASSERT_PAGE_SIZE_4K() { \ + if (getpagesize() != kVerityPageSize) { \ + return error("FSVerity only supports 4K pages"); \ + } \ +} + } // namespace status_t InstalldNativeService::start() { @@ -304,8 +356,11 @@ static int prepare_app_dir(const std::string& path, mode_t target_mode, uid_t ui * Ensure that we have a hard-limit quota to protect against abusive apps; * they should never use more than 90% of blocks or 50% of inodes. */ -static int prepare_app_quota(const std::unique_ptr<std::string>& uuid, const std::string& device, - uid_t uid) { +static int prepare_app_quota(const std::unique_ptr<std::string>& uuid ATTRIBUTE_UNUSED, + const std::string& device, uid_t uid) { + // Skip when reserved blocks are protecting us against abusive apps + if (android::base::GetBoolProperty(kPropHasReserved, false)) return 0; + // Skip when device no quotas present if (device.empty()) return 0; struct dqblk dq; @@ -1122,6 +1177,7 @@ binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::strin binder::Status InstalldNativeService::rmdex(const std::string& codePath, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(codePath); std::lock_guard<std::recursive_mutex> lock(mLock); char dex_path[PKG_PATH_MAX]; @@ -1381,6 +1437,9 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri for (const auto& packageName : packageNames) { CHECK_ARGUMENT_PACKAGE_NAME(packageName); } + for (const auto& codePath : codePaths) { + CHECK_ARGUMENT_PATH(codePath); + } // NOTE: Locking is relaxed on this method, since it's limited to // read-only measurements without mutation. @@ -1826,6 +1885,7 @@ binder::Status InstalldNativeService::dumpProfiles(int32_t uid, const std::strin const std::string& profileName, const std::string& codePath, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_PATH(codePath); std::lock_guard<std::recursive_mutex> lock(mLock); *_aidl_return = dump_profiles(uid, packageName, profileName, codePath); @@ -1893,9 +1953,12 @@ binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t const std::unique_ptr<std::string>& compilationReason) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); + CHECK_ARGUMENT_PATH(apkPath); if (packageName && *packageName != "*") { CHECK_ARGUMENT_PACKAGE_NAME(*packageName); } + CHECK_ARGUMENT_PATH(outputPath); + CHECK_ARGUMENT_PATH(dexMetadataPath); std::lock_guard<std::recursive_mutex> lock(mLock); const char* apk_path = apkPath.c_str(); @@ -1936,33 +1999,13 @@ binder::Status InstalldNativeService::markBootComplete(const std::string& instru return ok(); } -void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid, - struct stat* statbuf) -{ - while (path[basepos] != 0) { - if (path[basepos] == '/') { - path[basepos] = 0; - if (lstat(path, statbuf) < 0) { - ALOGV("Making directory: %s\n", path); - if (mkdir(path, mode) == 0) { - chown(path, uid, gid); - } else { - ALOGW("Unable to make directory %s: %s\n", path, strerror(errno)); - } - } - path[basepos] = '/'; - basepos++; - } - basepos++; - } -} - binder::Status InstalldNativeService::linkNativeLibraryDirectory( const std::unique_ptr<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_PATH(nativeLibPath32); std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -2130,6 +2173,8 @@ static int flatten_path(const char *prefix, const char *suffix, binder::Status InstalldNativeService::idmap(const std::string& targetApkPath, const std::string& overlayApkPath, int32_t uid) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(targetApkPath); + CHECK_ARGUMENT_PATH(overlayApkPath); std::lock_guard<std::recursive_mutex> lock(mLock); const char* target_apk = targetApkPath.c_str(); @@ -2215,6 +2260,10 @@ fail: } binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(overlayApkPath); + std::lock_guard<std::recursive_mutex> lock(mLock); + const char* overlay_apk = overlayApkPath.c_str(); char idmap_path[PATH_MAX]; @@ -2265,6 +2314,7 @@ binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<st binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, const std::string& instructionSet) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(oatDir); std::lock_guard<std::recursive_mutex> lock(mLock); const char* oat_dir = oatDir.c_str(); @@ -2289,6 +2339,7 @@ binder::Status InstalldNativeService::createOatDir(const std::string& oatDir, binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(packageDir); std::lock_guard<std::recursive_mutex> lock(mLock); if (validate_apk_path(packageDir.c_str())) { @@ -2303,6 +2354,8 @@ binder::Status InstalldNativeService::rmPackageDir(const std::string& packageDir binder::Status InstalldNativeService::linkFile(const std::string& relativePath, const std::string& fromBase, const std::string& toBase) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(fromBase); + CHECK_ARGUMENT_PATH(toBase); std::lock_guard<std::recursive_mutex> lock(mLock); const char* relative_path = relativePath.c_str(); @@ -2331,6 +2384,8 @@ binder::Status InstalldNativeService::linkFile(const std::string& relativePath, binder::Status InstalldNativeService::moveAb(const std::string& apkPath, const std::string& instructionSet, const std::string& outputPath) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(apkPath); + CHECK_ARGUMENT_PATH(outputPath); std::lock_guard<std::recursive_mutex> lock(mLock); const char* apk_path = apkPath.c_str(); @@ -2344,6 +2399,8 @@ binder::Status InstalldNativeService::moveAb(const std::string& apkPath, binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(apkPath); + CHECK_ARGUMENT_PATH(outputPath); std::lock_guard<std::recursive_mutex> lock(mLock); const char* apk_path = apkPath.c_str(); @@ -2354,15 +2411,140 @@ binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, return res ? ok() : error(); } -binder::Status InstalldNativeService::installApkVerity(const std::string& /*filePath*/, - const ::android::base::unique_fd& /*verityInput*/) { +// This kernel feature is experimental. +// TODO: remove local definition once upstreamed +#ifndef FS_IOC_ENABLE_VERITY + +#define FS_IOC_ENABLE_VERITY _IO('f', 133) +#define FS_IOC_SET_VERITY_MEASUREMENT _IOW('f', 134, struct fsverity_measurement) + +#define FS_VERITY_ALG_SHA256 1 + +struct fsverity_measurement { + __u16 digest_algorithm; + __u16 digest_size; + __u32 reserved1; + __u64 reserved2[3]; + __u8 digest[]; +}; + +#endif + +binder::Status InstalldNativeService::installApkVerity(const std::string& filePath, + const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) { + ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(filePath); + std::lock_guard<std::recursive_mutex> lock(mLock); + + if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { + return ok(); + } +#ifndef NDEBUG + ASSERT_PAGE_SIZE_4K(); +#endif + // TODO: also check fsverity support in the current file system if compiled with DEBUG. + // TODO: change ashmem to some temporary file to support huge apk. + if (!ashmem_valid(verityInputAshmem.get())) { + return error("FD is not an ashmem"); + } + + // 1. Seek to the next page boundary beyond the end of the file. + ::android::base::unique_fd wfd(open(filePath.c_str(), O_WRONLY)); + if (wfd.get() < 0) { + return error("Failed to open " + filePath); + } + struct stat st; + if (fstat(wfd.get(), &st) < 0) { + return error("Failed to stat " + filePath); + } + // fsverity starts from the block boundary. + off_t padding = kVerityPageSize - st.st_size % kVerityPageSize; + if (padding == kVerityPageSize) { + padding = 0; + } + if (lseek(wfd.get(), st.st_size + padding, SEEK_SET) < 0) { + return error("Failed to lseek " + filePath); + } + + // 2. Write everything in the ashmem to the file. Note that allocated + // ashmem size is multiple of page size, which is different from the + // actual content size. + int shmSize = ashmem_get_size_region(verityInputAshmem.get()); + if (shmSize < 0) { + return error("Failed to get ashmem size: " + std::to_string(shmSize)); + } + if (contentSize < 0) { + return error("Invalid content size: " + std::to_string(contentSize)); + } + if (contentSize > shmSize) { + return error("Content size overflow: " + std::to_string(contentSize) + " > " + + std::to_string(shmSize)); + } + auto data = std::unique_ptr<void, std::function<void (void *)>>( + mmap(NULL, contentSize, PROT_READ, MAP_SHARED, verityInputAshmem.get(), 0), + [contentSize] (void* ptr) { + if (ptr != MAP_FAILED) { + munmap(ptr, contentSize); + } + }); + + if (data.get() == MAP_FAILED) { + return error("Failed to mmap the ashmem"); + } + char* cursor = reinterpret_cast<char*>(data.get()); + int remaining = contentSize; + while (remaining > 0) { + int ret = TEMP_FAILURE_RETRY(write(wfd.get(), cursor, remaining)); + if (ret < 0) { + return error("Failed to write to " + filePath + " (" + std::to_string(remaining) + + + "/" + std::to_string(contentSize) + ")"); + } + cursor += ret; + remaining -= ret; + } + wfd.reset(); + + // 3. Enable fsverity (needs readonly fd. Once it's done, the file becomes immutable. + ::android::base::unique_fd rfd(open(filePath.c_str(), O_RDONLY)); + if (ioctl(rfd.get(), FS_IOC_ENABLE_VERITY, nullptr) < 0) { + return error("Failed to enable fsverity on " + filePath); + } + return ok(); +} + +binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::string& filePath, + const std::vector<uint8_t>& expectedHash) { ENFORCE_UID(AID_SYSTEM); + CHECK_ARGUMENT_PATH(filePath); + std::lock_guard<std::recursive_mutex> lock(mLock); + if (!android::base::GetBoolProperty(kPropApkVerityMode, false)) { return ok(); } - // TODO: Append verity to filePath then issue ioctl to enable - // it and hide the tree. See b/30972906. - return error("not implemented yet"); + // TODO: also check fsverity support in the current file system if compiled with DEBUG. + if (expectedHash.size() != kSha256Size) { + return error("verity hash size should be " + std::to_string(kSha256Size) + " but is " + + std::to_string(expectedHash.size())); + } + + ::android::base::unique_fd fd(open(filePath.c_str(), O_RDONLY)); + if (fd.get() < 0) { + return error("Failed to open " + filePath + ": " + strerror(errno)); + } + + unsigned int buffer_size = sizeof(fsverity_measurement) + kSha256Size; + std::vector<char> buffer(buffer_size, 0); + + fsverity_measurement* config = reinterpret_cast<fsverity_measurement*>(buffer.data()); + config->digest_algorithm = FS_VERITY_ALG_SHA256; + config->digest_size = kSha256Size; + memcpy(config->digest, expectedHash.data(), kSha256Size); + if (ioctl(fd.get(), FS_IOC_SET_VERITY_MEASUREMENT, config) < 0) { + // This includes an expected failure case with no FSVerity setup. It normally happens when + // the apk does not contains the Merkle tree root hash. + return error("Failed to measure fsverity on " + filePath + ": " + strerror(errno)); + } + return ok(); // hashes match } binder::Status InstalldNativeService::reconcileSecondaryDexFile( @@ -2372,8 +2554,9 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); - + CHECK_ARGUMENT_PATH(dexPath); std::lock_guard<std::recursive_mutex> lock(mLock); + bool result = android::installd::reconcile_secondary_dex_file( dexPath, packageName, uid, isas, volumeUuid, storage_flag, _aidl_return); return result ? ok() : error(); @@ -2386,6 +2569,7 @@ binder::Status InstalldNativeService::hashSecondaryDexFile( ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_PATH(dexPath); // mLock is not taken here since we will never modify the file system. // If a file is modified just as we are reading it this may result in an @@ -2431,14 +2615,18 @@ binder::Status InstalldNativeService::invalidateMounts() { mQuotaReverseMounts[target] = source; // ext4 only enables DQUOT_USAGE_ENABLED by default, so we - // need to kick it again to enable DQUOT_LIMITS_ENABLED. - if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 - && errno != EBUSY) { - PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; - } - if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, nullptr) != 0 - && errno != EBUSY) { - PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; + // need to kick it again to enable DQUOT_LIMITS_ENABLED. We + // only need hard limits enabled when we're not being protected + // by reserved blocks. + if (!android::base::GetBoolProperty(kPropHasReserved, false)) { + if (quotactl(QCMD(Q_QUOTAON, USRQUOTA), source.c_str(), QFMT_VFS_V1, + nullptr) != 0 && errno != EBUSY) { + PLOG(ERROR) << "Failed to enable USRQUOTA on " << source; + } + if (quotactl(QCMD(Q_QUOTAON, GRPQUOTA), source.c_str(), QFMT_VFS_V1, + nullptr) != 0 && errno != EBUSY) { + PLOG(ERROR) << "Failed to enable GRPQUOTA on " << source; + } } } } @@ -2478,6 +2666,7 @@ binder::Status InstalldNativeService::prepareAppProfile(const std::string& packa const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); + CHECK_ARGUMENT_PATH(codePath); std::lock_guard<std::recursive_mutex> lock(mLock); *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath, diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 1648603b52..cebd3f90d3 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -123,7 +123,9 @@ public: binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath); binder::Status installApkVerity(const std::string& filePath, - const ::android::base::unique_fd& verityInput); + const ::android::base::unique_fd& verityInput, int32_t contentSize); + binder::Status assertFsverityRootHashMatches(const std::string& filePath, + const std::vector<uint8_t>& expectedHash); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isa, const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index 432751feda..91e20b75ae 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -85,7 +85,9 @@ interface IInstalld { @utf8InCpp String outputPath); void deleteOdex(@utf8InCpp String apkPath, @utf8InCpp String instructionSet, @nullable @utf8InCpp String outputPath); - void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput); + void installApkVerity(@utf8InCpp String filePath, in FileDescriptor verityInput, + int contentSize); + void assertFsverityRootHashMatches(@utf8InCpp String filePath, in byte[] expectedHash); boolean reconcileSecondaryDexFile(@utf8InCpp String dexPath, @utf8InCpp String pkgName, int uid, in @utf8InCpp String[] isas, @nullable @utf8InCpp String volume_uuid, diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index a19ce0958c..fa23d3a93e 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -309,7 +309,9 @@ static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vd // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat. const char* dex2oat_bin = "/system/bin/dex2oat"; constexpr const char* kDex2oatDebugPath = "/system/bin/dex2oatd"; - if (is_debug_runtime() || (background_job_compile && is_debuggable_build())) { + // Do not use dex2oatd for release candidates (give dex2oat more soak time). + bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL"; + if (is_debug_runtime() || (background_job_compile && is_debuggable_build() && !is_release)) { if (access(kDex2oatDebugPath, X_OK) == 0) { dex2oat_bin = kDex2oatDebugPath; } diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 4c75eb51e1..96d8c47a8a 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -549,6 +549,7 @@ private: CHECK(EndsWith(path, "/")); path = path + "oat"; if (access(path.c_str(), F_OK) == 0) { + LOG(INFO) << "Skipping A/B OTA preopt of already preopted package " << apk_path; return true; } } @@ -560,7 +561,7 @@ private: // this tool will wipe the OTA artifact cache and try again (for robustness after // a failed OTA with remaining cache artifacts). if (access(apk_path, F_OK) != 0) { - LOG(WARNING) << "Skipping preopt of non-existing package " << apk_path; + LOG(WARNING) << "Skipping A/B OTA preopt of non-existing package " << apk_path; return true; } diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index bbff6fb0d7..bcdd03efad 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -33,7 +33,7 @@ #define TEST_APP_PRIVATE_DIR "/data/app-private/" #define TEST_APP_EPHEMERAL_DIR "/data/app-ephemeral/" #define TEST_ASEC_DIR "/mnt/asec/" -#define TEST_EXPAND_DIR "/mnt/expand/" +#define TEST_EXPAND_DIR "/mnt/expand/00000000-0000-0000-0000-000000000000/" #define TEST_SYSTEM_DIR1 "/system/app/" #define TEST_SYSTEM_DIR2 "/vendor/app/" @@ -116,6 +116,41 @@ TEST_F(UtilsTest, IsValidApkPath_Internal) { << bad_path5 << " should be rejected as a invalid path"; } +TEST_F(UtilsTest, IsValidApkPath_TopDir) { + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example")); + EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example")); + EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example")); + EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example")); +} + +TEST_F(UtilsTest, IsValidApkPath_TopFile) { + EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk")); + EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk")); + EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk")); + EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDir) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDirDir) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64")); +} + +TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) { + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex")); + EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex")); +} + TEST_F(UtilsTest, IsValidApkPath_Private) { // Internal directories const char *private1 = TEST_APP_PRIVATE_DIR "example.apk"; diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index a8c32ed2a5..1ff45e4845 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -855,21 +855,25 @@ bool validate_secondary_dex_path(const std::string& pkgname, const std::string& * that path. Returns -1 when an invalid path is encountered and 0 when a valid path * is encountered. */ -static int validate_apk_path_internal(const char *path, int maxSubdirs) { - std::string path_ = path; - if (validate_path(android_app_dir, path_, maxSubdirs) == 0) { - return 0; - } else if (validate_path(android_app_private_dir, path_, maxSubdirs) == 0) { +static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { + if (validate_path(android_app_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_app_ephemeral_dir, path_, maxSubdirs) == 0) { + } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_asec_dir, path_, maxSubdirs) == 0) { + } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) { return 0; - } else if (validate_path(android_mnt_expand_dir, path_, std::max(maxSubdirs, 2)) == 0) { + } else if (validate_path(android_asec_dir, path, maxSubdirs) == 0) { return 0; - } else { - return -1; + } else if (android::base::StartsWith(path, android_mnt_expand_dir)) { + // Rewrite the path as if it were on internal storage, and test that + size_t end = path.find('/', android_mnt_expand_dir.size() + 1); + if (end != std::string::npos) { + auto modified = path; + modified.replace(0, end + 1, android_data_dir); + return validate_apk_path_internal(modified, maxSubdirs); + } } + return -1; } int validate_apk_path(const char* path) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index b74073cb29..5829c4fd14 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -36,7 +36,7 @@ #define BYPASS_QUOTA 0 #define BYPASS_SDCARDFS 0 -#define APPLY_HARD_QUOTAS 1 +#define APPLY_HARD_QUOTAS 0 namespace android { namespace installd { diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index c0e1a35e20..92e915181b 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -576,7 +576,7 @@ Status ListCommand::fetchAllLibraries(const sp<IServiceManager> &manager) { using namespace ::android::hidl::manager::V1_0; using namespace ::android::hidl::base::V1_0; using std::literals::chrono_literals::operator""s; - auto ret = timeoutIPC(2s, manager, &IServiceManager::debugDump, [&] (const auto &infos) { + auto ret = timeoutIPC(10s, manager, &IServiceManager::debugDump, [&] (const auto &infos) { std::map<std::string, TableEntry> entries; for (const auto &info : infos) { std::string interfaceName = std::string{info.interfaceName.c_str()} + "/" + diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 31cd0cb5ac..6b340a8d3a 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -138,6 +138,7 @@ struct svcinfo uint32_t handle; struct binder_death death; int allow_isolated; + uint32_t dumpsys_priority; size_t len; uint16_t name[0]; }; @@ -198,11 +199,8 @@ uint32_t do_find_service(const uint16_t *s, size_t len, uid_t uid, pid_t spid) return si->handle; } -int do_add_service(struct binder_state *bs, - const uint16_t *s, size_t len, - uint32_t handle, uid_t uid, int allow_isolated, - pid_t spid) -{ +int do_add_service(struct binder_state *bs, const uint16_t *s, size_t len, uint32_t handle, + uid_t uid, int allow_isolated, uint32_t dumpsys_priority, pid_t spid) { struct svcinfo *si; //ALOGI("add_service('%s',%x,%s) uid=%d\n", str8(s, len), handle, @@ -239,6 +237,7 @@ int do_add_service(struct binder_state *bs, si->death.func = (void*) svcinfo_death; si->death.ptr = si; si->allow_isolated = allow_isolated; + si->dumpsys_priority = dumpsys_priority; si->next = svclist; svclist = si; } @@ -259,6 +258,7 @@ int svcmgr_handler(struct binder_state *bs, uint32_t handle; uint32_t strict_policy; int allow_isolated; + uint32_t dumpsys_priority; //ALOGI("target=%p code=%d pid=%d uid=%d\n", // (void*) txn->target.ptr, txn->code, txn->sender_pid, txn->sender_euid); @@ -317,13 +317,15 @@ int svcmgr_handler(struct binder_state *bs, } handle = bio_get_ref(msg); allow_isolated = bio_get_uint32(msg) ? 1 : 0; - if (do_add_service(bs, s, len, handle, txn->sender_euid, - allow_isolated, txn->sender_pid)) + dumpsys_priority = bio_get_uint32(msg); + if (do_add_service(bs, s, len, handle, txn->sender_euid, allow_isolated, dumpsys_priority, + txn->sender_pid)) return -1; break; case SVC_MGR_LIST_SERVICES: { uint32_t n = bio_get_uint32(msg); + uint32_t req_dumpsys_priority = bio_get_uint32(msg); if (!svc_can_list(txn->sender_pid, txn->sender_euid)) { ALOGE("list_service() uid=%d - PERMISSION DENIED\n", @@ -331,8 +333,15 @@ int svcmgr_handler(struct binder_state *bs, return -1; } si = svclist; - while ((n-- > 0) && si) + // walk through the list of services n times skipping services that + // do not support the requested priority + while (si) { + if (si->dumpsys_priority & req_dumpsys_priority) { + if (n == 0) break; + n--; + } si = si->next; + } if (si) { bio_put_string16(reply, si->name); return 0; diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp index 2b5389b8dc..4140f40888 100644 --- a/cmds/surfacereplayer/replayer/Replayer.cpp +++ b/cmds/surfacereplayer/replayer/Replayer.cpp @@ -24,9 +24,9 @@ #include <gui/BufferQueue.h> #include <gui/ISurfaceComposer.h> +#include <gui/LayerState.h> #include <gui/Surface.h> #include <private/gui/ComposerService.h> -#include <private/gui/LayerState.h> #include <ui/DisplayInfo.h> #include <utils/Log.h> @@ -338,27 +338,29 @@ status_t Replayer::dispatchEvent(int index) { status_t Replayer::doTransaction(const Transaction& t, const std::shared_ptr<Event>& event) { ALOGV("Started Transaction"); - SurfaceComposerClient::openGlobalTransaction(); + SurfaceComposerClient::Transaction liveTransaction; status_t status = NO_ERROR; - status = doSurfaceTransaction(t.surface_change()); - doDisplayTransaction(t.display_change()); + status = doSurfaceTransaction(liveTransaction, t.surface_change()); + doDisplayTransaction(liveTransaction, t.display_change()); if (t.animation()) { - SurfaceComposerClient::setAnimationTransaction(); + liveTransaction.setAnimationTransaction(); } event->readyToExecute(); - SurfaceComposerClient::closeGlobalTransaction(t.synchronous()); + liveTransaction.apply(t.synchronous()); ALOGV("Ended Transaction"); return status; } -status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) { +status_t Replayer::doSurfaceTransaction( + SurfaceComposerClient::Transaction& transaction, + const SurfaceChanges& surfaceChanges) { status_t status = NO_ERROR; for (const SurfaceChange& change : surfaceChanges) { @@ -369,62 +371,66 @@ status_t Replayer::doSurfaceTransaction(const SurfaceChanges& surfaceChanges) { switch (change.SurfaceChange_case()) { case SurfaceChange::SurfaceChangeCase::kPosition: - status = setPosition(change.id(), change.position()); + setPosition(transaction, change.id(), change.position()); break; case SurfaceChange::SurfaceChangeCase::kSize: - status = setSize(change.id(), change.size()); + setSize(transaction, change.id(), change.size()); break; case SurfaceChange::SurfaceChangeCase::kAlpha: - status = setAlpha(change.id(), change.alpha()); + setAlpha(transaction, change.id(), change.alpha()); break; case SurfaceChange::SurfaceChangeCase::kLayer: - status = setLayer(change.id(), change.layer()); + setLayer(transaction, change.id(), change.layer()); break; case SurfaceChange::SurfaceChangeCase::kCrop: - status = setCrop(change.id(), change.crop()); + setCrop(transaction, change.id(), change.crop()); break; case SurfaceChange::SurfaceChangeCase::kMatrix: - status = setMatrix(change.id(), change.matrix()); + setMatrix(transaction, change.id(), change.matrix()); break; case SurfaceChange::SurfaceChangeCase::kFinalCrop: - status = setFinalCrop(change.id(), change.final_crop()); + setFinalCrop(transaction, change.id(), change.final_crop()); break; case SurfaceChange::SurfaceChangeCase::kOverrideScalingMode: - status = setOverrideScalingMode(change.id(), change.override_scaling_mode()); + setOverrideScalingMode(transaction, change.id(), + change.override_scaling_mode()); break; case SurfaceChange::SurfaceChangeCase::kTransparentRegionHint: - status = setTransparentRegionHint(change.id(), change.transparent_region_hint()); + setTransparentRegionHint(transaction, change.id(), + change.transparent_region_hint()); break; case SurfaceChange::SurfaceChangeCase::kLayerStack: - status = setLayerStack(change.id(), change.layer_stack()); + setLayerStack(transaction, change.id(), change.layer_stack()); break; case SurfaceChange::SurfaceChangeCase::kHiddenFlag: - status = setHiddenFlag(change.id(), change.hidden_flag()); + setHiddenFlag(transaction, change.id(), change.hidden_flag()); break; case SurfaceChange::SurfaceChangeCase::kOpaqueFlag: - status = setOpaqueFlag(change.id(), change.opaque_flag()); + setOpaqueFlag(transaction, change.id(), change.opaque_flag()); break; case SurfaceChange::SurfaceChangeCase::kSecureFlag: - status = setSecureFlag(change.id(), change.secure_flag()); + setSecureFlag(transaction, change.id(), change.secure_flag()); break; case SurfaceChange::SurfaceChangeCase::kDeferredTransaction: waitUntilDeferredTransactionLayerExists(change.deferred_transaction(), lock); - status = setDeferredTransaction(change.id(), change.deferred_transaction()); + setDeferredTransaction(transaction, change.id(), + change.deferred_transaction()); break; default: - status = NO_ERROR; + status = 1; break; } if (status != NO_ERROR) { - ALOGE("SET TRANSACTION FAILED"); + ALOGE("Unknown Transaction Code"); return status; } } return status; } -void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) { +void Replayer::doDisplayTransaction(SurfaceComposerClient::Transaction& t, + const DisplayChanges& displayChanges) { for (const DisplayChange& change : displayChanges) { ALOGV("Doing display transaction"); std::unique_lock<std::mutex> lock(mDisplayLock); @@ -434,16 +440,16 @@ void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) { switch (change.DisplayChange_case()) { case DisplayChange::DisplayChangeCase::kSurface: - setDisplaySurface(change.id(), change.surface()); + setDisplaySurface(t, change.id(), change.surface()); break; case DisplayChange::DisplayChangeCase::kLayerStack: - setDisplayLayerStack(change.id(), change.layer_stack()); + setDisplayLayerStack(t, change.id(), change.layer_stack()); break; case DisplayChange::DisplayChangeCase::kSize: - setDisplaySize(change.id(), change.size()); + setDisplaySize(t, change.id(), change.size()); break; case DisplayChange::DisplayChangeCase::kProjection: - setDisplayProjection(change.id(), change.projection()); + setDisplayProjection(t, change.id(), change.projection()); break; default: break; @@ -451,57 +457,66 @@ void Replayer::doDisplayTransaction(const DisplayChanges& displayChanges) { } } -status_t Replayer::setPosition(layer_id id, const PositionChange& pc) { +void Replayer::setPosition(SurfaceComposerClient::Transaction& t, + layer_id id, const PositionChange& pc) { ALOGV("Layer %d: Setting Position -- x=%f, y=%f", id, pc.x(), pc.y()); - return mLayers[id]->setPosition(pc.x(), pc.y()); + t.setPosition(mLayers[id], pc.x(), pc.y()); } -status_t Replayer::setSize(layer_id id, const SizeChange& sc) { +void Replayer::setSize(SurfaceComposerClient::Transaction& t, + layer_id id, const SizeChange& sc) { ALOGV("Layer %d: Setting Size -- w=%u, h=%u", id, sc.w(), sc.h()); - return mLayers[id]->setSize(sc.w(), sc.h()); + t.setSize(mLayers[id], sc.w(), sc.h()); } -status_t Replayer::setLayer(layer_id id, const LayerChange& lc) { +void Replayer::setLayer(SurfaceComposerClient::Transaction& t, + layer_id id, const LayerChange& lc) { ALOGV("Layer %d: Setting Layer -- layer=%d", id, lc.layer()); - return mLayers[id]->setLayer(lc.layer()); + t.setLayer(mLayers[id], lc.layer()); } -status_t Replayer::setAlpha(layer_id id, const AlphaChange& ac) { +void Replayer::setAlpha(SurfaceComposerClient::Transaction& t, + layer_id id, const AlphaChange& ac) { ALOGV("Layer %d: Setting Alpha -- alpha=%f", id, ac.alpha()); - return mLayers[id]->setAlpha(ac.alpha()); + t.setAlpha(mLayers[id], ac.alpha()); } -status_t Replayer::setCrop(layer_id id, const CropChange& cc) { +void Replayer::setCrop(SurfaceComposerClient::Transaction& t, + layer_id id, const CropChange& cc) { ALOGV("Layer %d: Setting Crop -- left=%d, top=%d, right=%d, bottom=%d", id, cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); Rect r = Rect(cc.rectangle().left(), cc.rectangle().top(), cc.rectangle().right(), cc.rectangle().bottom()); - return mLayers[id]->setCrop(r); + t.setCrop(mLayers[id], r); } -status_t Replayer::setFinalCrop(layer_id id, const FinalCropChange& fcc) { +void Replayer::setFinalCrop(SurfaceComposerClient::Transaction& t, + layer_id id, const FinalCropChange& fcc) { ALOGV("Layer %d: Setting Final Crop -- left=%d, top=%d, right=%d, bottom=%d", id, fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), fcc.rectangle().bottom()); Rect r = Rect(fcc.rectangle().left(), fcc.rectangle().top(), fcc.rectangle().right(), fcc.rectangle().bottom()); - return mLayers[id]->setFinalCrop(r); + t.setFinalCrop(mLayers[id], r); } -status_t Replayer::setMatrix(layer_id id, const MatrixChange& mc) { +void Replayer::setMatrix(SurfaceComposerClient::Transaction& t, + layer_id id, const MatrixChange& mc) { ALOGV("Layer %d: Setting Matrix -- dsdx=%f, dtdx=%f, dsdy=%f, dtdy=%f", id, mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); - return mLayers[id]->setMatrix(mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); + t.setMatrix(mLayers[id], mc.dsdx(), mc.dtdx(), mc.dsdy(), mc.dtdy()); } -status_t Replayer::setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc) { +void Replayer::setOverrideScalingMode(SurfaceComposerClient::Transaction& t, + layer_id id, const OverrideScalingModeChange& osmc) { ALOGV("Layer %d: Setting Override Scaling Mode -- mode=%d", id, osmc.override_scaling_mode()); - return mLayers[id]->setOverrideScalingMode(osmc.override_scaling_mode()); + t.setOverrideScalingMode(mLayers[id], osmc.override_scaling_mode()); } -status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trhc) { +void Replayer::setTransparentRegionHint(SurfaceComposerClient::Transaction& t, + layer_id id, const TransparentRegionHintChange& trhc) { ALOGV("Setting Transparent Region Hint"); Region re = Region(); @@ -510,71 +525,80 @@ status_t Replayer::setTransparentRegionHint(layer_id id, const TransparentRegion re.merge(rect); } - return mLayers[id]->setTransparentRegionHint(re); + t.setTransparentRegionHint(mLayers[id], re); } -status_t Replayer::setLayerStack(layer_id id, const LayerStackChange& lsc) { +void Replayer::setLayerStack(SurfaceComposerClient::Transaction& t, + layer_id id, const LayerStackChange& lsc) { ALOGV("Layer %d: Setting LayerStack -- layer_stack=%d", id, lsc.layer_stack()); - return mLayers[id]->setLayerStack(lsc.layer_stack()); + t.setLayerStack(mLayers[id], lsc.layer_stack()); } -status_t Replayer::setHiddenFlag(layer_id id, const HiddenFlagChange& hfc) { +void Replayer::setHiddenFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const HiddenFlagChange& hfc) { ALOGV("Layer %d: Setting Hidden Flag -- hidden_flag=%d", id, hfc.hidden_flag()); layer_id flag = hfc.hidden_flag() ? layer_state_t::eLayerHidden : 0; - return mLayers[id]->setFlags(flag, layer_state_t::eLayerHidden); + t.setFlags(mLayers[id], flag, layer_state_t::eLayerHidden); } -status_t Replayer::setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc) { +void Replayer::setOpaqueFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const OpaqueFlagChange& ofc) { ALOGV("Layer %d: Setting Opaque Flag -- opaque_flag=%d", id, ofc.opaque_flag()); layer_id flag = ofc.opaque_flag() ? layer_state_t::eLayerOpaque : 0; - return mLayers[id]->setFlags(flag, layer_state_t::eLayerOpaque); + t.setFlags(mLayers[id], flag, layer_state_t::eLayerOpaque); } -status_t Replayer::setSecureFlag(layer_id id, const SecureFlagChange& sfc) { +void Replayer::setSecureFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const SecureFlagChange& sfc) { ALOGV("Layer %d: Setting Secure Flag -- secure_flag=%d", id, sfc.secure_flag()); layer_id flag = sfc.secure_flag() ? layer_state_t::eLayerSecure : 0; - return mLayers[id]->setFlags(flag, layer_state_t::eLayerSecure); + t.setFlags(mLayers[id], flag, layer_state_t::eLayerSecure); } -status_t Replayer::setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc) { +void Replayer::setDeferredTransaction(SurfaceComposerClient::Transaction& t, + layer_id id, const DeferredTransactionChange& dtc) { ALOGV("Layer %d: Setting Deferred Transaction -- layer_id=%d, " "frame_number=%llu", id, dtc.layer_id(), dtc.frame_number()); if (mLayers.count(dtc.layer_id()) == 0 || mLayers[dtc.layer_id()] == nullptr) { ALOGE("Layer %d not found in Deferred Transaction", dtc.layer_id()); - return BAD_VALUE; + return; } auto handle = mLayers[dtc.layer_id()]->getHandle(); - return mLayers[id]->deferTransactionUntil(handle, dtc.frame_number()); + t.deferTransactionUntil(mLayers[id], handle, dtc.frame_number()); } -void Replayer::setDisplaySurface(display_id id, const DispSurfaceChange& /*dsc*/) { +void Replayer::setDisplaySurface(SurfaceComposerClient::Transaction& t, + display_id id, const DispSurfaceChange& /*dsc*/) { sp<IGraphicBufferProducer> outProducer; sp<IGraphicBufferConsumer> outConsumer; BufferQueue::createBufferQueue(&outProducer, &outConsumer); - SurfaceComposerClient::setDisplaySurface(mDisplays[id], outProducer); + t.setDisplaySurface(mDisplays[id], outProducer); } -void Replayer::setDisplayLayerStack(display_id id, const LayerStackChange& lsc) { - SurfaceComposerClient::setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); +void Replayer::setDisplayLayerStack(SurfaceComposerClient::Transaction& t, + display_id id, const LayerStackChange& lsc) { + t.setDisplayLayerStack(mDisplays[id], lsc.layer_stack()); } -void Replayer::setDisplaySize(display_id id, const SizeChange& sc) { - SurfaceComposerClient::setDisplaySize(mDisplays[id], sc.w(), sc.h()); +void Replayer::setDisplaySize(SurfaceComposerClient::Transaction& t, + display_id id, const SizeChange& sc) { + t.setDisplaySize(mDisplays[id], sc.w(), sc.h()); } -void Replayer::setDisplayProjection(display_id id, const ProjectionChange& pc) { +void Replayer::setDisplayProjection(SurfaceComposerClient::Transaction& t, + display_id id, const ProjectionChange& pc) { Rect viewport = Rect(pc.viewport().left(), pc.viewport().top(), pc.viewport().right(), pc.viewport().bottom()); Rect frame = Rect(pc.frame().left(), pc.frame().top(), pc.frame().right(), pc.frame().bottom()); - SurfaceComposerClient::setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame); + t.setDisplayProjection(mDisplays[id], pc.orientation(), viewport, frame); } status_t Replayer::createSurfaceControl( diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h index f36c9fd4a4..295403eace 100644 --- a/cmds/surfacereplayer/replayer/Replayer.h +++ b/cmds/surfacereplayer/replayer/Replayer.h @@ -77,28 +77,48 @@ class Replayer { void deleteDisplay(const DisplayDeletion& delete_, const std::shared_ptr<Event>& event); void updatePowerMode(const PowerModeUpdate& update, const std::shared_ptr<Event>& event); - status_t doSurfaceTransaction(const SurfaceChanges& surfaceChange); - void doDisplayTransaction(const DisplayChanges& displayChange); - - status_t setPosition(layer_id id, const PositionChange& pc); - status_t setSize(layer_id id, const SizeChange& sc); - status_t setAlpha(layer_id id, const AlphaChange& ac); - status_t setLayer(layer_id id, const LayerChange& lc); - status_t setCrop(layer_id id, const CropChange& cc); - status_t setFinalCrop(layer_id id, const FinalCropChange& fcc); - status_t setMatrix(layer_id id, const MatrixChange& mc); - status_t setOverrideScalingMode(layer_id id, const OverrideScalingModeChange& osmc); - status_t setTransparentRegionHint(layer_id id, const TransparentRegionHintChange& trgc); - status_t setLayerStack(layer_id id, const LayerStackChange& lsc); - status_t setHiddenFlag(layer_id id, const HiddenFlagChange& hfc); - status_t setOpaqueFlag(layer_id id, const OpaqueFlagChange& ofc); - status_t setSecureFlag(layer_id id, const SecureFlagChange& sfc); - status_t setDeferredTransaction(layer_id id, const DeferredTransactionChange& dtc); - - void setDisplaySurface(display_id id, const DispSurfaceChange& dsc); - void setDisplayLayerStack(display_id id, const LayerStackChange& lsc); - void setDisplaySize(display_id id, const SizeChange& sc); - void setDisplayProjection(display_id id, const ProjectionChange& pc); + status_t doSurfaceTransaction(SurfaceComposerClient::Transaction& transaction, + const SurfaceChanges& surfaceChange); + void doDisplayTransaction(SurfaceComposerClient::Transaction& transaction, + const DisplayChanges& displayChange); + + void setPosition(SurfaceComposerClient::Transaction& t, + layer_id id, const PositionChange& pc); + void setSize(SurfaceComposerClient::Transaction& t, + layer_id id, const SizeChange& sc); + void setAlpha(SurfaceComposerClient::Transaction& t, + layer_id id, const AlphaChange& ac); + void setLayer(SurfaceComposerClient::Transaction& t, + layer_id id, const LayerChange& lc); + void setCrop(SurfaceComposerClient::Transaction& t, + layer_id id, const CropChange& cc); + void setFinalCrop(SurfaceComposerClient::Transaction& t, + layer_id id, const FinalCropChange& fcc); + void setMatrix(SurfaceComposerClient::Transaction& t, + layer_id id, const MatrixChange& mc); + void setOverrideScalingMode(SurfaceComposerClient::Transaction& t, + layer_id id, const OverrideScalingModeChange& osmc); + void setTransparentRegionHint(SurfaceComposerClient::Transaction& t, + layer_id id, const TransparentRegionHintChange& trgc); + void setLayerStack(SurfaceComposerClient::Transaction& t, + layer_id id, const LayerStackChange& lsc); + void setHiddenFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const HiddenFlagChange& hfc); + void setOpaqueFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const OpaqueFlagChange& ofc); + void setSecureFlag(SurfaceComposerClient::Transaction& t, + layer_id id, const SecureFlagChange& sfc); + void setDeferredTransaction(SurfaceComposerClient::Transaction& t, + layer_id id, const DeferredTransactionChange& dtc); + + void setDisplaySurface(SurfaceComposerClient::Transaction& t, + display_id id, const DispSurfaceChange& dsc); + void setDisplayLayerStack(SurfaceComposerClient::Transaction& t, + display_id id, const LayerStackChange& lsc); + void setDisplaySize(SurfaceComposerClient::Transaction& t, + display_id id, const SizeChange& sc); + void setDisplayProjection(SurfaceComposerClient::Transaction& t, + display_id id, const ProjectionChange& pc); void doDeleteSurfaceControls(); void waitUntilTimestamp(int64_t timestamp); |