diff options
author | Colin Cross <ccross@android.com> | 2017-07-13 19:41:26 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2017-07-13 19:41:26 +0000 |
commit | 454285dfcc6467a4745017134458fdc16c1c0220 (patch) | |
tree | 1bb58447a2c8cb32dc072115ef0510db3cab6e2d /libmemunreachable | |
parent | f42d02830de827096150fea42e99c55fdb2b7df7 (diff) | |
parent | f572b91c4f5acf739742da53ae3406e615c2d756 (diff) | |
download | system_core-454285dfcc6467a4745017134458fdc16c1c0220.tar.gz system_core-454285dfcc6467a4745017134458fdc16c1c0220.tar.bz2 system_core-454285dfcc6467a4745017134458fdc16c1c0220.zip |
Merge "libmemunreachable: track kernel binder references"
Diffstat (limited to 'libmemunreachable')
-rw-r--r-- | libmemunreachable/Android.bp | 16 | ||||
-rw-r--r-- | libmemunreachable/Binder.cpp | 80 | ||||
-rw-r--r-- | libmemunreachable/Binder.h | 28 | ||||
-rw-r--r-- | libmemunreachable/MemUnreachable.cpp | 17 | ||||
-rw-r--r-- | libmemunreachable/tests/Binder_test.cpp | 156 |
5 files changed, 294 insertions, 3 deletions
diff --git a/libmemunreachable/Android.bp b/libmemunreachable/Android.bp index cdac76bab..826a576e6 100644 --- a/libmemunreachable/Android.bp +++ b/libmemunreachable/Android.bp @@ -26,6 +26,7 @@ cc_library_shared { defaults: ["libmemunreachable_defaults"], srcs: [ "Allocator.cpp", + "Binder.cpp", "HeapWalker.cpp", "LeakFolding.cpp", "LeakPipe.cpp", @@ -84,3 +85,18 @@ cc_test { }, }, } + +cc_test { + name: "memunreachable_binder_test", + defaults: ["libmemunreachable_defaults"], + srcs: [ + "tests/Binder_test.cpp", + "tests/MemUnreachable_test.cpp", + ], + shared_libs: [ + "libbinder", + "libhwbinder", + "libmemunreachable", + "libutils", + ], +} diff --git a/libmemunreachable/Binder.cpp b/libmemunreachable/Binder.cpp new file mode 100644 index 000000000..60512a3f8 --- /dev/null +++ b/libmemunreachable/Binder.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 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 <sys/cdefs.h> +#include <unistd.h> + +#include <functional> + +#include "Binder.h" +#include "log.h" + +__BEGIN_DECLS + +// Weak undefined references to the symbols in libbinder and libhwbinder +// so that libmemunreachable can call them in processes that have them +// loaded without requiring libmemunreachable to have dependencies on them. +ssize_t __attribute__((weak)) getBinderKernelReferences(size_t, uintptr_t*); +ssize_t __attribute__((weak)) getHWBinderKernelReferences(size_t, uintptr_t*); + +__END_DECLS + +namespace android { + +static bool BinderReferencesToVector(allocator::vector<uintptr_t>& refs, + std::function<ssize_t(size_t, uintptr_t*)> fn) { + if (fn == nullptr) { + return true; + } + + size_t size = refs.size(); + + do { + refs.resize(size); + + ssize_t ret = fn(refs.size(), refs.data()); + if (ret < 0) { + return false; + } + + size = ret; + } while (size > refs.size()); + + refs.resize(size); + return true; +} + +bool BinderReferences(allocator::vector<uintptr_t>& refs) { + refs.clear(); + + allocator::vector<uintptr_t> binder_refs{refs.get_allocator()}; + if (BinderReferencesToVector(refs, getBinderKernelReferences)) { + refs.insert(refs.end(), binder_refs.begin(), binder_refs.end()); + } else { + MEM_ALOGE("getBinderKernelReferences failed"); + } + + allocator::vector<uintptr_t> hwbinder_refs{refs.get_allocator()}; + if (BinderReferencesToVector(hwbinder_refs, getHWBinderKernelReferences)) { + refs.insert(refs.end(), hwbinder_refs.begin(), hwbinder_refs.end()); + } else { + MEM_ALOGE("getHWBinderKernelReferences failed"); + } + + return true; +} + +} // namespace android diff --git a/libmemunreachable/Binder.h b/libmemunreachable/Binder.h new file mode 100644 index 000000000..bf4fd3e18 --- /dev/null +++ b/libmemunreachable/Binder.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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 LIBMEMUNREACHABLE_BINDER_H_ +#define LIBMEMUNREACHABLE_BINDER_H_ + +#include "Allocator.h" + +namespace android { + +bool BinderReferences(allocator::vector<uintptr_t>& refs); + +} // namespace android + +#endif // LIBMEMUNREACHABLE_BINDER_H_ diff --git a/libmemunreachable/MemUnreachable.cpp b/libmemunreachable/MemUnreachable.cpp index a1f74c333..5e062fde7 100644 --- a/libmemunreachable/MemUnreachable.cpp +++ b/libmemunreachable/MemUnreachable.cpp @@ -28,6 +28,7 @@ #include <backtrace.h> #include "Allocator.h" +#include "Binder.h" #include "HeapWalker.h" #include "Leak.h" #include "LeakFolding.h" @@ -53,7 +54,8 @@ class MemUnreachable { MemUnreachable(pid_t pid, Allocator<void> allocator) : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {} bool CollectAllocations(const allocator::vector<ThreadInfo>& threads, - const allocator::vector<Mapping>& mappings); + const allocator::vector<Mapping>& mappings, + const allocator::vector<uintptr_t>& refs); bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks, size_t* leak_bytes); size_t Allocations() { return heap_walker_.Allocations(); } @@ -82,7 +84,8 @@ static void HeapIterate(const Mapping& heap_mapping, } bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads, - const allocator::vector<Mapping>& mappings) { + const allocator::vector<Mapping>& mappings, + const allocator::vector<uintptr_t>& refs) { MEM_ALOGI("searching process %d for allocations", pid_); allocator::vector<Mapping> heap_mappings{mappings}; allocator::vector<Mapping> anon_mappings{mappings}; @@ -118,6 +121,8 @@ bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& thr heap_walker_.Root(thread_it->regs); } + heap_walker_.Root(refs); + MEM_ALOGI("searching done"); return true; @@ -282,6 +287,7 @@ bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) { ThreadCapture thread_capture(parent_pid, heap); allocator::vector<ThreadInfo> thread_info(heap); allocator::vector<Mapping> mappings(heap); + allocator::vector<uintptr_t> refs(heap); // ptrace all the threads if (!thread_capture.CaptureThreads()) { @@ -301,6 +307,11 @@ bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) { return 1; } + if (!BinderReferences(refs)) { + continue_parent_sem.Post(); + return 1; + } + // malloc must be enabled to call fork, at_fork handlers take the same // locks as ScopedDisableMalloc. All threads are paused in ptrace, so // memory state is still consistent. Unfreeze the original thread so it @@ -326,7 +337,7 @@ bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) { MemUnreachable unreachable{parent_pid, heap}; - if (!unreachable.CollectAllocations(thread_info, mappings)) { + if (!unreachable.CollectAllocations(thread_info, mappings, refs)) { _exit(2); } size_t num_allocations = unreachable.Allocations(); diff --git a/libmemunreachable/tests/Binder_test.cpp b/libmemunreachable/tests/Binder_test.cpp new file mode 100644 index 000000000..6e85d5ab3 --- /dev/null +++ b/libmemunreachable/tests/Binder_test.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 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 <signal.h> +#include <sys/types.h> +#include <unistd.h> + +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/IServiceManager.h> +#include <binder/Parcel.h> +#include <binder/ProcessState.h> + +#include <gtest/gtest.h> + +#include "Allocator.h" +#include "Binder.h" + +namespace android { + +static const String16 service_name("test.libmemunreachable_binder"); + +class BinderService : public BBinder { + public: + BinderService() = default; + virtual ~BinderService() = default; + + virtual status_t onTransact(uint32_t /*code*/, const Parcel& data, Parcel* reply, + uint32_t /*flags*/ = 0) { + reply->writeStrongBinder(ref); + ref = data.readStrongBinder(); + return 0; + } + + private: + sp<IBinder> ref; +}; + +class BinderObject : public BBinder { + public: + BinderObject() = default; + ~BinderObject() = default; +}; + +class ServiceProcess { + public: + ServiceProcess() : child_(0) {} + ~ServiceProcess() { Stop(); } + + bool Run() { + pid_t ret = fork(); + if (ret < 0) { + return false; + } else if (ret == 0) { + // child + _exit(Service()); + } else { + // parent + child_ = ret; + return true; + } + } + + bool Stop() { + if (child_ > 0) { + if (kill(child_, SIGTERM)) { + return false; + } + int status = 0; + if (TEMP_FAILURE_RETRY(waitpid(child_, &status, 0)) != child_) { + return false; + } + child_ = 0; + return WIFEXITED(status) && WEXITSTATUS(status) == 0; + } + + return true; + } + + int Service() { + sp<ProcessState> proc{ProcessState::self()}; + sp<IServiceManager> sm = defaultServiceManager(); + if (sm == nullptr) { + fprintf(stderr, "Failed to get service manager\n"); + return 1; + } + if (sm->addService(service_name, new BinderService()) != OK) { + fprintf(stderr, "Failed to add test service\n"); + return 1; + } + proc->startThreadPool(); + pause(); + return 0; + } + + private: + pid_t child_; +}; + +class BinderTest : public ::testing::Test { + protected: + ServiceProcess service_process_; +}; + +TEST_F(BinderTest, binder) { + ServiceProcess service_process; + ASSERT_TRUE(service_process.Run()); + + sp<IServiceManager> sm = defaultServiceManager(); + ASSERT_TRUE(sm != nullptr); + + // A small sleep allows the service to start, which + // prevents a longer sleep in getService. + usleep(100000); + + sp<IBinder> service = sm->getService(service_name); + ASSERT_TRUE(service != nullptr); + + sp<IBinder> binder{new BinderObject()}; + + Parcel send; + Parcel reply; + + send.writeStrongBinder(binder); + status_t rv = service->transact(0, send, &reply); + ASSERT_EQ(static_cast<status_t>(OK), rv); + + Heap heap; + allocator::vector<uintptr_t> refs{heap}; + + ASSERT_TRUE(BinderReferences(refs)); + + bool found_ref = false; + for (auto ref : refs) { + if (ref == reinterpret_cast<uintptr_t>(binder.get())) { + found_ref = true; + } + } + + ASSERT_TRUE(found_ref); +} + +} // namespace android |