am a97798af: Merge "Create an UnwindMapLocal object."
* commit 'a97798afc9105668a80b775516a7cc4db341a62e': Create an UnwindMapLocal object.
This commit is contained in:
commit
e35cba9af0
14 changed files with 380 additions and 63 deletions
|
|
@ -45,7 +45,7 @@ public:
|
||||||
virtual ~BacktraceMap();
|
virtual ~BacktraceMap();
|
||||||
|
|
||||||
// Get the map data structure for the given address.
|
// Get the map data structure for the given address.
|
||||||
const backtrace_map_t* Find(uintptr_t addr);
|
virtual const backtrace_map_t* Find(uintptr_t addr);
|
||||||
|
|
||||||
// The flags returned are the same flags as used by the mmap call.
|
// The flags returned are the same flags as used by the mmap call.
|
||||||
// The values are PROT_*.
|
// The values are PROT_*.
|
||||||
|
|
|
||||||
|
|
@ -129,9 +129,11 @@ endif # arm64
|
||||||
|
|
||||||
backtrace_test_cflags_target := \
|
backtrace_test_cflags_target := \
|
||||||
-DGTEST_OS_LINUX_ANDROID \
|
-DGTEST_OS_LINUX_ANDROID \
|
||||||
|
-DENABLE_PSS_TESTS \
|
||||||
|
|
||||||
backtrace_test_src_files := \
|
backtrace_test_src_files := \
|
||||||
backtrace_test.cpp \
|
backtrace_test.cpp \
|
||||||
|
GetPss.cpp \
|
||||||
thread_utils.c \
|
thread_utils.c \
|
||||||
|
|
||||||
backtrace_test_ldlibs := \
|
backtrace_test_ldlibs := \
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <backtrace/BacktraceMap.h>
|
#include <backtrace/BacktraceMap.h>
|
||||||
|
|
||||||
#include "BacktraceImpl.h"
|
#include "BacktraceImpl.h"
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "thread_utils.h"
|
#include "thread_utils.h"
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -21,11 +21,6 @@
|
||||||
#include <backtrace/BacktraceMap.h>
|
#include <backtrace/BacktraceMap.h>
|
||||||
|
|
||||||
#include <sys/types.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 {
|
class BacktraceImpl {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
28
libbacktrace/BacktraceLog.h
Executable file
28
libbacktrace/BacktraceLog.h
Executable file
|
|
@ -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
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <cutils/atomic.h>
|
#include <cutils/atomic.h>
|
||||||
|
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "BacktraceThread.h"
|
#include "BacktraceThread.h"
|
||||||
#include "thread_utils.h"
|
#include "thread_utils.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <backtrace/Backtrace.h>
|
#include <backtrace/Backtrace.h>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -28,6 +26,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "Corkscrew.h"
|
#include "Corkscrew.h"
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
|
||||||
85
libbacktrace/GetPss.cpp
Normal file
85
libbacktrace/GetPss.cpp
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
22
libbacktrace/GetPss.h
Normal file
22
libbacktrace/GetPss.h
Normal file
|
|
@ -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
|
||||||
|
|
@ -14,8 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <sys/ucontext.h>
|
#include <sys/ucontext.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
|
@ -25,6 +23,7 @@
|
||||||
#define UNW_LOCAL_ONLY
|
#define UNW_LOCAL_ONLY
|
||||||
#include <libunwind.h>
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "UnwindCurrent.h"
|
#include "UnwindCurrent.h"
|
||||||
#include "UnwindMap.h"
|
#include "UnwindMap.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
@ -24,6 +22,7 @@
|
||||||
|
|
||||||
#include <libunwind.h>
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "UnwindMap.h"
|
#include "UnwindMap.h"
|
||||||
|
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
|
|
@ -32,57 +31,21 @@
|
||||||
// only update the local address space once, and keep a reference count
|
// only update the local address space once, and keep a reference count
|
||||||
// of maps using the same map cursor.
|
// 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) {
|
UnwindMap::UnwindMap(pid_t pid) : BacktraceMap(pid) {
|
||||||
map_cursor_.map_list = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UnwindMap::~UnwindMap() {
|
UnwindMap::~UnwindMap() {
|
||||||
if (pid_ == getpid()) {
|
unw_map_cursor_destroy(&map_cursor_);
|
||||||
pthread_mutex_lock(&g_map_mutex);
|
unw_map_cursor_clear(&map_cursor_);
|
||||||
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_);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnwindMap::Build() {
|
bool UnwindMap::GenerateMap() {
|
||||||
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;
|
|
||||||
|
|
||||||
// Use the map_cursor information to construct the BacktraceMap data
|
// Use the map_cursor information to construct the BacktraceMap data
|
||||||
// rather than reparsing /proc/self/maps.
|
// rather than reparsing /proc/self/maps.
|
||||||
unw_map_cursor_reset(&map_cursor_);
|
unw_map_cursor_reset(&map_cursor_);
|
||||||
|
|
||||||
unw_map_t unw_map;
|
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;
|
backtrace_map_t map;
|
||||||
|
|
||||||
map.start = unw_map.start;
|
map.start = unw_map.start;
|
||||||
|
|
@ -97,11 +60,82 @@ bool UnwindMap::Build() {
|
||||||
return true;
|
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 create function.
|
||||||
//-------------------------------------------------------------------------
|
//-------------------------------------------------------------------------
|
||||||
BacktraceMap* BacktraceMap::Create(pid_t pid) {
|
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()) {
|
if (!map->Build()) {
|
||||||
delete map;
|
delete map;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,25 @@ public:
|
||||||
|
|
||||||
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
|
unw_map_cursor_t* GetMapCursor() { return &map_cursor_; }
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
virtual bool GenerateMap();
|
||||||
|
|
||||||
unw_map_cursor_t map_cursor_;
|
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
|
#endif // _LIBBACKTRACE_UNWIND_MAP_H
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <backtrace/Backtrace.h>
|
#include <backtrace/Backtrace.h>
|
||||||
#include <backtrace/BacktraceMap.h>
|
#include <backtrace/BacktraceMap.h>
|
||||||
|
|
||||||
|
|
@ -25,6 +23,7 @@
|
||||||
#include <libunwind.h>
|
#include <libunwind.h>
|
||||||
#include <libunwind-ptrace.h>
|
#include <libunwind-ptrace.h>
|
||||||
|
|
||||||
|
#include "BacktraceLog.h"
|
||||||
#include "UnwindMap.h"
|
#include "UnwindMap.h"
|
||||||
#include "UnwindPtrace.h"
|
#include "UnwindPtrace.h"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,10 @@
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdbool.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
@ -35,6 +36,7 @@
|
||||||
#include <cutils/atomic.h>
|
#include <cutils/atomic.h>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "thread_utils.h"
|
#include "thread_utils.h"
|
||||||
|
|
@ -287,7 +289,7 @@ TEST(libbacktrace, ptrace_trace) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 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);
|
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyLevelDump);
|
||||||
|
|
||||||
|
|
@ -300,7 +302,7 @@ TEST(libbacktrace, ptrace_trace_shared_map) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 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);
|
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, true, ReadyLevelBacktrace, VerifyLevelDump);
|
||||||
|
|
@ -314,7 +316,7 @@ TEST(libbacktrace, ptrace_max_trace) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 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);
|
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyMaxBacktrace, VerifyMaxDump);
|
||||||
|
|
||||||
|
|
@ -339,7 +341,7 @@ TEST(libbacktrace, ptrace_ignore_frames) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
if ((pid = fork()) == 0) {
|
if ((pid = fork()) == 0) {
|
||||||
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 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);
|
VerifyProcTest(pid, BACKTRACE_CURRENT_THREAD, false, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
|
||||||
|
|
||||||
|
|
@ -384,7 +386,7 @@ TEST(libbacktrace, ptrace_threads) {
|
||||||
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
|
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
|
||||||
}
|
}
|
||||||
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, 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.
|
// Check to see that all of the threads are running before unwinding.
|
||||||
|
|
@ -693,3 +695,136 @@ TEST(libbacktrace, format_test) {
|
||||||
#endif
|
#endif
|
||||||
backtrace->FormatFrameData(&frame));
|
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
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue