diff options
author | 2020-12-08 12:50:58 -0800 | |
---|---|---|
committer | 2021-03-31 11:42:15 -0700 | |
commit | cfeeda477dc1d4d63a5488358cf2ccce2b487117 (patch) | |
tree | 72eaaad1fd38bd89294f28997655b568b4e9efa3 | |
parent | 41c0644cd269e0ef3b346d4eccbf0e42bc569800 (diff) |
dumpsys: add --thread option
This displays the thread usage of services in the format
"<threadsUsed>/<threadCount>". This has been useful in lshal to find
deadlocks in binder threads.
Test: atest dumpsys_test
Test: run 'dumpsys --thread' on cuttlefish and verify thread usage
Test: run 'dumpsys --thread SurfaceFlinger' and verify thread usage
Bug: 140639610
Change-Id: I0cd3f86ca47ec911b276d8e8859ef54743ee34cf
-rw-r--r-- | cmds/dumpstate/Android.bp | 1 | ||||
-rw-r--r-- | cmds/dumpsys/Android.bp | 1 | ||||
-rw-r--r-- | cmds/dumpsys/dumpsys.cpp | 30 | ||||
-rw-r--r-- | cmds/dumpsys/dumpsys.h | 7 | ||||
-rw-r--r-- | cmds/dumpsys/tests/Android.bp | 3 | ||||
-rw-r--r-- | cmds/dumpsys/tests/AndroidTest.xml | 2 | ||||
-rw-r--r-- | cmds/dumpsys/tests/dumpsys_test.cpp | 41 |
7 files changed, 77 insertions, 8 deletions
diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index f48f1fb6f8..aff32c38c2 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -99,6 +99,7 @@ cc_defaults { "libhidlbase", "liblog", "libutils", + "libbinderdebug", ], srcs: [ "DumpstateService.cpp", diff --git a/cmds/dumpsys/Android.bp b/cmds/dumpsys/Android.bp index 91aa018451..6ab6b7f951 100644 --- a/cmds/dumpsys/Android.bp +++ b/cmds/dumpsys/Android.bp @@ -32,6 +32,7 @@ cc_defaults { "libutils", "liblog", "libbinder", + "libbinderdebug", ], static_libs: [ diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index a017246184..ba1c449dbf 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 <binderdebug/BinderDebug.h> #include <serviceutils/PriorityDumper.h> #include <utils/Log.h> #include <utils/Vector.h> @@ -60,13 +61,15 @@ static void usage() { "usage: dumpsys\n" " To dump all services.\n" "or:\n" - " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--help | -l | --skip SERVICES " + " dumpsys [-t TIMEOUT] [--priority LEVEL] [--pid] [--thread] [--help | -l | " + "--skip SERVICES " "| SERVICE [ARGS]]\n" " --help: shows this help\n" " -l: only list services, do not dump them\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" " --pid: dump PID instead of usual dump\n" + " --thread: dump thread usage instead of usual dump\n" " --proto: filter services that support dumping data in proto format. Dumps\n" " will be in proto format.\n" " --priority LEVEL: filter services based on specified priority\n" @@ -125,7 +128,8 @@ int Dumpsys::main(int argc, char* const argv[]) { Type type = Type::DUMP; int timeoutArgMs = 10000; int priorityFlags = IServiceManager::DUMP_FLAG_PRIORITY_ALL; - static struct option longOptions[] = {{"pid", no_argument, 0, 0}, + static struct option longOptions[] = {{"thread", no_argument, 0, 0}, + {"pid", no_argument, 0, 0}, {"priority", required_argument, 0, 0}, {"proto", no_argument, 0, 0}, {"skip", no_argument, 0, 0}, @@ -163,6 +167,8 @@ int Dumpsys::main(int argc, char* const argv[]) { } } else if (!strcmp(longOptions[optionIndex].name, "pid")) { type = Type::PID; + } else if (!strcmp(longOptions[optionIndex].name, "thread")) { + type = Type::THREAD; } break; @@ -329,6 +335,23 @@ static status_t dumpPidToFd(const sp<IBinder>& service, const unique_fd& fd) { return OK; } +static status_t dumpThreadsToFd(const sp<IBinder>& service, const unique_fd& fd) { + pid_t pid; + status_t status = service->getDebugPid(&pid); + if (status != OK) { + return status; + } + BinderPidInfo pidInfo; + status = getBinderPidInfo(BinderDebugContext::BINDER, pid, &pidInfo); + if (status != OK) { + return status; + } + WriteStringToFd("Threads in use: " + std::to_string(pidInfo.threadUsage) + "/" + + std::to_string(pidInfo.threadCount) + "\n", + fd.get()); + return OK; +} + status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, const Vector<String16>& args) { sp<IBinder> service = sm_->checkService(serviceName); @@ -359,6 +382,9 @@ status_t Dumpsys::startDumpThread(Type type, const String16& serviceName, case Type::PID: err = dumpPidToFd(service, remote_end); break; + case Type::THREAD: + err = dumpThreadsToFd(service, remote_end); + break; default: std::cerr << "Unknown dump type" << static_cast<int>(type) << std::endl; return; diff --git a/cmds/dumpsys/dumpsys.h b/cmds/dumpsys/dumpsys.h index 929c55c364..349947ce12 100644 --- a/cmds/dumpsys/dumpsys.h +++ b/cmds/dumpsys/dumpsys.h @@ -52,13 +52,14 @@ class Dumpsys { static void setServiceArgs(Vector<String16>& args, bool asProto, int priorityFlags); enum class Type { - DUMP, // dump using `dump` function - PID, // dump pid of server only + DUMP, // dump using `dump` function + PID, // dump pid of server only + THREAD, // dump thread usage of server only }; /** * 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 + * the output to a pipe. Thread must be stopped by a subsequent call to {@code * stopDumpThread}. * @param serviceName * @param args list of arguments to pass to service dump method. diff --git a/cmds/dumpsys/tests/Android.bp b/cmds/dumpsys/tests/Android.bp index 6854c7550e..58fec30c9b 100644 --- a/cmds/dumpsys/tests/Android.bp +++ b/cmds/dumpsys/tests/Android.bp @@ -19,6 +19,7 @@ cc_test { "libbase", "libbinder", "libutils", + "libbinderdebug", ], static_libs: [ @@ -26,6 +27,4 @@ cc_test { "libgmock", "libserviceutils", ], - - clang: true, } diff --git a/cmds/dumpsys/tests/AndroidTest.xml b/cmds/dumpsys/tests/AndroidTest.xml index 1a8c67f7aa..c2351d9aff 100644 --- a/cmds/dumpsys/tests/AndroidTest.xml +++ b/cmds/dumpsys/tests/AndroidTest.xml @@ -23,4 +23,4 @@ <option name="native-test-device-path" value="/data/local/tmp" /> <option name="module-name" value="dumpsys_test" /> </test> -</configuration>
\ No newline at end of file +</configuration> diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 67a77f6015..39af7dfae0 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -16,12 +16,15 @@ #include "../dumpsys.h" +#include <regex> #include <vector> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <android-base/file.h> +#include <binder/Binder.h> +#include <binder/ProcessState.h> #include <serviceutils/PriorityDumper.h> #include <utils/String16.h> #include <utils/String8.h> @@ -222,6 +225,10 @@ class DumpsysTest : public Test { EXPECT_THAT(stdout_, HasSubstr(expected)); } + void AssertOutputFormat(const std::string format) { + EXPECT_THAT(stdout_, testing::MatchesRegex(format)); + } + 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: ")); @@ -574,6 +581,30 @@ TEST_F(DumpsysTest, ListServiceWithPid) { AssertOutput(std::to_string(getpid()) + "\n"); } +// Tests 'dumpsys --thread' +TEST_F(DumpsysTest, ListAllServicesWithThread) { + ExpectListServices({"Locksmith", "Valet"}); + ExpectCheckService("Locksmith"); + ExpectCheckService("Valet"); + + CallMain({"--thread"}); + + AssertRunningServices({"Locksmith", "Valet"}); + + const std::string format("(.|\n)*((Threads in use: [0-9]+/[0-9]+)?\n-(.|\n)*){2}"); + AssertOutputFormat(format); +} + +// Tests 'dumpsys --thread service_name' +TEST_F(DumpsysTest, ListServiceWithThread) { + ExpectCheckService("Locksmith"); + + CallMain({"--thread", "Locksmith"}); + // returns an empty string without root enabled + const std::string format("(^$|Threads in use: [0-9]/[0-9]+\n)"); + AssertOutputFormat(format); +} + TEST_F(DumpsysTest, GetBytesWritten) { const char* serviceName = "service2"; const char* dumpContents = "dump1"; @@ -599,3 +630,13 @@ TEST_F(DumpsysTest, WriteDumpWithoutThreadStart) { /* as_proto = */ false, elapsedDuration, bytesWritten); EXPECT_THAT(status, Eq(INVALID_OPERATION)); } + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + // start a binder thread pool for testing --thread option + android::ProcessState::self()->setThreadPoolMaxThreadCount(8); + ProcessState::self()->startThreadPool(); + + return RUN_ALL_TESTS(); +} |