Merge "libmemunreachable: track kernel binder references" am: 454285dfcc
am: 028aca2d06
Change-Id: Ic4ba34dbe304a462928ff132d4897a26cef14c57
This commit is contained in:
commit
d5a42720a5
5 changed files with 294 additions and 3 deletions
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
80
libmemunreachable/Binder.cpp
Normal file
80
libmemunreachable/Binder.cpp
Normal 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
|
28
libmemunreachable/Binder.h
Normal file
28
libmemunreachable/Binder.h
Normal 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_
|
|
@ -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();
|
||||
|
|
156
libmemunreachable/tests/Binder_test.cpp
Normal file
156
libmemunreachable/tests/Binder_test.cpp
Normal 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
|
Loading…
Reference in a new issue