[go: nahoru, domu]

am 6a8c3164: am e35cba9a: am a97798af: Merge "Create an UnwindMapLocal object."

* commit '6a8c316438032c72ffdd2988cc1eb2717f477254':
  Create an UnwindMapLocal object.

Cr-Mirrored-From: https://chromium.googlesource.com/aosp/platform/system/core
Cr-Mirrored-Commit: 7962e4acc735c7083ea060661b847e818d0a79a7
diff --git a/Android.mk b/Android.mk
index 2e56756..c743077 100755
--- a/Android.mk
+++ b/Android.mk
@@ -129,9 +129,11 @@
 
 backtrace_test_cflags_target := \
 	-DGTEST_OS_LINUX_ANDROID \
+	-DENABLE_PSS_TESTS \
 
 backtrace_test_src_files := \
 	backtrace_test.cpp \
+	GetPss.cpp \
 	thread_utils.c \
 
 backtrace_test_ldlibs := \
diff --git a/BacktraceImpl.cpp b/BacktraceImpl.cpp
index 855810e..05007d9 100644
--- a/BacktraceImpl.cpp
+++ b/BacktraceImpl.cpp
@@ -27,6 +27,7 @@
 #include <backtrace/BacktraceMap.h>
 
 #include "BacktraceImpl.h"
+#include "BacktraceLog.h"
 #include "thread_utils.h"
 
 //-------------------------------------------------------------------------
diff --git a/BacktraceImpl.h b/BacktraceImpl.h
index 48dd11c..7b31c38 100755
--- a/BacktraceImpl.h
+++ b/BacktraceImpl.h
@@ -21,11 +21,6 @@
 #include <backtrace/BacktraceMap.h>
 
 #include <sys/types.h>
