summaryrefslogtreecommitdiffstats
path: root/libmemunreachable
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2017-07-13 19:41:26 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2017-07-13 19:41:26 +0000
commit454285dfcc6467a4745017134458fdc16c1c0220 (patch)
tree1bb58447a2c8cb32dc072115ef0510db3cab6e2d /libmemunreachable
parentf42d02830de827096150fea42e99c55fdb2b7df7 (diff)
parentf572b91c4f5acf739742da53ae3406e615c2d756 (diff)
downloadsystem_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.bp16
-rw-r--r--libmemunreachable/Binder.cpp80
-rw-r--r--libmemunreachable/Binder.h28
-rw-r--r--libmemunreachable/MemUnreachable.cpp17
-rw-r--r--libmemunreachable/tests/Binder_test.cpp156
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