Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 028aca2d authored by Colin Cross's avatar Colin Cross Committed by android-build-merger
Browse files

Merge "libmemunreachable: track kernel binder references"

am: 454285df

Change-Id: I2c30d94b6e628a637f59c353d862dc16cdf528e0
parents f2a35db7 454285df
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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_
+14 −3
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
/*
 * 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