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 +#include + +#include + +#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& refs, + std::function 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& refs) { + refs.clear(); + + allocator::vector 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 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& 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 #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 allocator) : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {} bool CollectAllocations(const allocator::vector& threads, - const allocator::vector& mappings); + const allocator::vector& mappings, + const allocator::vector& refs); bool GetUnreachableMemory(allocator::vector& 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& threads, - const allocator::vector& mappings) { + const allocator::vector& mappings, + const allocator::vector& refs) { MEM_ALOGI("searching process %d for allocations", pid_); allocator::vector heap_mappings{mappings}; allocator::vector anon_mappings{mappings}; @@ -118,6 +121,8 @@ bool MemUnreachable::CollectAllocations(const allocator::vector& 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 thread_info(heap); allocator::vector mappings(heap); + allocator::vector 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 +#include +#include + +#include +#include +#include +#include +#include + +#include + +#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 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 proc{ProcessState::self()}; + sp 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 sm = defaultServiceManager(); + ASSERT_TRUE(sm != nullptr); + + // A small sleep allows the service to start, which + // prevents a longer sleep in getService. + usleep(100000); + + sp service = sm->getService(service_name); + ASSERT_TRUE(service != nullptr); + + sp binder{new BinderObject()}; + + Parcel send; + Parcel reply; + + send.writeStrongBinder(binder); + status_t rv = service->transact(0, send, &reply); + ASSERT_EQ(static_cast(OK), rv); + + Heap heap; + allocator::vector refs{heap}; + + ASSERT_TRUE(BinderReferences(refs)); + + bool found_ref = false; + for (auto ref : refs) { + if (ref == reinterpret_cast(binder.get())) { + found_ref = true; + } + } + + ASSERT_TRUE(found_ref); +} + +} // namespace android