diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/binderdebug/Android.bp | 28 | ||||
| -rw-r--r-- | libs/binderdebug/BinderDebug.cpp | 119 | ||||
| -rw-r--r-- | libs/binderdebug/TEST_MAPPING | 7 | ||||
| -rw-r--r-- | libs/binderdebug/include/binderdebug/BinderDebug.h | 37 | ||||
| -rw-r--r-- | libs/binderdebug/tests/Android.bp | 30 | ||||
| -rw-r--r-- | libs/binderdebug/tests/android/binderdebug/test/IControl.aidl | 22 | ||||
| -rw-r--r-- | libs/binderdebug/tests/binderdebug_test.cpp | 90 |
7 files changed, 333 insertions, 0 deletions
diff --git a/libs/binderdebug/Android.bp b/libs/binderdebug/Android.bp new file mode 100644 index 0000000000..343246a324 --- /dev/null +++ b/libs/binderdebug/Android.bp @@ -0,0 +1,28 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "libbinderdebug", + vendor_available: true, + shared_libs: [ + "libbase", + "libbinder", + ], + srcs: [ + "BinderDebug.cpp", + ], + export_include_dirs: [ + "include", + ], +} diff --git a/libs/binderdebug/BinderDebug.cpp b/libs/binderdebug/BinderDebug.cpp new file mode 100644 index 0000000000..b435dba197 --- /dev/null +++ b/libs/binderdebug/BinderDebug.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <android-base/parseint.h> +#include <android-base/strings.h> +#include <binder/Binder.h> +#include <sys/types.h> +#include <fstream> +#include <regex> + +#include <binderdebug/BinderDebug.h> + +namespace android { + +static std::string contextToString(BinderDebugContext context) { + switch (context) { + case BinderDebugContext::BINDER: + return "binder"; + case BinderDebugContext::HWBINDER: + return "hwbinder"; + case BinderDebugContext::VNDBINDER: + return "vndbinder"; + default: + return std::string(); + } +} + +static status_t scanBinderContext(pid_t pid, const std::string& contextName, + std::function<void(const std::string&)> eachLine) { + std::ifstream ifs("/dev/binderfs/binder_logs/proc/" + std::to_string(pid)); + if (!ifs.is_open()) { + ifs.open("/d/binder/proc/" + std::to_string(pid)); + if (!ifs.is_open()) { + return -errno; + } + } + static const std::regex kContextLine("^context (\\w+)$"); + + bool isDesiredContext = false; + std::string line; + std::smatch match; + while (getline(ifs, line)) { + if (std::regex_search(line, match, kContextLine)) { + isDesiredContext = match.str(1) == contextName; + continue; + } + if (!isDesiredContext) { + continue; + } + eachLine(line); + } + return OK; +} + +status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo) { + std::smatch match; + static const std::regex kReferencePrefix("^\\s*node \\d+:\\s+u([0-9a-f]+)\\s+c([0-9a-f]+)\\s+"); + static const std::regex kThreadPrefix("^\\s*thread \\d+:\\s+l\\s+(\\d)(\\d)"); + std::string contextStr = contextToString(context); + status_t ret = scanBinderContext(pid, contextStr, [&](const std::string& line) { + if (std::regex_search(line, match, kReferencePrefix)) { + const std::string& ptrString = "0x" + match.str(2); // use number after c + uint64_t ptr; + if (!::android::base::ParseUint(ptrString.c_str(), &ptr)) { + // Should not reach here, but just be tolerant. + return; + } + const std::string proc = " proc "; + auto pos = line.rfind(proc); + if (pos != std::string::npos) { + for (const std::string& pidStr : base::Split(line.substr(pos + proc.size()), " ")) { + int32_t pid; + if (!::android::base::ParseInt(pidStr, &pid)) { + return; + } + pidInfo->refPids[ptr].push_back(pid); + } + } + + return; + } + if (std::regex_search(line, match, kThreadPrefix)) { + // "1" is waiting in binder driver + // "2" is poll. It's impossible to tell if these are in use. + // and HIDL default code doesn't use it. + bool isInUse = match.str(1) != "1"; + // "0" is a thread that has called into binder + // "1" is looper thread + // "2" is main looper thread + bool isBinderThread = match.str(2) != "0"; + if (!isBinderThread) { + return; + } + if (isInUse) { + pidInfo->threadUsage++; + } + + pidInfo->threadCount++; + return; + } + return; + }); + return ret; +} + +} // namespace android diff --git a/libs/binderdebug/TEST_MAPPING b/libs/binderdebug/TEST_MAPPING new file mode 100644 index 0000000000..2f3353e871 --- /dev/null +++ b/libs/binderdebug/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "libbinderdebug_test" + } + ] +} diff --git a/libs/binderdebug/include/binderdebug/BinderDebug.h b/libs/binderdebug/include/binderdebug/BinderDebug.h new file mode 100644 index 0000000000..14a0ef3672 --- /dev/null +++ b/libs/binderdebug/include/binderdebug/BinderDebug.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include <map> +#include <vector> + +namespace android { + +struct BinderPidInfo { + std::map<uint64_t, std::vector<pid_t>> refPids; // cookie -> processes which hold binder + uint32_t threadUsage; // number of threads in use + uint32_t threadCount; // number of threads total +}; + +enum class BinderDebugContext { + BINDER, + HWBINDER, + VNDBINDER, +}; + +status_t getBinderPidInfo(BinderDebugContext context, pid_t pid, BinderPidInfo* pidInfo); + +} // namespace android diff --git a/libs/binderdebug/tests/Android.bp b/libs/binderdebug/tests/Android.bp new file mode 100644 index 0000000000..4c06b1d0d9 --- /dev/null +++ b/libs/binderdebug/tests/Android.bp @@ -0,0 +1,30 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_test { + name: "libbinderdebug_test", + test_suites: ["general-tests"], + srcs: [ + "binderdebug_test.cpp", + "android/binderdebug/test/IControl.aidl", + ], + shared_libs: [ + "libbase", + "libbinder", + "libutils", + ], + static_libs: ["libbinderdebug"], + cflags: ["-Wall", "-Werror"], + require_root: true, +} diff --git a/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl new file mode 100644 index 0000000000..8efeb6352f --- /dev/null +++ b/libs/binderdebug/tests/android/binderdebug/test/IControl.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.binderdebug.test; + +interface IControl { + // Notifies the service to continue execution + void Continue(); +} diff --git a/libs/binderdebug/tests/binderdebug_test.cpp b/libs/binderdebug/tests/binderdebug_test.cpp new file mode 100644 index 0000000000..ea799c06a2 --- /dev/null +++ b/libs/binderdebug/tests/binderdebug_test.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/Binder.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> +#include <binder/IPCThreadState.h> +#include <binderdebug/BinderDebug.h> +#include <gtest/gtest.h> +#include <semaphore.h> +#include <thread> + +#include <android/binderdebug/test/BnControl.h> +#include <android/binderdebug/test/IControl.h> + +namespace android { +namespace binderdebug { +namespace test { + +class Control : public BnControl { +public: + Control() {sem_init(&s, 1, 0);}; + ::android::binder::Status Continue() override; + sem_t s; +}; + +::android::binder::Status Control::Continue() { + IPCThreadState::self()->flushCommands(); + sem_post(&s); + return binder::Status::ok(); +} + +TEST(BinderDebugTests, BinderPid) { + BinderPidInfo pidInfo; + const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo); + ASSERT_EQ(status, OK); + // There should be one referenced PID for servicemanager + EXPECT_TRUE(!pidInfo.refPids.empty()); +} + +TEST(BinderDebugTests, BinderThreads) { + BinderPidInfo pidInfo; + const auto& status = getBinderPidInfo(BinderDebugContext::BINDER, getpid(), &pidInfo); + ASSERT_EQ(status, OK); + EXPECT_TRUE(pidInfo.threadUsage <= pidInfo.threadCount); + // The second looper thread can sometimes take longer to spawn. + EXPECT_GE(pidInfo.threadCount, 1); +} + +extern "C" { +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + + // Create a child/client process to call into the main process so we can ensure + // looper thread has been registered before attempting to get the BinderPidInfo + pid_t pid = fork(); + if (pid == 0) { + sp<IBinder> binder = android::defaultServiceManager()->getService(String16("binderdebug")); + sp<IControl> service; + if (binder != nullptr) { + service = android::interface_cast<IControl>(binder); + } + service->Continue(); + exit(0); + } + sp<Control> iface = new Control; + android::defaultServiceManager()->addService(String16("binderdebug"), iface); + android::ProcessState::self()->setThreadPoolMaxThreadCount(8); + ProcessState::self()->startThreadPool(); + sem_wait(&iface->s); + + return RUN_ALL_TESTS(); +} +} // extern "C" +} // namespace test +} // namespace binderdebug +} // namespace android |