Merge "libmemunreachable: track kernel binder references" am: 454285dfcc

am: 028aca2d06

Change-Id: Ic4ba34dbe304a462928ff132d4897a26cef14c57
This commit is contained in:
Colin Cross 2017-07-13 20:04:10 +00:00 committed by android-build-merger
commit d5a42720a5
5 changed files with 294 additions and 3 deletions

View file

@ -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",
],
}

View file

@ -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

View file

@ -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_

View file

@ -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();

View file

@ -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