-#include <log/log.h>
-
-// Macro to log the function name along with the warning message.
-#define BACK_LOGW(format, ...) \
-  ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
 
 class BacktraceImpl {
 public:
diff --git a/BacktraceLog.h b/BacktraceLog.h
new file mode 100755
index 0000000..1632ec2
--- /dev/null
+++ b/BacktraceLog.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014 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 _LIBBACKTRACE_BACKTRACE_LOG_H
+#define _LIBBACKTRACE_BACKTRACE_LOG_H
+
+#define LOG_TAG "libbacktrace"
+
+#include <log/log.h>
+
+// Macro to log the function name along with the warning message.
+#define BACK_LOGW(format, ...) \
+  ALOGW("%s: " format, __PRETTY_FUNCTION__, ##__VA_ARGS__)
+
+#endif // _LIBBACKTRACE_BACKTRACE_LOG_H
diff --git a/BacktraceThread.cpp b/BacktraceThread.cpp
index 5ffe516..4cda19e 100644
--- a/BacktraceThread.cpp
+++ b/BacktraceThread.cpp
@@ -23,6 +23,7 @@
 
 #include <cutils/atomic.h>
 
+#include "BacktraceLog.h"
 #include "BacktraceThread.h"
 #include "thread_utils.h"
 
diff --git a/Corkscrew.cpp b/Corkscrew.cpp
index efeee2e..773b0a2 100644
--- a/Corkscrew.cpp
+++ b/Corkscrew.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <backtrace/Backtrace.h>
 
 #include <string.h>
@@ -28,6 +26,7 @@
 #endif
 #include <dlfcn.h>
 
+#include "BacktraceLog.h"
 #include "Corkscrew.h"
 
 //-------------------------------------------------------------------------
diff --git a/GetPss.cpp b/GetPss.cpp
new file mode 100644
index 0000000..442383b
--- /dev/null
+++ b/GetPss.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 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 <assert.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// This is an extremely simplified version of libpagemap.
+
+#define _BITS(x, offset, bits) (((x) >> offset) & ((1LL << (bits)) - 1))
+
+#define PAGEMAP_PRESENT(x)     (_BITS(x, 63, 1))
+#define PAGEMAP_SWAPPED(x)     (_BITS(x, 62, 1))
+#define PAGEMAP_SHIFT(x)       (_BITS(x, 55, 6))
+#define PAGEMAP_PFN(x)         (_BITS(x, 0, 55))
+#define PAGEMAP_SWAP_OFFSET(x) (_BITS(x, 5, 50))
+#define PAGEMAP_SWAP_TYPE(x)   (_BITS(x, 0,  5))
+
+static bool ReadData(int fd, unsigned long place, uint64_t *data) {
+  if (lseek(fd, place * sizeof(uint64_t), SEEK_SET) < 0) {
+    return false;
+  }
+  if (read(fd, (void*)data, sizeof(uint64_t)) != (ssize_t)sizeof(uint64_t)) {
+    return false;
+  }
+  return true;
+}
+
+size_t GetPssBytes() {
+  FILE* maps = fopen("/proc/self/maps", "r");
+  assert(maps != NULL);
+
+  int pagecount_fd = open("/proc/kpagecount", O_RDONLY);
+  assert(pagecount_fd >= 0);
+
+  int pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
+  assert(pagemap_fd >= 0);
+
+  char line[4096];
+  size_t total_pss = 0;
+  int pagesize = getpagesize();
+  while (fgets(line, sizeof(line), maps)) {
+    uintptr_t start, end;
+    if (sscanf(line, "%" SCNxPTR "-%" SCNxPTR " ", &start, &end) != 2) {
+      total_pss = 0;
+      break;
+    }
+    for (size_t page = start/pagesize; page < end/pagesize; page++) {
+      uint64_t data;
+      if (ReadData(pagemap_fd, page, &data)) {
+        if (PAGEMAP_PRESENT(data) && !PAGEMAP_SWAPPED(data)) {
+          uint64_t count;
+          if (ReadData(pagecount_fd, PAGEMAP_PFN(data), &count)) {
+            total_pss += (count >= 1) ? pagesize / count : 0;
+          }
+        }
+      }
+    }
+  }
+
+  fclose(maps);
+
+  close(pagecount_fd);
+  close(pagemap_fd);
+
+  return total_pss;
+}
diff --git a/GetPss.h b/GetPss.h
new file mode 100644
index 0000000..787c33d
--- /dev/null
+++ b/GetPss.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014 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 _LIBBACKTRACE_GET_PSS_H
+#define _LIBBACKTRACE_GET_PSS_H
+
+size_t GetPssBytes();
+
+#endif // _LIBBACKTRACE_GET_PSS_H
diff --git a/UnwindCurrent.cpp b/UnwindCurrent.cpp
index 81e69bb..034b73c 100755
--- a/UnwindCurrent.cpp
+++ b/UnwindCurrent.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <sys/ucontext.h>
 #include <sys/types.h>
 
@@ -25,6 +23,7 @@
 #define UNW_LOCAL_ONLY
 #include <libunwind.h>
 
+#include "BacktraceLog.h"
 #include "UnwindCurrent.h"
 #include "UnwindMap.h"
 
diff --git a/UnwindMap.cpp b/UnwindMap.cpp
index 8268db6..1615518 100644
--- a/UnwindMap.cpp
+++ b/UnwindMap.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <pthread.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -24,6 +22,7 @@
 
 #include <libunwind.h>
 
+#include "BacktraceLog.h"
 #include "UnwindMap.h"
 
 //-------------------------------------------------------------------------
@@ -32,57 +31,21 @@
 // only update the local address space once, and keep a reference count
 // of maps using the same map cursor.
 //-------------------------------------------------------------------------
-static pthread_mutex_t g_map_mutex = PTHREAD_MUTEX_INITIALIZER;
-static unw_map_cursor_t g_map_cursor;
-static int g_map_references = 0;
-
 UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
-  map_cursor_.map_list = NULL;
 }
 
 UnwindMap::~UnwindMap() {
-  if (pid_ == getpid()) {
-    pthread_mutex_lock(&g_map_mutex);
-    if (--g_map_references == 0) {
-      // Clear the local address space map.
-      unw_map_local_set(NULL);
-      unw_map_cursor_destroy(&map_cursor_);
-    }
-    pthread_mutex_unlock(&g_map_mutex);
-  } else {
-    unw_map_cursor_destroy(&map_cursor_);
-  }
+  unw_map_cursor_destroy(&map_cursor_);
+  unw_map_cursor_clear(&map_cursor_);
 }
 
-bool UnwindMap::Build() {
-  bool return_value = true;
-  if (pid_ == getpid()) {
-    pthread_mutex_lock(&g_map_mutex);
-    if (g_map_references == 0) {
-      return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
-      if (return_value) {
-        // Set the local address space map to our new map.
-        unw_map_local_set(&map_cursor_);
-        g_map_references = 1;
-        g_map_cursor = map_cursor_;
-      }
-    } else {
-      g_map_references++;
-      map_cursor_ = g_map_cursor;
-    }
-    pthread_mutex_unlock(&g_map_mutex);
-  } else {
-    return_value = (unw_map_cursor_create(&map_cursor_, pid_) == 0);
-  }
-
-  if (!return_value)
-    return false;
-
+bool UnwindMap::GenerateMap() {
   // Use the map_cursor information to construct the BacktraceMap data
   // rather than reparsing /proc/self/maps.
   unw_map_cursor_reset(&map_cursor_);
+
   unw_map_t unw_map;
-  while (unw_map_cursor_get(&map_cursor_, &unw_map)) {
+  while (unw_map_cursor_get_next(&map_cursor_, &unw_map)) {
     backtrace_map_t map;
 
     map.start = unw_map.start;
@@ -97,11 +60,82 @@
   return true;
 }
 
+bool UnwindMap::Build() {
+  return (unw_map_cursor_create(&map_cursor_, pid_) == 0) && GenerateMap();
+}
+
+UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+}
+
+UnwindMapLocal::~UnwindMapLocal() {
+  if (map_created_) {
+    unw_map_local_destroy();
+    unw_map_cursor_clear(&map_cursor_);
+  }
+}
+
+bool UnwindMapLocal::GenerateMap() {
+  // It's possible for the map to be regenerated while this loop is occurring.
+  // If that happens, get the map again, but only try at most three times
+  // before giving up.
+  for (int i = 0; i < 3; i++) {
+    maps_.clear();
+
+    unw_map_local_cursor_get(&map_cursor_);
+
+    unw_map_t unw_map;
+    int ret;
+    while ((ret = unw_map_local_cursor_get_next(&map_cursor_, &unw_map)) > 0) {
+      backtrace_map_t map;
+
+      map.start = unw_map.start;
+      map.end = unw_map.end;
+      map.flags = unw_map.flags;
+      map.name = unw_map.path;
+
+      free(unw_map.path);
+
+      // The maps are in descending order, but we want them in ascending order.
+      maps_.push_front(map);
+    }
+    // Check to see if the map changed while getting the data.
+    if (ret != -UNW_EINVAL) {
+      return true;
+    }
+  }
+
+  BACK_LOGW("Unable to generate the map.");
+  return false;
+}
+
+bool UnwindMapLocal::Build() {
+  return (map_created_ = (unw_map_local_create() == 0)) && GenerateMap();;
+}
+
+const backtrace_map_t* UnwindMapLocal::Find(uintptr_t addr) {
+  const backtrace_map_t* map = BacktraceMap::Find(addr);
+  if (!map) {
+    // Check to see if the underlying map changed and regenerate the map
+    // if it did.
+    if (unw_map_local_cursor_valid(&map_cursor_) < 0) {
+      if (GenerateMap()) {
+        map = BacktraceMap::Find(addr);
+      }
+    }
+  }
+  return map;
+}
+
 //-------------------------------------------------------------------------
 // BacktraceMap create function.
 //-------------------------------------------------------------------------
 BacktraceMap* BacktraceMap::Create(pid_t pid) {
-  BacktraceMap* map = new UnwindMap(pid);
+  BacktraceMap* map;
+  if (pid == getpid()) {
+    map = new UnwindMapLocal();
+  } else {
+    map = new UnwindMap(pid);
+  }
   if (!map->Build()) {
     delete map;
     return NULL;
diff --git a/UnwindMap.h b/UnwindMap.h
index 5a874e8..2fdb29f 100644
--- a/UnwindMap.h
+++ b/UnwindMap.h
@@ -32,8 +32,25 @@
 
   unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
 
-private:
+protected:
+  virtual bool GenerateMap();
+
   unw_map_cursor_t map_cursor_;
 };
 
+class UnwindMapLocal : public UnwindMap {
+public:
+  UnwindMapLocal();
+  virtual ~UnwindMapLocal();
+
+  virtual bool Build();
+
+  virtual const backtrace_map_t* Find(uintptr_t addr);
+
+protected:
+  virtual bool GenerateMap();
+
+  bool map_created_;
+};
+
 #endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/UnwindPtrace.cpp b/UnwindPtrace.cpp
index 732dae8..5ca7e60 100644
--- a/UnwindPtrace.cpp
+++ b/UnwindPtrace.cpp
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "libbacktrace"
-
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 
@@ -25,6 +23,7 @@
 #include <libunwind.h>
 #include <libunwind-ptrace.h>
 
+#include "BacktraceLog.h"
 #include "UnwindMap.h"
 #include "UnwindPtrace.h"
 
diff --git a/backtrace_test.cpp b/backtrace_test.cpp
index 23eaf92..a5e141b 100644
--- a/backtrace_test.cpp
+++ b/backtrace_test.cpp
@@ -16,9 +16,10 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <pthread.h>
 #include <signal.h>
-#include <stdbool.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -35,6 +36,7 @@
 #include <cutils/atomic.h>
 #include <gtest/gtest.h>
 
+#include <algorithm>
 #include <vector>
 
 #include "thread_utils.h"
@@ -287,7 +289,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
 
@@ -300,7 +302,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
 
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
@@ -314,7 +316,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
 
@@ -339,7 +341,7 @@
   pid_t pid;
   if ((pid = fork()) == 0) {
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
   VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
 
@@ -384,7 +386,7 @@
       ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
     }
     ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
-    exit(1);
+    _exit(1);
   }
 
   // Check to see that all of the threads are running before unwinding.
@@ -693,3 +695,136 @@
 #endif
             backtrace->FormatFrameData(&frame));
 }
+
+struct map_test_t {
+  uintptr_t start;
+  uintptr_t end;
+};
+
+bool map_sort(map_test_t i, map_test_t j) {
+  return i.start < j.start;
+}
+
+static void VerifyMap(pid_t pid) {
+  char buffer[4096];
+  snprintf(buffer, sizeof(buffer), "/proc/%d/maps", pid);
+
+  FILE* map_file = fopen(buffer, "r");
+  ASSERT_TRUE(map_file != NULL);
+  std::vector<map_test_t> test_maps;
+  while (fgets(buffer, sizeof(buffer), map_file)) {
+    map_test_t map;
+    ASSERT_EQ(2, sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " ", &map.start, &map.end));
+    test_maps.push_back(map);
+  }
+  fclose(map_file);
+  std::sort(test_maps.begin(), test_maps.end(), map_sort);
+
+  UniquePtr<BacktraceMap> map(BacktraceMap::Create(pid));
+
+  // Basic test that verifies that the map is in the expected order.
+  std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
+  for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
+    ASSERT_TRUE(test_it != test_maps.end());
+    ASSERT_EQ(test_it->start, it->start);
+    ASSERT_EQ(test_it->end, it->end);
+    ++test_it;
+  }
+  ASSERT_TRUE(test_it == test_maps.end());
+}
+
+TEST(libbacktrace, verify_map_remote) {
+  pid_t pid;
+
+  if ((pid = fork()) == 0) {
+    while (true) {
+    }
+    _exit(0);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+  // Wait for the process to get to a stopping point.
+  WaitForStop(pid);
+
+  // The maps should match exactly since the forked process has been paused.
+  VerifyMap(pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+  ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+
+#if defined(ENABLE_PSS_TESTS)
+#include "GetPss.h"
+
+#define MAX_LEAK_BYTES 32*1024UL
+
+static void CheckForLeak(pid_t pid, pid_t tid) {
+  // Do a few runs to get the PSS stable.
+  for (size_t i = 0; i < 100; i++) {
+    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    ASSERT_TRUE(backtrace != NULL);
+    ASSERT_TRUE(backtrace->Unwind(0));
+    delete backtrace;
+  }
+  size_t stable_pss = GetPssBytes();
+
+  // Loop enough that even a small leak should be detectable.
+  for (size_t i = 0; i < 4096; i++) {
+    Backtrace* backtrace = Backtrace::Create(pid, tid);
+    ASSERT_TRUE(backtrace != NULL);
+    ASSERT_TRUE(backtrace->Unwind(0));
+    delete backtrace;
+  }
+  size_t new_pss = GetPssBytes();
+  size_t abs_diff = (new_pss > stable_pss) ? new_pss - stable_pss : stable_pss - new_pss;
+  // As long as the new pss is within a certain amount, consider everything okay.
+  ASSERT_LE(abs_diff, MAX_LEAK_BYTES);
+}
+
+TEST(libbacktrace, check_for_leak_local) {
+  CheckForLeak(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD);
+}
+
+TEST(libbacktrace, check_for_leak_local_thread) {
+  thread_t thread_data = { 0, 0, 0 };
+  pthread_t thread;
+  ASSERT_TRUE(pthread_create(&thread, NULL, ThreadLevelRun, &thread_data) == 0);
+
+  // Wait up to 2 seconds for the tid to be set.
+  ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
+
+  CheckForLeak(BACKTRACE_CURRENT_PROCESS, thread_data.tid);
+
+  // Tell the thread to exit its infinite loop.
+  android_atomic_acquire_store(0, &thread_data.state);
+
+  ASSERT_TRUE(pthread_join(thread, NULL) == 0);
+}
+
+TEST(libbacktrace, check_for_leak_remote) {
+  pid_t pid;
+
+  if ((pid = fork()) == 0) {
+    while (true) {
+    }
+    _exit(0);
+  }
+  ASSERT_LT(0, pid);
+
+  ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+  // Wait for the process to get to a stopping point.
+  WaitForStop(pid);
+
+  CheckForLeak(pid, BACKTRACE_CURRENT_THREAD);
+
+  ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+  kill(pid, SIGKILL);
+  ASSERT_EQ(waitpid(pid, NULL, 0), pid);
+}
+#endif