Merge "Rewrite libbacktrace using C++."
This commit is contained in:
commit
98f87d92d8
28 changed files with 2402 additions and 1063 deletions
|
|
@ -89,12 +89,12 @@ static void dump_thread(log_t* log, pid_t tid, bool attached,
|
||||||
|
|
||||||
wait_for_stop(tid, total_sleep_time_usec);
|
wait_for_stop(tid, total_sleep_time_usec);
|
||||||
|
|
||||||
backtrace_t backtrace;
|
backtrace_context_t context;
|
||||||
if (!backtrace_get_data(&backtrace, tid)) {
|
if (!backtrace_create_context(&context, tid, -1, 0)) {
|
||||||
_LOG(log, SCOPE_AT_FAULT, "Could not create backtrace context.\n");
|
_LOG(log, SCOPE_AT_FAULT, "Could not create backtrace context.\n");
|
||||||
} else {
|
} else {
|
||||||
dump_backtrace_to_log(&backtrace, log, SCOPE_AT_FAULT, " ");
|
dump_backtrace_to_log(&context, log, SCOPE_AT_FAULT, " ");
|
||||||
backtrace_free_data(&backtrace);
|
backtrace_destroy_context(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
|
if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
|
||||||
|
|
@ -137,11 +137,11 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
|
||||||
dump_process_footer(&log, pid);
|
dump_process_footer(&log, pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_backtrace_to_log(const backtrace_t* backtrace, log_t* log,
|
void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log,
|
||||||
int scope_flags, const char* prefix) {
|
int scope_flags, const char* prefix) {
|
||||||
char buf[512];
|
char buf[512];
|
||||||
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
for (size_t i = 0; i < context->backtrace->num_frames; i++) {
|
||||||
backtrace_format_frame_data(&backtrace->frames[i], i, buf, sizeof(buf));
|
backtrace_format_frame_data(context, i, buf, sizeof(buf));
|
||||||
_LOG(log, scope_flags, "%s%s\n", prefix, buf);
|
_LOG(log, scope_flags, "%s%s\n", prefix, buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ void dump_backtrace(int fd, int amfd, pid_t pid, pid_t tid, bool* detach_failed,
|
||||||
int* total_sleep_time_usec);
|
int* total_sleep_time_usec);
|
||||||
|
|
||||||
/* Dumps the backtrace in the backtrace data structure to the log. */
|
/* Dumps the backtrace in the backtrace data structure to the log. */
|
||||||
void dump_backtrace_to_log(const backtrace_t* backtrace, log_t* log,
|
void dump_backtrace_to_log(const backtrace_context_t* context, log_t* log,
|
||||||
int scope_flags, const char* prefix);
|
int scope_flags, const char* prefix);
|
||||||
|
|
||||||
#endif // _DEBUGGERD_BACKTRACE_H
|
#endif // _DEBUGGERD_BACKTRACE_H
|
||||||
|
|
|
||||||
|
|
@ -228,39 +228,39 @@ static void dump_thread_info(log_t* log, pid_t pid, pid_t tid, int scope_flags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_stack_segment(const backtrace_t* backtrace, log_t* log,
|
static void dump_stack_segment(const backtrace_context_t* context, log_t* log,
|
||||||
int scope_flags, uintptr_t *sp, size_t words, int label) {
|
int scope_flags, uintptr_t *sp, size_t words, int label) {
|
||||||
for (size_t i = 0; i < words; i++) {
|
for (size_t i = 0; i < words; i++) {
|
||||||
uint32_t stack_content;
|
uint32_t stack_content;
|
||||||
if (!backtrace_read_word(backtrace, *sp, &stack_content)) {
|
if (!backtrace_read_word(context, *sp, &stack_content)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* map_name = backtrace_get_map_info(backtrace, stack_content, NULL);
|
const char* map_name = backtrace_get_map_name(context, stack_content, NULL);
|
||||||
if (!map_name) {
|
if (!map_name) {
|
||||||
map_name = "";
|
map_name = "";
|
||||||
}
|
}
|
||||||
uintptr_t offset = 0;
|
uintptr_t offset = 0;
|
||||||
char* proc_name = backtrace_get_proc_name(backtrace, stack_content, &offset);
|
char* func_name = backtrace_get_func_name(context, stack_content, &offset);
|
||||||
if (proc_name) {
|
if (func_name) {
|
||||||
if (!i && label >= 0) {
|
if (!i && label >= 0) {
|
||||||
if (offset) {
|
if (offset) {
|
||||||
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n",
|
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s+%u)\n",
|
||||||
label, *sp, stack_content, map_name, proc_name, offset);
|
label, *sp, stack_content, map_name, func_name, offset);
|
||||||
} else {
|
} else {
|
||||||
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n",
|
_LOG(log, scope_flags, " #%02d %08x %08x %s (%s)\n",
|
||||||
label, *sp, stack_content, map_name, proc_name);
|
label, *sp, stack_content, map_name, func_name);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (offset) {
|
if (offset) {
|
||||||
_LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n",
|
_LOG(log, scope_flags, " %08x %08x %s (%s+%u)\n",
|
||||||
*sp, stack_content, map_name, proc_name, offset);
|
*sp, stack_content, map_name, func_name, offset);
|
||||||
} else {
|
} else {
|
||||||
_LOG(log, scope_flags, " %08x %08x %s (%s)\n",
|
_LOG(log, scope_flags, " %08x %08x %s (%s)\n",
|
||||||
*sp, stack_content, map_name, proc_name);
|
*sp, stack_content, map_name, func_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(proc_name);
|
free(func_name);
|
||||||
} else {
|
} else {
|
||||||
if (!i && label >= 0) {
|
if (!i && label >= 0) {
|
||||||
_LOG(log, scope_flags, " #%02d %08x %08x %s\n",
|
_LOG(log, scope_flags, " #%02d %08x %08x %s\n",
|
||||||
|
|
@ -275,7 +275,8 @@ static void dump_stack_segment(const backtrace_t* backtrace, log_t* log,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags) {
|
static void dump_stack(const backtrace_context_t* context, log_t* log, int scope_flags) {
|
||||||
|
const backtrace_t* backtrace = context->backtrace;
|
||||||
size_t first = 0, last;
|
size_t first = 0, last;
|
||||||
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
||||||
if (backtrace->frames[i].sp) {
|
if (backtrace->frames[i].sp) {
|
||||||
|
|
@ -294,7 +295,7 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
|
||||||
|
|
||||||
// Dump a few words before the first frame.
|
// Dump a few words before the first frame.
|
||||||
uintptr_t sp = backtrace->frames[first].sp - STACK_WORDS * sizeof(uint32_t);
|
uintptr_t sp = backtrace->frames[first].sp - STACK_WORDS * sizeof(uint32_t);
|
||||||
dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, -1);
|
dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, -1);
|
||||||
|
|
||||||
// Dump a few words from all successive frames.
|
// Dump a few words from all successive frames.
|
||||||
// Only log the first 3 frames, put the rest in the tombstone.
|
// Only log the first 3 frames, put the rest in the tombstone.
|
||||||
|
|
@ -308,7 +309,7 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
|
||||||
scope_flags &= (~SCOPE_AT_FAULT);
|
scope_flags &= (~SCOPE_AT_FAULT);
|
||||||
}
|
}
|
||||||
if (i == last) {
|
if (i == last) {
|
||||||
dump_stack_segment(backtrace, log, scope_flags, &sp, STACK_WORDS, i);
|
dump_stack_segment(context, log, scope_flags, &sp, STACK_WORDS, i);
|
||||||
if (sp < frame->sp + frame->stack_size) {
|
if (sp < frame->sp + frame->stack_size) {
|
||||||
_LOG(log, scope_flags, " ........ ........\n");
|
_LOG(log, scope_flags, " ........ ........\n");
|
||||||
}
|
}
|
||||||
|
|
@ -319,19 +320,19 @@ static void dump_stack(const backtrace_t* backtrace, log_t* log, int scope_flags
|
||||||
} else if (words > STACK_WORDS) {
|
} else if (words > STACK_WORDS) {
|
||||||
words = STACK_WORDS;
|
words = STACK_WORDS;
|
||||||
}
|
}
|
||||||
dump_stack_segment(backtrace, log, scope_flags, &sp, words, i);
|
dump_stack_segment(context, log, scope_flags, &sp, words, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_backtrace_and_stack(const backtrace_t* backtrace, log_t* log,
|
static void dump_backtrace_and_stack(const backtrace_context_t* context,
|
||||||
int scope_flags) {
|
log_t* log, int scope_flags) {
|
||||||
if (backtrace->num_frames) {
|
if (context->backtrace->num_frames) {
|
||||||
_LOG(log, scope_flags, "\nbacktrace:\n");
|
_LOG(log, scope_flags, "\nbacktrace:\n");
|
||||||
dump_backtrace_to_log(backtrace, log, scope_flags, " ");
|
dump_backtrace_to_log(context, log, scope_flags, " ");
|
||||||
|
|
||||||
_LOG(log, scope_flags, "\nstack:\n");
|
_LOG(log, scope_flags, "\nstack:\n");
|
||||||
dump_stack(backtrace, log, scope_flags);
|
dump_stack(context, log, scope_flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -399,12 +400,13 @@ static void dump_nearby_maps(const backtrace_map_info_t* map_info_list, log_t* l
|
||||||
dump_map(log, prev, "map above", scope_flags);
|
dump_map(log, prev, "map above", scope_flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_thread(const backtrace_t* backtrace, log_t* log, int scope_flags,
|
static void dump_thread(const backtrace_context_t* context, log_t* log,
|
||||||
int* total_sleep_time_usec) {
|
int scope_flags, int* total_sleep_time_usec) {
|
||||||
|
const backtrace_t* backtrace = context->backtrace;
|
||||||
wait_for_stop(backtrace->tid, total_sleep_time_usec);
|
wait_for_stop(backtrace->tid, total_sleep_time_usec);
|
||||||
|
|
||||||
dump_registers(log, backtrace->tid, scope_flags);
|
dump_registers(log, backtrace->tid, scope_flags);
|
||||||
dump_backtrace_and_stack(backtrace, log, scope_flags);
|
dump_backtrace_and_stack(context, log, scope_flags);
|
||||||
if (IS_AT_FAULT(scope_flags)) {
|
if (IS_AT_FAULT(scope_flags)) {
|
||||||
dump_memory_and_code(log, backtrace->tid, scope_flags);
|
dump_memory_and_code(log, backtrace->tid, scope_flags);
|
||||||
dump_nearby_maps(backtrace->map_info_list, log, backtrace->tid, scope_flags);
|
dump_nearby_maps(backtrace->map_info_list, log, backtrace->tid, scope_flags);
|
||||||
|
|
@ -446,11 +448,11 @@ static bool dump_sibling_thread_report(
|
||||||
|
|
||||||
_LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
|
_LOG(log, 0, "--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---\n");
|
||||||
dump_thread_info(log, pid, new_tid, 0);
|
dump_thread_info(log, pid, new_tid, 0);
|
||||||
backtrace_t new_backtrace;
|
backtrace_context_t new_context;
|
||||||
if (backtrace_get_data(&new_backtrace, new_tid)) {
|
if (backtrace_create_context(&new_context, pid, new_tid, 0)) {
|
||||||
dump_thread(&new_backtrace, log, 0, total_sleep_time_usec);
|
dump_thread(&new_context, log, 0, total_sleep_time_usec);
|
||||||
|
backtrace_destroy_context(&new_context);
|
||||||
}
|
}
|
||||||
backtrace_free_data(&new_backtrace);
|
|
||||||
|
|
||||||
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
|
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
|
||||||
LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
|
LOG("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
|
||||||
|
|
@ -606,7 +608,7 @@ static void dump_logs(log_t* log, pid_t pid, bool tailOnly)
|
||||||
dump_log_file(log, pid, "/dev/log/main", tailOnly);
|
dump_log_file(log, pid, "/dev/log/main", tailOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_abort_message(const backtrace_t* backtrace, log_t* log, uintptr_t address) {
|
static void dump_abort_message(const backtrace_context_t* context, log_t* log, uintptr_t address) {
|
||||||
if (address == 0) {
|
if (address == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -618,7 +620,7 @@ static void dump_abort_message(const backtrace_t* backtrace, log_t* log, uintptr
|
||||||
char* p = &msg[0];
|
char* p = &msg[0];
|
||||||
while (p < &msg[sizeof(msg)]) {
|
while (p < &msg[sizeof(msg)]) {
|
||||||
uint32_t data;
|
uint32_t data;
|
||||||
if (!backtrace_read_word(backtrace, address, &data)) {
|
if (!backtrace_read_word(context, address, &data)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
address += sizeof(uint32_t);
|
address += sizeof(uint32_t);
|
||||||
|
|
@ -673,11 +675,11 @@ static bool dump_crash(log_t* log, pid_t pid, pid_t tid, int signal, uintptr_t a
|
||||||
dump_fault_addr(log, tid, signal);
|
dump_fault_addr(log, tid, signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
backtrace_t backtrace;
|
backtrace_context_t context;
|
||||||
if (backtrace_get_data(&backtrace, tid)) {
|
if (backtrace_create_context(&context, pid, tid, 0)) {
|
||||||
dump_abort_message(&backtrace, log, abort_msg_address);
|
dump_abort_message(&context, log, abort_msg_address);
|
||||||
dump_thread(&backtrace, log, SCOPE_AT_FAULT, total_sleep_time_usec);
|
dump_thread(&context, log, SCOPE_AT_FAULT, total_sleep_time_usec);
|
||||||
backtrace_free_data(&backtrace);
|
backtrace_destroy_context(&context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (want_logs) {
|
if (want_logs) {
|
||||||
|
|
|
||||||
85
include/backtrace/Backtrace.h
Normal file
85
include/backtrace/Backtrace.h
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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 _BACKTRACE_BACKTRACE_H
|
||||||
|
#define _BACKTRACE_BACKTRACE_H
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class BacktraceImpl;
|
||||||
|
|
||||||
|
class Backtrace {
|
||||||
|
public:
|
||||||
|
Backtrace(BacktraceImpl* impl);
|
||||||
|
virtual ~Backtrace();
|
||||||
|
|
||||||
|
// Get the current stack trace and store in the backtrace_ structure.
|
||||||
|
virtual bool Unwind(size_t num_ignore_frames);
|
||||||
|
|
||||||
|
// Get the function name and offset into the function given the pc.
|
||||||
|
// If the string is empty, then no valid function name was found.
|
||||||
|
virtual std::string GetFunctionName(uintptr_t pc, uintptr_t* offset);
|
||||||
|
|
||||||
|
// Get the name of the map associated with the given pc. If NULL is returned,
|
||||||
|
// then map_start is not set. Otherwise, map_start is the beginning of this
|
||||||
|
// map.
|
||||||
|
virtual const char* GetMapName(uintptr_t pc, uintptr_t* map_start);
|
||||||
|
|
||||||
|
// Finds the memory map associated with the given ptr.
|
||||||
|
virtual const backtrace_map_info_t* FindMapInfo(uintptr_t ptr);
|
||||||
|
|
||||||
|
// Read the data at a specific address.
|
||||||
|
virtual bool ReadWord(uintptr_t ptr, uint32_t* out_value) = 0;
|
||||||
|
|
||||||
|
// Create a string representing the formatted line of backtrace information
|
||||||
|
// for a single frame.
|
||||||
|
virtual std::string FormatFrameData(size_t frame_num);
|
||||||
|
|
||||||
|
pid_t Pid() { return backtrace_.pid; }
|
||||||
|
pid_t Tid() { return backtrace_.tid; }
|
||||||
|
size_t NumFrames() { return backtrace_.num_frames; }
|
||||||
|
|
||||||
|
const backtrace_t* GetBacktrace() { return &backtrace_; }
|
||||||
|
|
||||||
|
const backtrace_frame_data_t* GetFrame(size_t frame_num) {
|
||||||
|
return &backtrace_.frames[frame_num];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the correct Backtrace object based on what is to be unwound.
|
||||||
|
// If pid < 0 or equals the current pid, then the Backtrace object
|
||||||
|
// corresponds to the current process.
|
||||||
|
// If pid < 0 or equals the current pid and tid >= 0, then the Backtrace
|
||||||
|
// object corresponds to a thread in the current process.
|
||||||
|
// If pid >= 0 and tid < 0, then the Backtrace object corresponds to a
|
||||||
|
// different process.
|
||||||
|
// Tracing a thread in a different process is not supported.
|
||||||
|
static Backtrace* Create(pid_t pid, pid_t tid);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value);
|
||||||
|
|
||||||
|
BacktraceImpl* impl_;
|
||||||
|
|
||||||
|
backtrace_map_info_t* map_info_;
|
||||||
|
|
||||||
|
backtrace_t backtrace_;
|
||||||
|
|
||||||
|
friend class BacktraceImpl;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _BACKTRACE_BACKTRACE_H
|
||||||
|
|
@ -21,9 +21,7 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
__BEGIN_DECLS
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define MAX_BACKTRACE_FRAMES 64
|
#define MAX_BACKTRACE_FRAMES 64
|
||||||
|
|
||||||
|
|
@ -43,48 +41,58 @@ typedef struct {
|
||||||
size_t stack_size; /* The size of the stack, zero indicate an unknown stack size. */
|
size_t stack_size; /* The size of the stack, zero indicate an unknown stack size. */
|
||||||
const char* map_name; /* The name of the map to which this pc belongs, NULL indicates the pc doesn't belong to a known map. */
|
const char* map_name; /* The name of the map to which this pc belongs, NULL indicates the pc doesn't belong to a known map. */
|
||||||
uintptr_t map_offset; /* pc relative to the start of the map, only valid if map_name is not NULL. */
|
uintptr_t map_offset; /* pc relative to the start of the map, only valid if map_name is not NULL. */
|
||||||
char* proc_name; /* The function name associated with this pc, NULL if not found. */
|
char* func_name; /* The function name associated with this pc, NULL if not found. */
|
||||||
uintptr_t proc_offset; /* pc relative to the start of the procedure, only valid if proc_name is not NULL. */
|
uintptr_t func_offset; /* pc relative to the start of the function, only valid if func_name is not NULL. */
|
||||||
} backtrace_frame_data_t;
|
} backtrace_frame_data_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES];
|
backtrace_frame_data_t frames[MAX_BACKTRACE_FRAMES];
|
||||||
size_t num_frames;
|
size_t num_frames;
|
||||||
|
|
||||||
|
pid_t pid;
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
backtrace_map_info_t* map_info_list;
|
backtrace_map_info_t* map_info_list;
|
||||||
void* private_data;
|
|
||||||
} backtrace_t;
|
} backtrace_t;
|
||||||
|
|
||||||
/* Gather the backtrace data for tid and fill in the backtrace structure.
|
typedef struct {
|
||||||
* If tid < 0, then gather the backtrace for the current thread.
|
void* data;
|
||||||
*/
|
const backtrace_t* backtrace;
|
||||||
bool backtrace_get_data(backtrace_t* backtrace, pid_t tid);
|
} backtrace_context_t;
|
||||||
|
|
||||||
/* Free any memory associated with the backtrace structure. */
|
/* Create a context for the backtrace data and gather the backtrace.
|
||||||
void backtrace_free_data(backtrace_t* backtrace);
|
* If pid < 0, then gather the backtrace for the current process.
|
||||||
|
*/
|
||||||
|
bool backtrace_create_context(
|
||||||
|
backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames);
|
||||||
|
|
||||||
|
/* Gather the backtrace data for a pthread instead of a process. */
|
||||||
|
bool backtrace_create_thread_context(
|
||||||
|
backtrace_context_t* context, pid_t tid, size_t num_ignore_frames);
|
||||||
|
|
||||||
|
/* Free any memory allocated during the context create. */
|
||||||
|
void backtrace_destroy_context(backtrace_context_t* context);
|
||||||
|
|
||||||
/* Read data at a specific address for a process. */
|
/* Read data at a specific address for a process. */
|
||||||
bool backtrace_read_word(
|
bool backtrace_read_word(
|
||||||
const backtrace_t* backtrace, uintptr_t ptr, uint32_t* value);
|
const backtrace_context_t* context, uintptr_t ptr, uint32_t* value);
|
||||||
|
|
||||||
/* Get information about the map associated with a pc. If NULL is
|
/* Get information about the map name associated with a pc. If NULL is
|
||||||
* returned, then map_start is not set.
|
* returned, then map_start is not set.
|
||||||
*/
|
*/
|
||||||
const char* backtrace_get_map_info(
|
const char* backtrace_get_map_name(
|
||||||
const backtrace_t* backtrace, uintptr_t pc, uintptr_t* map_start);
|
const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start);
|
||||||
|
|
||||||
/* Get the procedure name and offest given the pc. If NULL is returned,
|
/* Get the function name and offset given the pc. If NULL is returned,
|
||||||
* then proc_offset is not set. The returned string is allocated using
|
* then func_offset is not set. The returned string is allocated using
|
||||||
* malloc and must be freed by the caller.
|
* malloc and must be freed by the caller.
|
||||||
*/
|
*/
|
||||||
char* backtrace_get_proc_name(
|
char* backtrace_get_func_name(
|
||||||
const backtrace_t* backtrace, uintptr_t pc, uintptr_t* proc_offset);
|
const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset);
|
||||||
|
|
||||||
/* Loads memory map from /proc/<tid>/maps. If tid < 0, then load the memory
|
/* Loads memory map from /proc/<pid>/maps. If pid < 0, then load the memory
|
||||||
* map for the current process.
|
* map for the current process.
|
||||||
*/
|
*/
|
||||||
backtrace_map_info_t* backtrace_create_map_info_list(pid_t tid);
|
backtrace_map_info_t* backtrace_create_map_info_list(pid_t pid);
|
||||||
|
|
||||||
/* Frees memory associated with the map list. */
|
/* Frees memory associated with the map list. */
|
||||||
void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list);
|
void backtrace_destroy_map_info_list(backtrace_map_info_t* map_info_list);
|
||||||
|
|
@ -95,10 +103,12 @@ const backtrace_map_info_t* backtrace_find_map_info(
|
||||||
|
|
||||||
/* Create a formatted line of backtrace information for a single frame. */
|
/* Create a formatted line of backtrace information for a single frame. */
|
||||||
void backtrace_format_frame_data(
|
void backtrace_format_frame_data(
|
||||||
const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size);
|
const backtrace_context_t* context, size_t frame_num, char* buf,
|
||||||
|
size_t buf_size);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
/* Get the backtrace data structure associated with the context. */
|
||||||
}
|
const backtrace_t* backtrace_get_data(backtrace_context_t* context);
|
||||||
#endif
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
#endif /* _BACKTRACE_H */
|
#endif /* _BACKTRACE_H */
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,109 @@
|
||||||
LOCAL_PATH:= $(call my-dir)
|
LOCAL_PATH:= $(call my-dir)
|
||||||
|
|
||||||
|
common_src := \
|
||||||
|
Backtrace.cpp \
|
||||||
|
BacktraceThread.cpp \
|
||||||
|
map_info.c \
|
||||||
|
thread_utils.c \
|
||||||
|
|
||||||
|
common_cflags := \
|
||||||
|
-Wall \
|
||||||
|
-Wno-unused-parameter \
|
||||||
|
-Werror \
|
||||||
|
|
||||||
|
common_conlyflags := \
|
||||||
|
-std=gnu99 \
|
||||||
|
|
||||||
|
common_cppflags := \
|
||||||
|
-std=gnu++11 \
|
||||||
|
|
||||||
|
common_shared_libs := \
|
||||||
|
libcutils \
|
||||||
|
libgccdemangle \
|
||||||
|
liblog \
|
||||||
|
|
||||||
|
# To enable using libunwind on each arch, add it to the list below.
|
||||||
|
ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),))
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
# The libbacktrace library using libunwind
|
# The native libbacktrace library with libunwind.
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
LOCAL_SRC_FILES:= \
|
||||||
unwind.c \
|
$(common_src) \
|
||||||
unwind_remote.c \
|
UnwindCurrent.cpp \
|
||||||
unwind_local.c \
|
UnwindPtrace.cpp \
|
||||||
common.c \
|
|
||||||
demangle.c \
|
|
||||||
map_info.c \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
-Wall \
|
$(common_cflags) \
|
||||||
-Wno-unused-parameter \
|
|
||||||
-Werror \
|
LOCAL_CONLYFLAGS += \
|
||||||
-std=gnu99 \
|
$(common_conlyflags) \
|
||||||
|
|
||||||
|
LOCAL_CPPFLAGS += \
|
||||||
|
$(common_cppflags) \
|
||||||
|
|
||||||
LOCAL_MODULE := libbacktrace
|
LOCAL_MODULE := libbacktrace
|
||||||
LOCAL_MODULE_TAGS := optional
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
|
||||||
liblog \
|
|
||||||
libunwind \
|
|
||||||
libunwind-ptrace \
|
|
||||||
libgccdemangle \
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := \
|
LOCAL_C_INCLUDES := \
|
||||||
|
$(common_c_includes) \
|
||||||
external/libunwind/include \
|
external/libunwind/include \
|
||||||
|
|
||||||
# The libunwind code is not in the tree yet, so don't build this library yet.
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
#include $(BUILD_SHARED_LIBRARY)
|
$(common_shared_libs) \
|
||||||
|
libunwind \
|
||||||
|
libunwind-ptrace \
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
|
include external/stlport/libstlport.mk
|
||||||
|
|
||||||
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
else
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
# The libbacktrace library using libcorkscrew
|
# The native libbacktrace library with libcorkscrew.
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
LOCAL_SRC_FILES:= \
|
||||||
corkscrew.c \
|
$(common_src) \
|
||||||
common.c \
|
Corkscrew.cpp \
|
||||||
demangle.c \
|
|
||||||
map_info.c \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := \
|
LOCAL_CFLAGS := \
|
||||||
-Wall \
|
$(common_cflags) \
|
||||||
-Wno-unused-parameter \
|
|
||||||
-Werror \
|
LOCAL_CONLYFLAGS += \
|
||||||
-std=gnu99 \
|
$(common_conlyflags) \
|
||||||
|
|
||||||
|
LOCAL_CPPFLAGS += \
|
||||||
|
$(common_cppflags) \
|
||||||
|
|
||||||
LOCAL_MODULE := libbacktrace
|
LOCAL_MODULE := libbacktrace
|
||||||
LOCAL_MODULE_TAGS := optional
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES := \
|
||||||
|
$(common_c_includes) \
|
||||||
|
system/core/libcorkscrew \
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
|
$(common_shared_libs) \
|
||||||
libcorkscrew \
|
libcorkscrew \
|
||||||
libdl \
|
libdl \
|
||||||
libgccdemangle \
|
|
||||||
liblog \
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
|
include external/stlport/libstlport.mk
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
endif
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
# libbacktrace test library, all optimizations turned off
|
# libbacktrace test library, all optimizations turned off
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
@ -77,6 +119,9 @@ LOCAL_CFLAGS += \
|
||||||
-std=gnu99 \
|
-std=gnu99 \
|
||||||
-O0 \
|
-O0 \
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
@ -88,16 +133,36 @@ LOCAL_MODULE := backtrace_test
|
||||||
LOCAL_MODULE_FLAGS := debug
|
LOCAL_MODULE_FLAGS := debug
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
backtrace_test.c \
|
backtrace_test.cpp \
|
||||||
|
thread_utils.c \
|
||||||
|
|
||||||
LOCAL_CFLAGS += \
|
LOCAL_CFLAGS += \
|
||||||
-std=gnu99 \
|
-fno-builtin \
|
||||||
|
-fstack-protector-all \
|
||||||
|
-O0 \
|
||||||
|
-g \
|
||||||
|
-DGTEST_OS_LINUX_ANDROID \
|
||||||
|
-DGTEST_HAS_STD_STRING \
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_CONLYFLAGS += \
|
||||||
|
$(common_conlyflags) \
|
||||||
|
|
||||||
|
LOCAL_CPPFLAGS += \
|
||||||
|
$(common_cppflags) \
|
||||||
|
-fpermissive \
|
||||||
|
|
||||||
|
LOCAL_SHARED_LIBRARIES += \
|
||||||
|
libcutils \
|
||||||
libbacktrace_test \
|
libbacktrace_test \
|
||||||
libbacktrace \
|
libbacktrace \
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
LOCAL_LDLIBS := \
|
||||||
|
-lpthread \
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
|
include $(BUILD_NATIVE_TEST)
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
# Only linux-x86 host versions of libbacktrace supported.
|
# Only linux-x86 host versions of libbacktrace supported.
|
||||||
|
|
@ -110,22 +175,26 @@ ifeq ($(HOST_OS)-$(HOST_ARCH),linux-x86)
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
LOCAL_SRC_FILES += \
|
LOCAL_SRC_FILES += \
|
||||||
corkscrew.c \
|
$(common_src) \
|
||||||
common.c \
|
Corkscrew.cpp \
|
||||||
demangle.c \
|
|
||||||
map_info.c \
|
|
||||||
|
|
||||||
LOCAL_CFLAGS += \
|
LOCAL_CFLAGS += \
|
||||||
-Wall \
|
$(common_cflags) \
|
||||||
-Wno-unused-parameter \
|
|
||||||
-Werror \
|
LOCAL_CONLYFLAGS += \
|
||||||
-std=gnu99 \
|
$(common_conlyflags) \
|
||||||
|
|
||||||
|
LOCAL_CPPFLAGS += \
|
||||||
|
$(common_cppflags) \
|
||||||
|
|
||||||
|
LOCAL_C_INCLUDES := \
|
||||||
|
$(common_c_includes) \
|
||||||
|
system/core/libcorkscrew \
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
liblog \
|
|
||||||
libcorkscrew \
|
|
||||||
libgccdemangle \
|
libgccdemangle \
|
||||||
liblog \
|
liblog \
|
||||||
|
libcorkscrew \
|
||||||
|
|
||||||
LOCAL_LDLIBS += \
|
LOCAL_LDLIBS += \
|
||||||
-ldl \
|
-ldl \
|
||||||
|
|
@ -134,6 +203,9 @@ LOCAL_LDLIBS += \
|
||||||
LOCAL_MODULE := libbacktrace
|
LOCAL_MODULE := libbacktrace
|
||||||
LOCAL_MODULE_TAGS := optional
|
LOCAL_MODULE_TAGS := optional
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
include $(BUILD_HOST_SHARED_LIBRARY)
|
include $(BUILD_HOST_SHARED_LIBRARY)
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
@ -151,6 +223,9 @@ LOCAL_CFLAGS += \
|
||||||
-std=gnu99 \
|
-std=gnu99 \
|
||||||
-O0 \
|
-O0 \
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
include $(BUILD_HOST_SHARED_LIBRARY)
|
include $(BUILD_HOST_SHARED_LIBRARY)
|
||||||
|
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
|
|
@ -162,15 +237,29 @@ LOCAL_MODULE := backtrace_test
|
||||||
LOCAL_MODULE_FLAGS := debug
|
LOCAL_MODULE_FLAGS := debug
|
||||||
|
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
backtrace_test.c \
|
backtrace_test.cpp \
|
||||||
|
thread_utils.c \
|
||||||
|
|
||||||
LOCAL_CFLAGS += \
|
LOCAL_CFLAGS += \
|
||||||
-std=gnu99 \
|
-fno-builtin \
|
||||||
|
-fstack-protector-all \
|
||||||
|
-O0 \
|
||||||
|
-g \
|
||||||
|
-DGTEST_HAS_STD_STRING \
|
||||||
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
LOCAL_SHARED_LIBRARIES := \
|
||||||
libbacktrace_test \
|
libbacktrace_test \
|
||||||
libbacktrace \
|
libbacktrace \
|
||||||
|
|
||||||
include $(BUILD_HOST_EXECUTABLE)
|
LOCAL_CPPFLAGS += \
|
||||||
|
-fpermissive \
|
||||||
|
|
||||||
|
LOCAL_LDLIBS := \
|
||||||
|
-lpthread \
|
||||||
|
|
||||||
|
LOCAL_ADDITIONAL_DEPENDENCIES := \
|
||||||
|
$(LOCAL_PATH)/Android.mk
|
||||||
|
|
||||||
|
include $(BUILD_HOST_NATIVE_TEST)
|
||||||
|
|
||||||
endif # HOST_OS-HOST_ARCH == linux-x86
|
endif # HOST_OS-HOST_ARCH == linux-x86
|
||||||
|
|
|
||||||
312
libbacktrace/Backtrace.cpp
Normal file
312
libbacktrace/Backtrace.cpp
Normal file
|
|
@ -0,0 +1,312 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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 <errno.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#define __STDC_FORMAT_MACROS
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <backtrace/Backtrace.h>
|
||||||
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
#include "Backtrace.h"
|
||||||
|
#include "thread_utils.h"
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// BacktraceImpl functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
backtrace_t* BacktraceImpl::GetBacktraceData() {
|
||||||
|
return &backtrace_obj_->backtrace_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Backtrace functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
Backtrace::Backtrace(BacktraceImpl* impl) : impl_(impl), map_info_(NULL) {
|
||||||
|
impl_->SetParent(this);
|
||||||
|
backtrace_.num_frames = 0;
|
||||||
|
backtrace_.pid = -1;
|
||||||
|
backtrace_.tid = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtrace::~Backtrace() {
|
||||||
|
for (size_t i = 0; i < NumFrames(); i++) {
|
||||||
|
if (backtrace_.frames[i].func_name) {
|
||||||
|
free(backtrace_.frames[i].func_name);
|
||||||
|
backtrace_.frames[i].func_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (map_info_) {
|
||||||
|
backtrace_destroy_map_info_list(map_info_);
|
||||||
|
map_info_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (impl_) {
|
||||||
|
delete impl_;
|
||||||
|
impl_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Backtrace::Unwind(size_t num_ignore_frames) {
|
||||||
|
return impl_->Unwind(num_ignore_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
__BEGIN_DECLS
|
||||||
|
extern char* __cxa_demangle (const char* mangled, char* buf, size_t* len,
|
||||||
|
int* status);
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
std::string Backtrace::GetFunctionName(uintptr_t pc, uintptr_t* offset) {
|
||||||
|
std::string func_name = impl_->GetFunctionNameRaw(pc, offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
// Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
|
||||||
|
if (symbol_name[0] != '_') {
|
||||||
|
return func_name;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
char* name = __cxa_demangle(func_name.c_str(), 0, 0, 0);
|
||||||
|
if (name) {
|
||||||
|
func_name = name;
|
||||||
|
free(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return func_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Backtrace::VerifyReadWordArgs(uintptr_t ptr, uint32_t* out_value) {
|
||||||
|
if (ptr & 3) {
|
||||||
|
ALOGW("Backtrace::verifyReadWordArgs: invalid pointer %p", (void*)ptr);
|
||||||
|
*out_value = (uint32_t)-1;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* Backtrace::GetMapName(uintptr_t pc, uintptr_t* map_start) {
|
||||||
|
const backtrace_map_info_t* map_info = FindMapInfo(pc);
|
||||||
|
if (map_info) {
|
||||||
|
if (map_start) {
|
||||||
|
*map_start = map_info->start;
|
||||||
|
}
|
||||||
|
return map_info->name;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backtrace_map_info_t* Backtrace::FindMapInfo(uintptr_t ptr) {
|
||||||
|
return backtrace_find_map_info(map_info_, ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Backtrace::FormatFrameData(size_t frame_num) {
|
||||||
|
backtrace_frame_data_t* frame = &backtrace_.frames[frame_num];
|
||||||
|
const char* map_name;
|
||||||
|
if (frame->map_name) {
|
||||||
|
map_name = frame->map_name;
|
||||||
|
} else {
|
||||||
|
map_name = "<unknown>";
|
||||||
|
}
|
||||||
|
uintptr_t relative_pc;
|
||||||
|
if (frame->map_offset) {
|
||||||
|
relative_pc = frame->map_offset;
|
||||||
|
} else {
|
||||||
|
relative_pc = frame->pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[512];
|
||||||
|
if (frame->func_name && frame->func_offset) {
|
||||||
|
snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
|
||||||
|
frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
|
||||||
|
frame->func_name, frame->func_offset);
|
||||||
|
} else if (frame->func_name) {
|
||||||
|
snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num,
|
||||||
|
(int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->func_name);
|
||||||
|
} else {
|
||||||
|
snprintf(buf, sizeof(buf), "#%02zu pc %0*" PRIxPTR " %s", frame_num,
|
||||||
|
(int)sizeof(uintptr_t)*2, relative_pc, map_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// BacktraceCurrent functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
BacktraceCurrent::BacktraceCurrent(BacktraceImpl* impl) : Backtrace(impl) {
|
||||||
|
map_info_ = backtrace_create_map_info_list(-1);
|
||||||
|
|
||||||
|
backtrace_.pid = getpid();
|
||||||
|
}
|
||||||
|
|
||||||
|
BacktraceCurrent::~BacktraceCurrent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BacktraceCurrent::ReadWord(uintptr_t ptr, uint32_t* out_value) {
|
||||||
|
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backtrace_map_info_t* map_info = FindMapInfo(ptr);
|
||||||
|
if (map_info && map_info->is_readable) {
|
||||||
|
*out_value = *reinterpret_cast<uint32_t*>(ptr);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
ALOGW("BacktraceCurrent::readWord: pointer %p not in a readbale map", reinterpret_cast<void*>(ptr));
|
||||||
|
*out_value = static_cast<uint32_t>(-1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// BacktracePtrace functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
BacktracePtrace::BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid)
|
||||||
|
: Backtrace(impl) {
|
||||||
|
map_info_ = backtrace_create_map_info_list(tid);
|
||||||
|
|
||||||
|
backtrace_.pid = pid;
|
||||||
|
backtrace_.tid = tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
BacktracePtrace::~BacktracePtrace() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BacktracePtrace::ReadWord(uintptr_t ptr, uint32_t* out_value) {
|
||||||
|
if (!VerifyReadWordArgs(ptr, out_value)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__APPLE__)
|
||||||
|
ALOGW("BacktracePtrace::readWord: MacOS does not support reading from another pid.\n");
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
// ptrace() returns -1 and sets errno when the operation fails.
|
||||||
|
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
||||||
|
errno = 0;
|
||||||
|
*out_value = ptrace(PTRACE_PEEKTEXT, Tid(), reinterpret_cast<void*>(ptr), NULL);
|
||||||
|
if (*out_value == static_cast<uint32_t>(-1) && errno) {
|
||||||
|
ALOGW("BacktracePtrace::readWord: invalid pointer 0x%08x reading from tid %d, "
|
||||||
|
"ptrace() errno=%d", ptr, Tid(), errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtrace* Backtrace::Create(pid_t pid, pid_t tid) {
|
||||||
|
if (pid < 0 || pid == getpid()) {
|
||||||
|
if (tid < 0 || tid == gettid()) {
|
||||||
|
return CreateCurrentObj();
|
||||||
|
} else {
|
||||||
|
return CreateThreadObj(tid);
|
||||||
|
}
|
||||||
|
} else if (tid < 0) {
|
||||||
|
return CreatePtraceObj(pid, pid);
|
||||||
|
} else {
|
||||||
|
return CreatePtraceObj(pid, tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Common interface functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
bool backtrace_create_context(
|
||||||
|
backtrace_context_t* context, pid_t pid, pid_t tid, size_t num_ignore_frames) {
|
||||||
|
Backtrace* backtrace = Backtrace::Create(pid, tid);
|
||||||
|
if (!backtrace) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!backtrace->Unwind(num_ignore_frames)) {
|
||||||
|
delete backtrace;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
context->data = backtrace;
|
||||||
|
context->backtrace = backtrace->GetBacktrace();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrace_destroy_context(backtrace_context_t* context) {
|
||||||
|
if (context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
delete backtrace;
|
||||||
|
context->data = NULL;
|
||||||
|
}
|
||||||
|
context->backtrace = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backtrace_t* backtrace_get_data(backtrace_context_t* context) {
|
||||||
|
if (context && context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
return backtrace->GetBacktrace();
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool backtrace_read_word(const backtrace_context_t* context, uintptr_t ptr, uint32_t* value) {
|
||||||
|
if (context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
return backtrace->ReadWord(ptr, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* backtrace_get_map_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* map_start) {
|
||||||
|
if (context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
return backtrace->GetMapName(pc, map_start);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* backtrace_get_func_name(const backtrace_context_t* context, uintptr_t pc, uintptr_t* func_offset) {
|
||||||
|
if (context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
std::string func_name = backtrace->GetFunctionName(pc, func_offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
return strdup(func_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void backtrace_format_frame_data(
|
||||||
|
const backtrace_context_t* context, size_t frame_num, char* buf,
|
||||||
|
size_t buf_size) {
|
||||||
|
if (buf_size == 0 || buf == NULL) {
|
||||||
|
ALOGW("backtrace_format_frame_data: bad call buf %p buf_size %zu\n",
|
||||||
|
buf, buf_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (context->data) {
|
||||||
|
Backtrace* backtrace = reinterpret_cast<Backtrace*>(context->data);
|
||||||
|
std::string line = backtrace->FormatFrameData(frame_num);
|
||||||
|
if (line.size() > buf_size) {
|
||||||
|
memcpy(buf, line.c_str(), buf_size-1);
|
||||||
|
buf[buf_size] = '\0';
|
||||||
|
} else {
|
||||||
|
memcpy(buf, line.c_str(), line.size()+1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
libbacktrace/Backtrace.h
Normal file
62
libbacktrace/Backtrace.h
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_H
|
||||||
|
#define _LIBBACKTRACE_BACKTRACE_H
|
||||||
|
|
||||||
|
#include <backtrace/Backtrace.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
class BacktraceImpl {
|
||||||
|
public:
|
||||||
|
virtual ~BacktraceImpl() { }
|
||||||
|
|
||||||
|
virtual bool Unwind(size_t num_ignore_frames) = 0;
|
||||||
|
|
||||||
|
// The name returned is not demangled, Backtrace::GetFunctionName()
|
||||||
|
// takes care of demangling the name.
|
||||||
|
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) = 0;
|
||||||
|
|
||||||
|
void SetParent(Backtrace* backtrace) { backtrace_obj_ = backtrace; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
backtrace_t* GetBacktraceData();
|
||||||
|
|
||||||
|
Backtrace* backtrace_obj_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BacktraceCurrent : public Backtrace {
|
||||||
|
public:
|
||||||
|
BacktraceCurrent(BacktraceImpl* impl);
|
||||||
|
virtual ~BacktraceCurrent();
|
||||||
|
|
||||||
|
bool ReadWord(uintptr_t ptr, uint32_t* out_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
class BacktracePtrace : public Backtrace {
|
||||||
|
public:
|
||||||
|
BacktracePtrace(BacktraceImpl* impl, pid_t pid, pid_t tid);
|
||||||
|
virtual ~BacktracePtrace();
|
||||||
|
|
||||||
|
bool ReadWord(uintptr_t ptr, uint32_t* out_value);
|
||||||
|
};
|
||||||
|
|
||||||
|
Backtrace* CreateCurrentObj();
|
||||||
|
Backtrace* CreatePtraceObj(pid_t pid, pid_t tid);
|
||||||
|
Backtrace* CreateThreadObj(pid_t tid);
|
||||||
|
|
||||||
|
#endif // _LIBBACKTRACE_BACKTRACE_H
|
||||||
224
libbacktrace/BacktraceThread.cpp
Normal file
224
libbacktrace/BacktraceThread.cpp
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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 <errno.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <cutils/atomic.h>
|
||||||
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
#include "BacktraceThread.h"
|
||||||
|
#include "thread_utils.h"
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// ThreadEntry implementation.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
static ThreadEntry* g_list = NULL;
|
||||||
|
static pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
ThreadEntry::ThreadEntry(
|
||||||
|
BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames)
|
||||||
|
: thread_intf_(intf), pid_(pid), tid_(tid), next_(NULL), prev_(NULL),
|
||||||
|
state_(STATE_WAITING), num_ignore_frames_(num_ignore_frames) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadEntry::~ThreadEntry() {
|
||||||
|
pthread_mutex_lock(&g_mutex);
|
||||||
|
if (g_list == this) {
|
||||||
|
g_list = next_;
|
||||||
|
} else {
|
||||||
|
if (next_) {
|
||||||
|
next_->prev_ = prev_;
|
||||||
|
}
|
||||||
|
prev_->next_ = next_;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_mutex);
|
||||||
|
|
||||||
|
next_ = NULL;
|
||||||
|
prev_ = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadEntry* ThreadEntry::AddThreadToUnwind(
|
||||||
|
BacktraceThreadInterface* intf, pid_t pid, pid_t tid, size_t num_ignore_frames) {
|
||||||
|
ThreadEntry* entry = new ThreadEntry(intf, pid, tid, num_ignore_frames);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&g_mutex);
|
||||||
|
ThreadEntry* cur_entry = g_list;
|
||||||
|
while (cur_entry != NULL) {
|
||||||
|
if (cur_entry->Match(pid, tid)) {
|
||||||
|
// There is already an entry for this pid/tid, this is bad.
|
||||||
|
ALOGW("%s::%s(): Entry for pid %d tid %d already exists.\n",
|
||||||
|
__FILE__, __FUNCTION__, pid, tid);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&g_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cur_entry = cur_entry->next_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the entry to the list.
|
||||||
|
entry->next_ = g_list;
|
||||||
|
if (g_list) {
|
||||||
|
g_list->prev_ = entry;
|
||||||
|
}
|
||||||
|
g_list = entry;
|
||||||
|
pthread_mutex_unlock(&g_mutex);
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// BacktraceThread functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
static void SignalHandler(int n __attribute__((unused)), siginfo_t* siginfo,
|
||||||
|
void* sigcontext) {
|
||||||
|
if (pthread_mutex_lock(&g_mutex) == 0) {
|
||||||
|
pid_t pid = getpid();
|
||||||
|
pid_t tid = gettid();
|
||||||
|
ThreadEntry* cur_entry = g_list;
|
||||||
|
while (cur_entry) {
|
||||||
|
if (cur_entry->Match(pid, tid)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cur_entry = cur_entry->next_;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&g_mutex);
|
||||||
|
if (!cur_entry) {
|
||||||
|
ALOGW("%s::%s(): Unable to find pid %d tid %d information\n",
|
||||||
|
__FILE__, __FUNCTION__, pid, tid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (android_atomic_acquire_cas(STATE_WAITING, STATE_DUMPING, &cur_entry->state_) == 0) {
|
||||||
|
cur_entry->thread_intf_->ThreadUnwind(siginfo, sigcontext,
|
||||||
|
cur_entry->num_ignore_frames_);
|
||||||
|
}
|
||||||
|
android_atomic_release_store(STATE_DONE, &cur_entry->state_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BacktraceThread::BacktraceThread(
|
||||||
|
BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid)
|
||||||
|
: BacktraceCurrent(impl), thread_intf_(thread_intf) {
|
||||||
|
backtrace_.tid = tid;
|
||||||
|
}
|
||||||
|
|
||||||
|
BacktraceThread::~BacktraceThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void BacktraceThread::FinishUnwind() {
|
||||||
|
for (size_t i = 0; i < NumFrames(); i++) {
|
||||||
|
backtrace_frame_data_t* frame = &backtrace_.frames[i];
|
||||||
|
|
||||||
|
frame->map_offset = 0;
|
||||||
|
uintptr_t map_start;
|
||||||
|
frame->map_name = GetMapName(frame->pc, &map_start);
|
||||||
|
if (frame->map_name) {
|
||||||
|
frame->map_offset = frame->pc - map_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame->func_offset = 0;
|
||||||
|
std::string func_name = GetFunctionName(frame->pc, &frame->func_offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
frame->func_name = strdup(func_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BacktraceThread::TriggerUnwindOnThread(ThreadEntry* entry) {
|
||||||
|
entry->state_ = STATE_WAITING;
|
||||||
|
|
||||||
|
if (tgkill(Pid(), Tid(), SIGURG) != 0) {
|
||||||
|
ALOGW("%s::%s(): tgkill failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow up to a second for the dump to occur.
|
||||||
|
int wait_millis = 1000;
|
||||||
|
int32_t state;
|
||||||
|
while (true) {
|
||||||
|
state = android_atomic_acquire_load(&entry->state_);
|
||||||
|
if (state != STATE_WAITING) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (wait_millis--) {
|
||||||
|
usleep(1000);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool cancelled = false;
|
||||||
|
if (state == STATE_WAITING) {
|
||||||
|
if (android_atomic_acquire_cas(state, STATE_CANCEL, &entry->state_) == 0) {
|
||||||
|
ALOGW("%s::%s(): Cancelled dump of thread %d\n", __FILE__, __FUNCTION__,
|
||||||
|
entry->tid_);
|
||||||
|
state = STATE_CANCEL;
|
||||||
|
cancelled = true;
|
||||||
|
} else {
|
||||||
|
state = android_atomic_acquire_load(&entry->state_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for at most one minute for the dump to finish.
|
||||||
|
wait_millis = 60000;
|
||||||
|
while (android_atomic_acquire_load(&entry->state_) != STATE_DONE) {
|
||||||
|
if (wait_millis--) {
|
||||||
|
usleep(1000);
|
||||||
|
} else {
|
||||||
|
ALOGW("%s::%s(): Didn't finish thread unwind in 60 seconds.\n",
|
||||||
|
__FILE__, __FUNCTION__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !cancelled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BacktraceThread::Unwind(size_t num_ignore_frames) {
|
||||||
|
if (!thread_intf_->Init()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadEntry* entry = ThreadEntry::AddThreadToUnwind(
|
||||||
|
thread_intf_, Pid(), Tid(), num_ignore_frames);
|
||||||
|
if (!entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool retval = false;
|
||||||
|
struct sigaction act, oldact;
|
||||||
|
memset(&act, 0, sizeof(act));
|
||||||
|
act.sa_sigaction = SignalHandler;
|
||||||
|
act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
|
||||||
|
sigemptyset(&act.sa_mask);
|
||||||
|
if (sigaction(SIGURG, &act, &oldact) == 0) {
|
||||||
|
retval = TriggerUnwindOnThread(entry);
|
||||||
|
sigaction(SIGURG, &oldact, NULL);
|
||||||
|
} else {
|
||||||
|
ALOGW("%s::%s(): sigaction failed %s\n", __FILE__, __FUNCTION__, strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retval) {
|
||||||
|
FinishUnwind();
|
||||||
|
}
|
||||||
|
delete entry;
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
93
libbacktrace/BacktraceThread.h
Normal file
93
libbacktrace/BacktraceThread.h
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_THREAD_H
|
||||||
|
#define _LIBBACKTRACE_BACKTRACE_THREAD_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "Backtrace.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
STATE_WAITING = 0,
|
||||||
|
STATE_DUMPING,
|
||||||
|
STATE_DONE,
|
||||||
|
STATE_CANCEL,
|
||||||
|
} state_e;
|
||||||
|
|
||||||
|
class BacktraceThreadInterface;
|
||||||
|
|
||||||
|
class ThreadEntry {
|
||||||
|
public:
|
||||||
|
ThreadEntry(
|
||||||
|
BacktraceThreadInterface* impl, pid_t pid, pid_t tid,
|
||||||
|
size_t num_ignore_frames);
|
||||||
|
~ThreadEntry();
|
||||||
|
|
||||||
|
bool Match(pid_t pid, pid_t tid) { return (pid == pid_ && tid == tid_); }
|
||||||
|
|
||||||
|
static ThreadEntry* AddThreadToUnwind(
|
||||||
|
BacktraceThreadInterface* thread_intf, pid_t pid, pid_t tid,
|
||||||
|
size_t num_ignored_frames);
|
||||||
|
|
||||||
|
BacktraceThreadInterface* thread_intf_;
|
||||||
|
pid_t pid_;
|
||||||
|
pid_t tid_;
|
||||||
|
ThreadEntry* next_;
|
||||||
|
ThreadEntry* prev_;
|
||||||
|
int32_t state_;
|
||||||
|
int num_ignore_frames_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Interface class that does not contain any local storage, only defines
|
||||||
|
// virtual functions to be defined by subclasses.
|
||||||
|
class BacktraceThreadInterface {
|
||||||
|
public:
|
||||||
|
virtual ~BacktraceThreadInterface() { }
|
||||||
|
|
||||||
|
virtual bool Init() = 0;
|
||||||
|
|
||||||
|
virtual void ThreadUnwind(
|
||||||
|
siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BacktraceThread : public BacktraceCurrent {
|
||||||
|
public:
|
||||||
|
// impl and thread_intf should point to the same object, this allows
|
||||||
|
// the compiler to catch if an implementation does not properly
|
||||||
|
// subclass both.
|
||||||
|
BacktraceThread(
|
||||||
|
BacktraceImpl* impl, BacktraceThreadInterface* thread_intf, pid_t tid);
|
||||||
|
virtual ~BacktraceThread();
|
||||||
|
|
||||||
|
virtual bool Unwind(size_t num_ignore_frames);
|
||||||
|
|
||||||
|
virtual void ThreadUnwind(
|
||||||
|
siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
|
||||||
|
thread_intf_->ThreadUnwind(siginfo, sigcontext, num_ignore_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual bool TriggerUnwindOnThread(ThreadEntry* entry);
|
||||||
|
|
||||||
|
virtual void FinishUnwind();
|
||||||
|
|
||||||
|
BacktraceThreadInterface* thread_intf_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _LIBBACKTRACE_BACKTRACE_THREAD_H
|
||||||
200
libbacktrace/Corkscrew.cpp
Normal file
200
libbacktrace/Corkscrew.cpp
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "libbacktrace"
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <backtrace-arch.h>
|
||||||
|
#include <cutils/log.h>
|
||||||
|
#include <corkscrew/backtrace.h>
|
||||||
|
|
||||||
|
#ifndef __USE_GNU
|
||||||
|
#define __USE_GNU
|
||||||
|
#endif
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
|
#include "Corkscrew.h"
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CorkscrewCommon functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
bool CorkscrewCommon::GenerateFrameData(
|
||||||
|
backtrace_frame_t* cork_frames, ssize_t num_frames) {
|
||||||
|
if (num_frames < 0) {
|
||||||
|
ALOGW("CorkscrewCommon::GenerateFrameData: libcorkscrew unwind failed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_t* data = GetBacktraceData();
|
||||||
|
data->num_frames = num_frames;
|
||||||
|
for (size_t i = 0; i < data->num_frames; i++) {
|
||||||
|
backtrace_frame_data_t* frame = &data->frames[i];
|
||||||
|
frame->pc = cork_frames[i].absolute_pc;
|
||||||
|
frame->sp = cork_frames[i].stack_top;
|
||||||
|
frame->stack_size = cork_frames[i].stack_size;
|
||||||
|
frame->map_name = NULL;
|
||||||
|
frame->map_offset = 0;
|
||||||
|
frame->func_name = NULL;
|
||||||
|
frame->func_offset = 0;
|
||||||
|
|
||||||
|
uintptr_t map_start;
|
||||||
|
frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
|
||||||
|
if (frame->map_name) {
|
||||||
|
frame->map_offset = frame->pc - map_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
frame->func_name = strdup(func_name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CorkscrewCurrent functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
CorkscrewCurrent::CorkscrewCurrent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
CorkscrewCurrent::~CorkscrewCurrent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CorkscrewCurrent::Unwind(size_t num_ignore_frames) {
|
||||||
|
backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
|
||||||
|
ssize_t num_frames = unwind_backtrace(frames, num_ignore_frames, MAX_BACKTRACE_FRAMES);
|
||||||
|
|
||||||
|
return GenerateFrameData(frames, num_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CorkscrewCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||||
|
*offset = 0;
|
||||||
|
|
||||||
|
// Get information about the current thread.
|
||||||
|
Dl_info info;
|
||||||
|
const backtrace_map_info_t* map_info = backtrace_obj_->FindMapInfo(pc);
|
||||||
|
const char* symbol_name = NULL;
|
||||||
|
if (map_info && dladdr((const void*)pc, &info) && info.dli_sname) {
|
||||||
|
*offset = pc - map_info->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
|
||||||
|
symbol_name = info.dli_sname;
|
||||||
|
|
||||||
|
return symbol_name;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CorkscrewThread functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
CorkscrewThread::CorkscrewThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
CorkscrewThread::~CorkscrewThread() {
|
||||||
|
if (corkscrew_map_info_) {
|
||||||
|
free_map_info_list(corkscrew_map_info_);
|
||||||
|
corkscrew_map_info_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CorkscrewThread::Init() {
|
||||||
|
corkscrew_map_info_ = load_map_info_list(backtrace_obj_->Pid());
|
||||||
|
return corkscrew_map_info_ != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CorkscrewThread::ThreadUnwind(
|
||||||
|
siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames) {
|
||||||
|
backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
|
||||||
|
ssize_t num_frames = unwind_backtrace_signal_arch(
|
||||||
|
siginfo, sigcontext, corkscrew_map_info_, frames, num_ignore_frames,
|
||||||
|
MAX_BACKTRACE_FRAMES);
|
||||||
|
if (num_frames > 0) {
|
||||||
|
backtrace_t* data = GetBacktraceData();
|
||||||
|
data->num_frames = num_frames;
|
||||||
|
for (size_t i = 0; i < data->num_frames; i++) {
|
||||||
|
backtrace_frame_data_t* frame = &data->frames[i];
|
||||||
|
frame->pc = frames[i].absolute_pc;
|
||||||
|
frame->sp = frames[i].stack_top;
|
||||||
|
frame->stack_size = frames[i].stack_size;
|
||||||
|
|
||||||
|
frame->map_offset = 0;
|
||||||
|
frame->map_name = NULL;
|
||||||
|
frame->map_offset = 0;
|
||||||
|
|
||||||
|
frame->func_offset = 0;
|
||||||
|
frame->func_name = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// CorkscrewPtrace functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
CorkscrewPtrace::CorkscrewPtrace() : ptrace_context_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
CorkscrewPtrace::~CorkscrewPtrace() {
|
||||||
|
if (ptrace_context_) {
|
||||||
|
free_ptrace_context(ptrace_context_);
|
||||||
|
ptrace_context_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CorkscrewPtrace::Unwind(size_t num_ignore_frames) {
|
||||||
|
ptrace_context_ = load_ptrace_context(backtrace_obj_->Tid());
|
||||||
|
|
||||||
|
backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
|
||||||
|
ssize_t num_frames = unwind_backtrace_ptrace(
|
||||||
|
backtrace_obj_->Tid(), ptrace_context_, frames, num_ignore_frames,
|
||||||
|
MAX_BACKTRACE_FRAMES);
|
||||||
|
|
||||||
|
return GenerateFrameData(frames, num_frames);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CorkscrewPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||||
|
// Get information about a different process.
|
||||||
|
const map_info_t* map_info;
|
||||||
|
const symbol_t* symbol;
|
||||||
|
find_symbol_ptrace(ptrace_context_, pc, &map_info, &symbol);
|
||||||
|
char* symbol_name = NULL;
|
||||||
|
if (symbol) {
|
||||||
|
if (map_info) {
|
||||||
|
*offset = pc - map_info->start - symbol->start;
|
||||||
|
}
|
||||||
|
symbol_name = symbol->name;
|
||||||
|
return symbol_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// C++ object createion functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
Backtrace* CreateCurrentObj() {
|
||||||
|
return new BacktraceCurrent(new CorkscrewCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
|
||||||
|
return new BacktracePtrace(new CorkscrewPtrace(), pid, tid);
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtrace* CreateThreadObj(pid_t tid) {
|
||||||
|
CorkscrewThread* thread_obj = new CorkscrewThread();
|
||||||
|
return new BacktraceThread(thread_obj, thread_obj, tid);
|
||||||
|
}
|
||||||
74
libbacktrace/Corkscrew.h
Normal file
74
libbacktrace/Corkscrew.h
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_CORKSCREW_H
|
||||||
|
#define _LIBBACKTRACE_CORKSCREW_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
#include <backtrace/Backtrace.h>
|
||||||
|
|
||||||
|
#include <corkscrew/backtrace.h>
|
||||||
|
|
||||||
|
#include "Backtrace.h"
|
||||||
|
#include "BacktraceThread.h"
|
||||||
|
|
||||||
|
class CorkscrewCommon : public BacktraceImpl {
|
||||||
|
public:
|
||||||
|
bool GenerateFrameData(backtrace_frame_t* cork_frames, ssize_t num_frames);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CorkscrewCurrent : public CorkscrewCommon {
|
||||||
|
public:
|
||||||
|
CorkscrewCurrent();
|
||||||
|
virtual ~CorkscrewCurrent();
|
||||||
|
|
||||||
|
virtual bool Unwind(size_t num_ignore_threads);
|
||||||
|
|
||||||
|
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
class CorkscrewThread : public CorkscrewCurrent, public BacktraceThreadInterface {
|
||||||
|
public:
|
||||||
|
CorkscrewThread();
|
||||||
|
virtual ~CorkscrewThread();
|
||||||
|
|
||||||
|
virtual bool Init();
|
||||||
|
|
||||||
|
virtual void ThreadUnwind(
|
||||||
|
siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
|
||||||
|
|
||||||
|
private:
|
||||||
|
map_info_t* corkscrew_map_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CorkscrewPtrace : public CorkscrewCommon {
|
||||||
|
public:
|
||||||
|
CorkscrewPtrace();
|
||||||
|
virtual ~CorkscrewPtrace();
|
||||||
|
|
||||||
|
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
|
||||||
|
|
||||||
|
virtual bool Unwind(size_t num_ignore_threads);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ptrace_context_t* ptrace_context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _LIBBACKTRACE_CORKSCREW_H
|
||||||
222
libbacktrace/UnwindCurrent.cpp
Normal file
222
libbacktrace/UnwindCurrent.cpp
Normal file
|
|
@ -0,0 +1,222 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "libbacktrace"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
|
||||||
|
#define UNW_LOCAL_ONLY
|
||||||
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
#include "UnwindCurrent.h"
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
#if !defined(__BIONIC_HAVE_UCONTEXT_T)
|
||||||
|
// The Current version of the Android <signal.h> doesn't define ucontext_t.
|
||||||
|
#include <asm/sigcontext.h> // Ensure 'struct sigcontext' is defined.
|
||||||
|
|
||||||
|
// Machine context at the time a signal was raised.
|
||||||
|
typedef struct ucontext {
|
||||||
|
uint32_t uc_flags;
|
||||||
|
struct ucontext* uc_link;
|
||||||
|
stack_t uc_stack;
|
||||||
|
struct sigcontext uc_mcontext;
|
||||||
|
uint32_t uc_sigmask;
|
||||||
|
} ucontext_t;
|
||||||
|
#endif // !__BIONIC_HAVE_UCONTEXT_T
|
||||||
|
#endif // defined(__arm__)
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// UnwindCurrent functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
UnwindCurrent::UnwindCurrent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindCurrent::~UnwindCurrent() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnwindCurrent::Unwind(size_t num_ignore_frames) {
|
||||||
|
int ret = unw_getcontext(&context_);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindCurrent::Unwind: unw_getcontext failed %d\n", ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return UnwindFromContext(num_ignore_frames, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UnwindCurrent::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||||
|
*offset = 0;
|
||||||
|
char buf[512];
|
||||||
|
unw_word_t value;
|
||||||
|
if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
|
||||||
|
&value, &context_) >= 0 && buf[0] != '\0') {
|
||||||
|
*offset = static_cast<uintptr_t>(value);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, bool resolve) {
|
||||||
|
backtrace_t* backtrace = GetBacktraceData();
|
||||||
|
backtrace->num_frames = 0;
|
||||||
|
|
||||||
|
// The cursor structure is pretty large, do not put it on the stack.
|
||||||
|
unw_cursor_t* cursor = new unw_cursor_t;
|
||||||
|
int ret = unw_init_local(cursor, &context_);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindCurrent::UnwindWithContext: unw_init_local failed %d\n", ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
unw_word_t pc;
|
||||||
|
ret = unw_get_reg(cursor, UNW_REG_IP, &pc);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindCurrent::UnwindWithContext: Failed to read IP %d\n", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unw_word_t sp;
|
||||||
|
ret = unw_get_reg(cursor, UNW_REG_SP, &sp);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindCurrent::UnwindWithContext: Failed to read SP %d\n", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_ignore_frames == 0) {
|
||||||
|
size_t num_frames = backtrace->num_frames;
|
||||||
|
backtrace_frame_data_t* frame = &backtrace->frames[num_frames];
|
||||||
|
frame->pc = static_cast<uintptr_t>(pc);
|
||||||
|
frame->sp = static_cast<uintptr_t>(sp);
|
||||||
|
frame->stack_size = 0;
|
||||||
|
frame->map_name = NULL;
|
||||||
|
frame->map_offset = 0;
|
||||||
|
frame->func_name = NULL;
|
||||||
|
frame->func_offset = 0;
|
||||||
|
|
||||||
|
if (num_frames > 0) {
|
||||||
|
// Set the stack size for the previous frame.
|
||||||
|
backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1];
|
||||||
|
prev->stack_size = frame->sp - prev->sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resolve) {
|
||||||
|
std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
frame->func_name = strdup(func_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t map_start;
|
||||||
|
frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
|
||||||
|
if (frame->map_name) {
|
||||||
|
frame->map_offset = frame->pc - map_start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace->num_frames++;
|
||||||
|
} else {
|
||||||
|
num_ignore_frames--;
|
||||||
|
}
|
||||||
|
ret = unw_step (cursor);
|
||||||
|
} while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
|
||||||
|
|
||||||
|
delete cursor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnwindCurrent::ExtractContext(void* sigcontext) {
|
||||||
|
unw_tdep_context_t* context = reinterpret_cast<unw_tdep_context_t*>(&context_);
|
||||||
|
|
||||||
|
#if defined(__arm__)
|
||||||
|
const ucontext_t* uc = reinterpret_cast<const ucontext_t*>(sigcontext);
|
||||||
|
|
||||||
|
context->regs[0] = uc->uc_mcontext.arm_r0;
|
||||||
|
context->regs[1] = uc->uc_mcontext.arm_r1;
|
||||||
|
context->regs[2] = uc->uc_mcontext.arm_r2;
|
||||||
|
context->regs[3] = uc->uc_mcontext.arm_r3;
|
||||||
|
context->regs[4] = uc->uc_mcontext.arm_r4;
|
||||||
|
context->regs[5] = uc->uc_mcontext.arm_r5;
|
||||||
|
context->regs[6] = uc->uc_mcontext.arm_r6;
|
||||||
|
context->regs[7] = uc->uc_mcontext.arm_r7;
|
||||||
|
context->regs[8] = uc->uc_mcontext.arm_r8;
|
||||||
|
context->regs[9] = uc->uc_mcontext.arm_r9;
|
||||||
|
context->regs[10] = uc->uc_mcontext.arm_r10;
|
||||||
|
context->regs[11] = uc->uc_mcontext.arm_fp;
|
||||||
|
context->regs[12] = uc->uc_mcontext.arm_ip;
|
||||||
|
context->regs[13] = uc->uc_mcontext.arm_sp;
|
||||||
|
context->regs[14] = uc->uc_mcontext.arm_lr;
|
||||||
|
context->regs[15] = uc->uc_mcontext.arm_pc;
|
||||||
|
|
||||||
|
#elif defined(__mips__)
|
||||||
|
|
||||||
|
typedef struct ucontext {
|
||||||
|
uint32_t sp;
|
||||||
|
uint32_t ra;
|
||||||
|
uint32_t pc;
|
||||||
|
} ucontext_t;
|
||||||
|
|
||||||
|
const ucontext_t* uc = (const ucontext_t*)sigcontext;
|
||||||
|
|
||||||
|
context->uc_mcontext.sp = uc->sp;
|
||||||
|
context->uc_mcontext.pc = uc->pc;
|
||||||
|
context->uc_mcontext.ra = uc->ra;
|
||||||
|
#elif defined(__x86__)
|
||||||
|
|
||||||
|
#include <asm/sigcontext.h>
|
||||||
|
#include <asm/ucontext.h>
|
||||||
|
typedef struct ucontext ucontext_t;
|
||||||
|
|
||||||
|
const ucontext_t* uc = (const ucontext_t*)sigcontext;
|
||||||
|
|
||||||
|
context->uc_mcontext.gregs[REG_EBP] = uc->uc_mcontext.gregs[REG_EBP];
|
||||||
|
context->uc_mcontext.gregs[REG_ESP] = uc->uc_mcontext.gregs[REG_ESP];
|
||||||
|
context->uc_mcontext.gregs[REG_EIP] = uc->uc_mcontext.gregs[REG_EIP];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// UnwindThread functions.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
UnwindThread::UnwindThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindThread::~UnwindThread() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnwindThread::Init() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnwindThread::ThreadUnwind(
|
||||||
|
siginfo_t* /*siginfo*/, void* sigcontext, size_t num_ignore_frames) {
|
||||||
|
ExtractContext(sigcontext);
|
||||||
|
UnwindFromContext(num_ignore_frames, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// C++ object creation function.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
Backtrace* CreateCurrentObj() {
|
||||||
|
return new BacktraceCurrent(new UnwindCurrent());
|
||||||
|
}
|
||||||
|
|
||||||
|
Backtrace* CreateThreadObj(pid_t tid) {
|
||||||
|
UnwindThread* thread_obj = new UnwindThread();
|
||||||
|
return new BacktraceThread(thread_obj, thread_obj, tid);
|
||||||
|
}
|
||||||
56
libbacktrace/UnwindCurrent.h
Normal file
56
libbacktrace/UnwindCurrent.h
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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_UNWIND_CURRENT_H
|
||||||
|
#define _LIBBACKTRACE_UNWIND_CURRENT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Backtrace.h"
|
||||||
|
#include "BacktraceThread.h"
|
||||||
|
|
||||||
|
#define UNW_LOCAL_ONLY
|
||||||
|
#include <libunwind.h>
|
||||||
|
|
||||||
|
class UnwindCurrent : public BacktraceImpl {
|
||||||
|
public:
|
||||||
|
UnwindCurrent();
|
||||||
|
virtual ~UnwindCurrent();
|
||||||
|
|
||||||
|
virtual bool Unwind(size_t num_ignore_frames);
|
||||||
|
|
||||||
|
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
|
||||||
|
|
||||||
|
bool UnwindFromContext(size_t num_ignore_frames, bool resolve);
|
||||||
|
|
||||||
|
void ExtractContext(void* sigcontext);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
unw_context_t context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnwindThread : public UnwindCurrent, public BacktraceThreadInterface {
|
||||||
|
public:
|
||||||
|
UnwindThread();
|
||||||
|
virtual ~UnwindThread();
|
||||||
|
|
||||||
|
virtual bool Init();
|
||||||
|
|
||||||
|
virtual void ThreadUnwind(
|
||||||
|
siginfo_t* siginfo, void* sigcontext, size_t num_ignore_frames);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _LIBBACKTRACE_UNWIND_CURRENT_H
|
||||||
136
libbacktrace/UnwindPtrace.cpp
Normal file
136
libbacktrace/UnwindPtrace.cpp
Normal file
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define LOG_TAG "libbacktrace"
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
#include <libunwind.h>
|
||||||
|
#include <libunwind-ptrace.h>
|
||||||
|
|
||||||
|
#include "UnwindPtrace.h"
|
||||||
|
|
||||||
|
UnwindPtrace::UnwindPtrace() : addr_space_(NULL), upt_info_(NULL) {
|
||||||
|
}
|
||||||
|
|
||||||
|
UnwindPtrace::~UnwindPtrace() {
|
||||||
|
if (upt_info_) {
|
||||||
|
_UPT_destroy(upt_info_);
|
||||||
|
upt_info_ = NULL;
|
||||||
|
}
|
||||||
|
if (addr_space_) {
|
||||||
|
unw_destroy_addr_space(addr_space_);
|
||||||
|
addr_space_ = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnwindPtrace::Unwind(size_t num_ignore_frames) {
|
||||||
|
addr_space_ = unw_create_addr_space(&_UPT_accessors, 0);
|
||||||
|
if (!addr_space_) {
|
||||||
|
ALOGW("UnwindPtrace::Unwind: unw_create_addr_space failed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
upt_info_ = reinterpret_cast<struct UPT_info*>(_UPT_create(backtrace_obj_->Tid()));
|
||||||
|
if (!upt_info_) {
|
||||||
|
ALOGW("UnwindPtrace::Unwind: Failed to create upt info.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_t* backtrace = GetBacktraceData();
|
||||||
|
backtrace->num_frames = 0;
|
||||||
|
|
||||||
|
unw_cursor_t cursor;
|
||||||
|
int ret = unw_init_remote(&cursor, addr_space_, upt_info_);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindPtrace::Unwind: unw_init_remote failed %d\n", ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
unw_word_t pc;
|
||||||
|
ret = unw_get_reg(&cursor, UNW_REG_IP, &pc);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindPtrace::Unwind: Failed to read IP %d\n", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unw_word_t sp;
|
||||||
|
ret = unw_get_reg(&cursor, UNW_REG_SP, &sp);
|
||||||
|
if (ret < 0) {
|
||||||
|
ALOGW("UnwindPtrace::Unwind: Failed to read SP %d\n", ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_ignore_frames == 0) {
|
||||||
|
size_t num_frames = backtrace->num_frames;
|
||||||
|
backtrace_frame_data_t* frame = &backtrace->frames[num_frames];
|
||||||
|
frame->pc = static_cast<uintptr_t>(pc);
|
||||||
|
frame->sp = static_cast<uintptr_t>(sp);
|
||||||
|
frame->stack_size = 0;
|
||||||
|
frame->map_name = NULL;
|
||||||
|
frame->map_offset = 0;
|
||||||
|
frame->func_name = NULL;
|
||||||
|
frame->func_offset = 0;
|
||||||
|
|
||||||
|
if (num_frames > 0) {
|
||||||
|
backtrace_frame_data_t* prev = &backtrace->frames[num_frames-1];
|
||||||
|
prev->stack_size = frame->sp - prev->sp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string func_name = backtrace_obj_->GetFunctionName(frame->pc, &frame->func_offset);
|
||||||
|
if (!func_name.empty()) {
|
||||||
|
frame->func_name = strdup(func_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
uintptr_t map_start;
|
||||||
|
frame->map_name = backtrace_obj_->GetMapName(frame->pc, &map_start);
|
||||||
|
if (frame->map_name) {
|
||||||
|
frame->map_offset = frame->pc - map_start;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace->num_frames++;
|
||||||
|
} else {
|
||||||
|
num_ignore_frames--;
|
||||||
|
}
|
||||||
|
ret = unw_step (&cursor);
|
||||||
|
} while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UnwindPtrace::GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset) {
|
||||||
|
*offset = 0;
|
||||||
|
char buf[512];
|
||||||
|
unw_word_t value;
|
||||||
|
if (unw_get_proc_name_by_ip(addr_space_, pc, buf, sizeof(buf), &value,
|
||||||
|
upt_info_) >= 0 && buf[0] != '\0') {
|
||||||
|
*offset = static_cast<uintptr_t>(value);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// C++ object creation function.
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
Backtrace* CreatePtraceObj(pid_t pid, pid_t tid) {
|
||||||
|
return new BacktracePtrace(new UnwindPtrace(), pid, tid);
|
||||||
|
}
|
||||||
|
|
@ -14,21 +14,27 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _UNWIND_H
|
#ifndef _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||||
#define _UNWIND_H
|
#define _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||||
|
|
||||||
bool local_get_data(backtrace_t* backtrace);
|
#include <string>
|
||||||
|
|
||||||
void local_free_data(backtrace_t* backtrace);
|
#include "Backtrace.h"
|
||||||
|
|
||||||
char* local_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
#include <libunwind.h>
|
||||||
uintptr_t* offset);
|
|
||||||
|
|
||||||
bool remote_get_data(backtrace_t* backtrace);
|
class UnwindPtrace : public BacktraceImpl {
|
||||||
|
public:
|
||||||
|
UnwindPtrace();
|
||||||
|
virtual ~UnwindPtrace();
|
||||||
|
|
||||||
void remote_free_data(backtrace_t* backtrace);
|
virtual bool Unwind(size_t num_ignore_frames);
|
||||||
|
|
||||||
char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
virtual std::string GetFunctionNameRaw(uintptr_t pc, uintptr_t* offset);
|
||||||
uintptr_t* offset);
|
|
||||||
|
|
||||||
#endif /* _UNWIND_H */
|
private:
|
||||||
|
unw_addr_space_t addr_space_;
|
||||||
|
struct UPT_info* upt_info_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _LIBBACKTRACE_UNWIND_PTRACE_H
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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 <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/ptrace.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#define FINISH(pid) dump_frames(&backtrace); if (pid < 0) exit(1); else return false;
|
|
||||||
|
|
||||||
#define WAIT_INTERVAL_USECS 1000
|
|
||||||
|
|
||||||
// Prototypes for functions in the test library.
|
|
||||||
int test_level_one(int, int, int, int, bool (*)(pid_t));
|
|
||||||
|
|
||||||
int test_recursive_call(int, bool (*)(pid_t));
|
|
||||||
|
|
||||||
void dump_frames(const backtrace_t* backtrace) {
|
|
||||||
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
|
||||||
printf("%zu ", i);
|
|
||||||
if (backtrace->frames[i].map_name) {
|
|
||||||
printf("%s", backtrace->frames[i].map_name);
|
|
||||||
} else {
|
|
||||||
printf("<unknown>");
|
|
||||||
}
|
|
||||||
if (backtrace->frames[i].proc_name) {
|
|
||||||
printf(" %s", backtrace->frames[i].proc_name);
|
|
||||||
if (backtrace->frames[i].proc_offset) {
|
|
||||||
printf("+%" PRIuPTR, backtrace->frames[i].proc_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_for_stop(pid_t pid, size_t max_usecs_to_wait) {
|
|
||||||
siginfo_t si;
|
|
||||||
size_t usecs_waited = 0;
|
|
||||||
|
|
||||||
while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
|
|
||||||
if (usecs_waited >= max_usecs_to_wait) {
|
|
||||||
printf("The process did not get to a stopping point in %zu usecs.\n",
|
|
||||||
usecs_waited);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(WAIT_INTERVAL_USECS);
|
|
||||||
usecs_waited += WAIT_INTERVAL_USECS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool check_frame(const backtrace_t* backtrace, size_t frame_num,
|
|
||||||
const char* expected_name) {
|
|
||||||
if (backtrace->frames[frame_num].proc_name == NULL) {
|
|
||||||
printf(" Frame %zu function name expected %s, real value is NULL.\n",
|
|
||||||
frame_num, expected_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (strcmp(backtrace->frames[frame_num].proc_name, expected_name) != 0) {
|
|
||||||
printf(" Frame %zu function name expected %s, real value is %s.\n",
|
|
||||||
frame_num, expected_name, backtrace->frames[frame_num].proc_name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verify_level_backtrace(pid_t pid) {
|
|
||||||
const char* test_type;
|
|
||||||
if (pid < 0) {
|
|
||||||
test_type = "current";
|
|
||||||
} else {
|
|
||||||
test_type = "running";
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace_t backtrace;
|
|
||||||
if (!backtrace_get_data(&backtrace, pid)) {
|
|
||||||
printf(" backtrace_get_data failed on %s process.\n", test_type);
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backtrace.num_frames == 0) {
|
|
||||||
printf(" backtrace_get_data returned no frames for %s process.\n",
|
|
||||||
test_type);
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look through the frames starting at the highest to find the
|
|
||||||
// frame we want.
|
|
||||||
size_t frame_num = 0;
|
|
||||||
for (size_t i = backtrace.num_frames-1; i > 2; i--) {
|
|
||||||
if (backtrace.frames[i].proc_name != NULL &&
|
|
||||||
strcmp(backtrace.frames[i].proc_name, "test_level_one") == 0) {
|
|
||||||
frame_num = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!frame_num) {
|
|
||||||
printf(" backtrace_get_data did not include the test_level_one frame.\n");
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!check_frame(&backtrace, frame_num, "test_level_one")) {
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
if (!check_frame(&backtrace, frame_num-1, "test_level_two")) {
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
if (!check_frame(&backtrace, frame_num-2, "test_level_three")) {
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
if (!check_frame(&backtrace, frame_num-3, "test_level_four")) {
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
backtrace_free_data(&backtrace);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool verify_max_backtrace(pid_t pid) {
|
|
||||||
const char* test_type;
|
|
||||||
if (pid < 0) {
|
|
||||||
test_type = "current";
|
|
||||||
} else {
|
|
||||||
test_type = "running";
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace_t backtrace;
|
|
||||||
if (!backtrace_get_data(&backtrace, pid)) {
|
|
||||||
printf(" backtrace_get_data failed on %s process.\n", test_type);
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backtrace.num_frames != MAX_BACKTRACE_FRAMES) {
|
|
||||||
printf(" backtrace_get_data %s process max frame check failed:\n",
|
|
||||||
test_type);
|
|
||||||
printf(" Expected num frames to be %zu, found %zu\n",
|
|
||||||
MAX_BACKTRACE_FRAMES, backtrace.num_frames);
|
|
||||||
FINISH(pid);
|
|
||||||
}
|
|
||||||
backtrace_free_data(&backtrace);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void verify_proc_test(pid_t pid, bool (*verify_func)(pid_t)) {
|
|
||||||
printf(" Waiting 5 seconds for process to get to infinite loop.\n");
|
|
||||||
sleep(5);
|
|
||||||
if (ptrace(PTRACE_ATTACH, pid, 0, 0) < 0) {
|
|
||||||
printf("Failed to attach to pid %d\n", pid);
|
|
||||||
kill(pid, SIGKILL);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait up to 1 second for the process to get to a point that we can trace it.
|
|
||||||
wait_for_stop(pid, 1000000);
|
|
||||||
|
|
||||||
bool pass = verify_func(pid);
|
|
||||||
if (ptrace(PTRACE_DETACH, pid, 0, 0) != 0) {
|
|
||||||
printf("Failed to detach from pid %d\n", pid);
|
|
||||||
kill(pid, SIGKILL);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
kill(pid, SIGKILL);
|
|
||||||
int status;
|
|
||||||
if (waitpid(pid, &status, 0) != pid) {
|
|
||||||
printf("Forked process did not terminate properly.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pass) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
printf("Running level test on current process...\n");
|
|
||||||
int value = test_level_one(1, 2, 3, 4, verify_level_backtrace);
|
|
||||||
if (value == 0) {
|
|
||||||
printf("This should never happen.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
printf(" Passed.\n");
|
|
||||||
|
|
||||||
printf("Running max level test on current process...\n");
|
|
||||||
value = test_recursive_call(MAX_BACKTRACE_FRAMES+10, verify_max_backtrace);
|
|
||||||
if (value == 0) {
|
|
||||||
printf("This should never happen.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
printf(" Passed.\n");
|
|
||||||
|
|
||||||
printf("Running level test on process...\n");
|
|
||||||
pid_t pid;
|
|
||||||
if ((pid = fork()) == 0) {
|
|
||||||
value = test_level_one(1, 2, 3, 4, NULL);
|
|
||||||
if (value == 0) {
|
|
||||||
printf("This should never happen.\n");
|
|
||||||
}
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
verify_proc_test(pid, verify_level_backtrace);
|
|
||||||
printf(" Passed.\n");
|
|
||||||
|
|
||||||
printf("Running max frame test on process...\n");
|
|
||||||
if ((pid = fork()) == 0) {
|
|
||||||
value = test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL);
|
|
||||||
if (value == 0) {
|
|
||||||
printf("This should never happen.\n");
|
|
||||||
}
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
verify_proc_test(pid, verify_max_backtrace);
|
|
||||||
printf(" Passed.\n");
|
|
||||||
|
|
||||||
printf("All tests passed.\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
660
libbacktrace/backtrace_test.cpp
Normal file
660
libbacktrace/backtrace_test.cpp
Normal file
|
|
@ -0,0 +1,660 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 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 <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ptrace.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <backtrace/backtrace.h>
|
||||||
|
|
||||||
|
#include <cutils/atomic.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "thread_utils.h"
|
||||||
|
|
||||||
|
// Number of microseconds per milliseconds.
|
||||||
|
#define US_PER_MSEC 1000
|
||||||
|
|
||||||
|
// Number of nanoseconds in a second.
|
||||||
|
#define NS_PER_SEC 1000000000ULL
|
||||||
|
|
||||||
|
// Number of simultaneous dumping operations to perform.
|
||||||
|
#define NUM_THREADS 20
|
||||||
|
|
||||||
|
// Number of simultaneous threads running in our forked process.
|
||||||
|
#define NUM_PTRACE_THREADS 5
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
pid_t tid;
|
||||||
|
int32_t state;
|
||||||
|
pthread_t threadId;
|
||||||
|
} thread_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
thread_t thread;
|
||||||
|
backtrace_context_t context;
|
||||||
|
int32_t* now;
|
||||||
|
int32_t done;
|
||||||
|
} dump_thread_t;
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// Prototypes for functions in the test library.
|
||||||
|
int test_level_one(int, int, int, int, void (*)(void*), void*);
|
||||||
|
|
||||||
|
int test_recursive_call(int, void (*)(void*), void*);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t NanoTime() {
|
||||||
|
struct timespec t = { 0, 0 };
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||||
|
return static_cast<uint64_t>(t.tv_sec * NS_PER_SEC + t.tv_nsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DumpFrames(const backtrace_context_t* context) {
|
||||||
|
if (context->backtrace->num_frames == 0) {
|
||||||
|
printf(" No frames to dump\n");
|
||||||
|
} else {
|
||||||
|
char line[512];
|
||||||
|
for (size_t i = 0; i < context->backtrace->num_frames; i++) {
|
||||||
|
backtrace_format_frame_data(context, i, line, sizeof(line));
|
||||||
|
printf(" %s\n", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WaitForStop(pid_t pid) {
|
||||||
|
uint64_t start = NanoTime();
|
||||||
|
|
||||||
|
siginfo_t si;
|
||||||
|
while (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) < 0 && (errno == EINTR || errno == ESRCH)) {
|
||||||
|
if ((NanoTime() - start) > NS_PER_SEC) {
|
||||||
|
printf("The process did not get to a stopping point in 1 second.\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
usleep(US_PER_MSEC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadyLevelBacktrace(const backtrace_t* backtrace) {
|
||||||
|
// See if test_level_four is in the backtrace.
|
||||||
|
bool found = false;
|
||||||
|
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
||||||
|
if (backtrace->frames[i].func_name != NULL &&
|
||||||
|
strcmp(backtrace->frames[i].func_name, "test_level_four") == 0) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyLevelDump(const backtrace_t* backtrace) {
|
||||||
|
ASSERT_GT(backtrace->num_frames, static_cast<size_t>(0));
|
||||||
|
ASSERT_LT(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES));
|
||||||
|
|
||||||
|
// Look through the frames starting at the highest to find the
|
||||||
|
// frame we want.
|
||||||
|
size_t frame_num = 0;
|
||||||
|
for (size_t i = backtrace->num_frames-1; i > 2; i--) {
|
||||||
|
if (backtrace->frames[i].func_name != NULL &&
|
||||||
|
strcmp(backtrace->frames[i].func_name, "test_level_one") == 0) {
|
||||||
|
frame_num = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASSERT_GT(frame_num, static_cast<size_t>(0));
|
||||||
|
|
||||||
|
ASSERT_TRUE(NULL != backtrace->frames[frame_num].func_name);
|
||||||
|
ASSERT_STREQ(backtrace->frames[frame_num].func_name, "test_level_one");
|
||||||
|
ASSERT_TRUE(NULL != backtrace->frames[frame_num-1].func_name);
|
||||||
|
ASSERT_STREQ(backtrace->frames[frame_num-1].func_name, "test_level_two");
|
||||||
|
ASSERT_TRUE(NULL != backtrace->frames[frame_num-2].func_name);
|
||||||
|
ASSERT_STREQ(backtrace->frames[frame_num-2].func_name, "test_level_three");
|
||||||
|
ASSERT_TRUE(NULL != backtrace->frames[frame_num-3].func_name);
|
||||||
|
ASSERT_STREQ(backtrace->frames[frame_num-3].func_name, "test_level_four");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyLevelBacktrace(void*) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
|
||||||
|
|
||||||
|
VerifyLevelDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadyMaxBacktrace(const backtrace_t* backtrace) {
|
||||||
|
return (backtrace->num_frames == MAX_BACKTRACE_FRAMES);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyMaxDump(const backtrace_t* backtrace) {
|
||||||
|
ASSERT_EQ(backtrace->num_frames, static_cast<size_t>(MAX_BACKTRACE_FRAMES));
|
||||||
|
// Verify that the last frame is our recursive call.
|
||||||
|
ASSERT_TRUE(NULL != backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name);
|
||||||
|
ASSERT_STREQ(backtrace->frames[MAX_BACKTRACE_FRAMES-1].func_name,
|
||||||
|
"test_recursive_call");
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyMaxBacktrace(void*) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
|
||||||
|
|
||||||
|
VerifyMaxDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadSetState(void* data) {
|
||||||
|
thread_t* thread = reinterpret_cast<thread_t*>(data);
|
||||||
|
android_atomic_acquire_store(1, &thread->state);
|
||||||
|
volatile int i = 0;
|
||||||
|
while (thread->state) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyThreadTest(pid_t tid, void (*VerifyFunc)(const backtrace_t*)) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
backtrace_create_context(&context, getpid(), tid, 0);
|
||||||
|
|
||||||
|
VerifyFunc(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WaitForNonZero(int32_t* value, uint64_t seconds) {
|
||||||
|
uint64_t start = NanoTime();
|
||||||
|
do {
|
||||||
|
if (android_atomic_acquire_load(value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} while ((NanoTime() - start) < seconds * NS_PER_SEC);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, local_trace) {
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, NULL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyIgnoreFrames(
|
||||||
|
const backtrace_t* bt_all, const backtrace_t* bt_ign1,
|
||||||
|
const backtrace_t* bt_ign2, const char* cur_proc) {
|
||||||
|
EXPECT_EQ(bt_all->num_frames, bt_ign1->num_frames + 1);
|
||||||
|
EXPECT_EQ(bt_all->num_frames, bt_ign2->num_frames + 2);
|
||||||
|
|
||||||
|
// Check all of the frames are the same > the current frame.
|
||||||
|
bool check = (cur_proc == NULL);
|
||||||
|
for (size_t i = 0; i < bt_ign2->num_frames; i++) {
|
||||||
|
if (check) {
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].pc, bt_ign1->frames[i+1].pc);
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].sp, bt_ign1->frames[i+1].sp);
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_ign1->frames[i+1].stack_size);
|
||||||
|
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].pc, bt_all->frames[i+2].pc);
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].sp, bt_all->frames[i+2].sp);
|
||||||
|
EXPECT_EQ(bt_ign2->frames[i].stack_size, bt_all->frames[i+2].stack_size);
|
||||||
|
}
|
||||||
|
if (!check && bt_ign2->frames[i].func_name &&
|
||||||
|
strcmp(bt_ign2->frames[i].func_name, cur_proc) == 0) {
|
||||||
|
check = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyLevelIgnoreFrames(void*) {
|
||||||
|
backtrace_context_t all;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&all, -1, -1, 0));
|
||||||
|
ASSERT_TRUE(all.backtrace != NULL);
|
||||||
|
|
||||||
|
backtrace_context_t ign1;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign1, -1, -1, 1));
|
||||||
|
ASSERT_TRUE(ign1.backtrace != NULL);
|
||||||
|
|
||||||
|
backtrace_context_t ign2;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign2, -1, -1, 2));
|
||||||
|
ASSERT_TRUE(ign2.backtrace != NULL);
|
||||||
|
|
||||||
|
VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace,
|
||||||
|
"VerifyLevelIgnoreFrames");
|
||||||
|
|
||||||
|
backtrace_destroy_context(&all);
|
||||||
|
backtrace_destroy_context(&ign1);
|
||||||
|
backtrace_destroy_context(&ign2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, local_trace_ignore_frames) {
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelIgnoreFrames, NULL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, local_max_trace) {
|
||||||
|
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxBacktrace, NULL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProcTest(pid_t pid, pid_t tid,
|
||||||
|
bool (*ReadyFunc)(const backtrace_t*),
|
||||||
|
void (*VerifyFunc)(const backtrace_t*)) {
|
||||||
|
pid_t ptrace_tid;
|
||||||
|
if (tid < 0) {
|
||||||
|
ptrace_tid = pid;
|
||||||
|
} else {
|
||||||
|
ptrace_tid = tid;
|
||||||
|
}
|
||||||
|
uint64_t start = NanoTime();
|
||||||
|
bool verified = false;
|
||||||
|
do {
|
||||||
|
usleep(US_PER_MSEC);
|
||||||
|
if (ptrace(PTRACE_ATTACH, ptrace_tid, 0, 0) == 0) {
|
||||||
|
// Wait for the process to get to a stopping point.
|
||||||
|
WaitForStop(ptrace_tid);
|
||||||
|
|
||||||
|
backtrace_context_t context;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, pid, tid, 0));
|
||||||
|
if (ReadyFunc(context.backtrace)) {
|
||||||
|
VerifyFunc(context.backtrace);
|
||||||
|
verified = true;
|
||||||
|
}
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
ASSERT_TRUE(ptrace(PTRACE_DETACH, ptrace_tid, 0, 0) == 0);
|
||||||
|
}
|
||||||
|
// If 5 seconds have passed, then we are done.
|
||||||
|
} while (!verified && (NanoTime() - start) <= 5 * NS_PER_SEC);
|
||||||
|
ASSERT_TRUE(verified);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, ptrace_trace) {
|
||||||
|
pid_t pid;
|
||||||
|
if ((pid = fork()) == 0) {
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyLevelDump);
|
||||||
|
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
int status;
|
||||||
|
ASSERT_EQ(waitpid(pid, &status, 0), pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, ptrace_max_trace) {
|
||||||
|
pid_t pid;
|
||||||
|
if ((pid = fork()) == 0) {
|
||||||
|
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, NULL, NULL), 0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
VerifyProcTest(pid, -1, ReadyMaxBacktrace, VerifyMaxDump);
|
||||||
|
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
int status;
|
||||||
|
ASSERT_EQ(waitpid(pid, &status, 0), pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyProcessIgnoreFrames(const backtrace_t* bt_all) {
|
||||||
|
pid_t pid = bt_all->pid;
|
||||||
|
|
||||||
|
backtrace_context_t ign1;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign1, pid, -1, 1));
|
||||||
|
ASSERT_TRUE(ign1.backtrace != NULL);
|
||||||
|
|
||||||
|
backtrace_context_t ign2;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign2, pid, -1, 2));
|
||||||
|
ASSERT_TRUE(ign2.backtrace != NULL);
|
||||||
|
|
||||||
|
VerifyIgnoreFrames(bt_all, ign1.backtrace, ign2.backtrace, NULL);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&ign1);
|
||||||
|
backtrace_destroy_context(&ign2);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, ptrace_ignore_frames) {
|
||||||
|
pid_t pid;
|
||||||
|
if ((pid = fork()) == 0) {
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
VerifyProcTest(pid, -1, ReadyLevelBacktrace, VerifyProcessIgnoreFrames);
|
||||||
|
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
int status;
|
||||||
|
ASSERT_EQ(waitpid(pid, &status, 0), pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a process with multiple threads and dump all of the threads.
|
||||||
|
void* PtraceThreadLevelRun(void*) {
|
||||||
|
EXPECT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetThreads(pid_t pid, std::vector<pid_t>* threads) {
|
||||||
|
// Get the list of tasks.
|
||||||
|
char task_path[128];
|
||||||
|
snprintf(task_path, sizeof(task_path), "/proc/%d/task", pid);
|
||||||
|
|
||||||
|
DIR* tasks_dir = opendir(task_path);
|
||||||
|
ASSERT_TRUE(tasks_dir != NULL);
|
||||||
|
struct dirent* entry;
|
||||||
|
while ((entry = readdir(tasks_dir)) != NULL) {
|
||||||
|
char* end;
|
||||||
|
pid_t tid = strtoul(entry->d_name, &end, 10);
|
||||||
|
if (*end == '\0') {
|
||||||
|
threads->push_back(tid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(tasks_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, ptrace_threads) {
|
||||||
|
pid_t pid;
|
||||||
|
if ((pid = fork()) == 0) {
|
||||||
|
for (size_t i = 0; i < NUM_PTRACE_THREADS; i++) {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_TRUE(pthread_create(&thread, &attr, PtraceThreadLevelRun, NULL) == 0);
|
||||||
|
}
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, NULL, NULL), 0);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see that all of the threads are running before unwinding.
|
||||||
|
std::vector<pid_t> threads;
|
||||||
|
uint64_t start = NanoTime();
|
||||||
|
do {
|
||||||
|
usleep(US_PER_MSEC);
|
||||||
|
threads.clear();
|
||||||
|
GetThreads(pid, &threads);
|
||||||
|
} while ((threads.size() != NUM_PTRACE_THREADS + 1) &&
|
||||||
|
((NanoTime() - start) <= 5 * NS_PER_SEC));
|
||||||
|
ASSERT_EQ(threads.size(), static_cast<size_t>(NUM_PTRACE_THREADS + 1));
|
||||||
|
|
||||||
|
ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
|
||||||
|
WaitForStop(pid);
|
||||||
|
for (std::vector<int>::const_iterator it = threads.begin(); it != threads.end(); ++it) {
|
||||||
|
// Skip the current forked process, we only care about the threads.
|
||||||
|
if (pid == *it) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
VerifyProcTest(pid, *it, ReadyLevelBacktrace, VerifyLevelDump);
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
|
||||||
|
|
||||||
|
kill(pid, SIGKILL);
|
||||||
|
int status;
|
||||||
|
ASSERT_EQ(waitpid(pid, &status, 0), pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyLevelThread(void*) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0));
|
||||||
|
|
||||||
|
VerifyLevelDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_current_level) {
|
||||||
|
ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelThread, NULL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyMaxThread(void*) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, getpid(), gettid(), 0));
|
||||||
|
|
||||||
|
VerifyMaxDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_current_max) {
|
||||||
|
ASSERT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, VerifyMaxThread, NULL), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ThreadLevelRun(void* data) {
|
||||||
|
thread_t* thread = reinterpret_cast<thread_t*>(data);
|
||||||
|
|
||||||
|
thread->tid = gettid();
|
||||||
|
EXPECT_NE(test_level_one(1, 2, 3, 4, ThreadSetState, data), 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_level_trace) {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
thread_t thread_data = { 0, 0, 0 };
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
|
||||||
|
|
||||||
|
// Wait up to 2 seconds for the tid to be set.
|
||||||
|
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
|
||||||
|
|
||||||
|
// Save the current signal action and make sure it is restored afterwards.
|
||||||
|
struct sigaction cur_action;
|
||||||
|
ASSERT_TRUE(sigaction(SIGURG, NULL, &cur_action) == 0);
|
||||||
|
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid,0));
|
||||||
|
|
||||||
|
VerifyLevelDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
|
||||||
|
// Tell the thread to exit its infinite loop.
|
||||||
|
android_atomic_acquire_store(0, &thread_data.state);
|
||||||
|
|
||||||
|
// Verify that the old action was restored.
|
||||||
|
struct sigaction new_action;
|
||||||
|
ASSERT_TRUE(sigaction(SIGURG, NULL, &new_action) == 0);
|
||||||
|
EXPECT_EQ(cur_action.sa_sigaction, new_action.sa_sigaction);
|
||||||
|
EXPECT_EQ(cur_action.sa_flags, new_action.sa_flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_ignore_frames) {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
thread_t thread_data = { 0, 0, 0 };
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_TRUE(pthread_create(&thread, &attr, ThreadLevelRun, &thread_data) == 0);
|
||||||
|
|
||||||
|
// Wait up to 2 seconds for the tid to be set.
|
||||||
|
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
|
||||||
|
|
||||||
|
backtrace_context_t all;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&all, getpid(), thread_data.tid, 0));
|
||||||
|
|
||||||
|
backtrace_context_t ign1;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign1, getpid(), thread_data.tid, 1));
|
||||||
|
|
||||||
|
backtrace_context_t ign2;
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&ign2, getpid(), thread_data.tid, 2));
|
||||||
|
|
||||||
|
VerifyIgnoreFrames(all.backtrace, ign1.backtrace, ign2.backtrace, NULL);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&all);
|
||||||
|
backtrace_destroy_context(&ign1);
|
||||||
|
backtrace_destroy_context(&ign2);
|
||||||
|
|
||||||
|
// Tell the thread to exit its infinite loop.
|
||||||
|
android_atomic_acquire_store(0, &thread_data.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ThreadMaxRun(void* data) {
|
||||||
|
thread_t* thread = reinterpret_cast<thread_t*>(data);
|
||||||
|
|
||||||
|
thread->tid = gettid();
|
||||||
|
EXPECT_NE(test_recursive_call(MAX_BACKTRACE_FRAMES+10, ThreadSetState, data), 0);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_max_trace) {
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
|
||||||
|
thread_t thread_data = { 0, 0, 0 };
|
||||||
|
pthread_t thread;
|
||||||
|
ASSERT_TRUE(pthread_create(&thread, &attr, ThreadMaxRun, &thread_data) == 0);
|
||||||
|
|
||||||
|
// Wait for the tid to be set.
|
||||||
|
ASSERT_TRUE(WaitForNonZero(&thread_data.state, 2));
|
||||||
|
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, getpid(), thread_data.tid, 0));
|
||||||
|
|
||||||
|
VerifyMaxDump(context.backtrace);
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
|
||||||
|
// Tell the thread to exit its infinite loop.
|
||||||
|
android_atomic_acquire_store(0, &thread_data.state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ThreadDump(void* data) {
|
||||||
|
dump_thread_t* dump = reinterpret_cast<dump_thread_t*>(data);
|
||||||
|
while (true) {
|
||||||
|
if (android_atomic_acquire_load(dump->now)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dump->context.data = NULL;
|
||||||
|
dump->context.backtrace = NULL;
|
||||||
|
|
||||||
|
// The status of the actual unwind will be checked elsewhere.
|
||||||
|
backtrace_create_context(&dump->context, getpid(), dump->thread.tid, 0);
|
||||||
|
|
||||||
|
android_atomic_acquire_store(1, &dump->done);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, thread_multiple_dump) {
|
||||||
|
// Dump NUM_THREADS simultaneously.
|
||||||
|
std::vector<thread_t> runners(NUM_THREADS);
|
||||||
|
std::vector<dump_thread_t> dumpers(NUM_THREADS);
|
||||||
|
|
||||||
|
pthread_attr_t attr;
|
||||||
|
pthread_attr_init(&attr);
|
||||||
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||||
|
for (size_t i = 0; i < NUM_THREADS; i++) {
|
||||||
|
// Launch the runners, they will spin in hard loops doing nothing.
|
||||||
|
runners[i].tid = 0;
|
||||||
|
runners[i].state = 0;
|
||||||
|
ASSERT_TRUE(pthread_create(&runners[i].threadId, &attr, ThreadMaxRun, &runners[i]) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for tids to be set.
|
||||||
|
for (std::vector<thread_t>::iterator it = runners.begin(); it != runners.end(); ++it) {
|
||||||
|
ASSERT_TRUE(WaitForNonZero(&it->state, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start all of the dumpers at once, they will spin until they are signalled
|
||||||
|
// to begin their dump run.
|
||||||
|
int32_t dump_now = 0;
|
||||||
|
for (size_t i = 0; i < NUM_THREADS; i++) {
|
||||||
|
dumpers[i].thread.tid = runners[i].tid;
|
||||||
|
dumpers[i].thread.state = 0;
|
||||||
|
dumpers[i].done = 0;
|
||||||
|
dumpers[i].now = &dump_now;
|
||||||
|
|
||||||
|
ASSERT_TRUE(pthread_create(&dumpers[i].thread.threadId, &attr, ThreadDump, &dumpers[i]) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start all of the dumpers going at once.
|
||||||
|
android_atomic_acquire_store(1, &dump_now);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < NUM_THREADS; i++) {
|
||||||
|
ASSERT_TRUE(WaitForNonZero(&dumpers[i].done, 10));
|
||||||
|
|
||||||
|
// Tell the runner thread to exit its infinite loop.
|
||||||
|
android_atomic_acquire_store(0, &runners[i].state);
|
||||||
|
|
||||||
|
ASSERT_TRUE(dumpers[i].context.backtrace != NULL);
|
||||||
|
VerifyMaxDump(dumpers[i].context.backtrace);
|
||||||
|
backtrace_destroy_context(&dumpers[i].context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(libbacktrace, format_test) {
|
||||||
|
backtrace_context_t context;
|
||||||
|
|
||||||
|
ASSERT_TRUE(backtrace_create_context(&context, -1, -1, 0));
|
||||||
|
ASSERT_TRUE(context.backtrace != NULL);
|
||||||
|
|
||||||
|
backtrace_frame_data_t* frame = &context.backtrace->frames[1];
|
||||||
|
backtrace_frame_data_t save_frame = *frame;
|
||||||
|
|
||||||
|
memset(frame, 0, sizeof(backtrace_frame_data_t));
|
||||||
|
char buf[512];
|
||||||
|
backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
|
||||||
|
#if defined(__LP64__)
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 0000000000000000 <unknown>");
|
||||||
|
#else
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 00000000 <unknown>");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frame->pc = 0x12345678;
|
||||||
|
frame->map_name = "MapFake";
|
||||||
|
backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
|
||||||
|
#if defined(__LP64__)
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake");
|
||||||
|
#else
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 12345678 MapFake");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frame->func_name = "ProcFake";
|
||||||
|
backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
|
||||||
|
#if defined(__LP64__)
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake)");
|
||||||
|
#else
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake)");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
frame->func_offset = 645;
|
||||||
|
backtrace_format_frame_data(&context, 1, buf, sizeof(buf));
|
||||||
|
#if defined(__LP64__)
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 0000000012345678 MapFake (ProcFake+645)");
|
||||||
|
#else
|
||||||
|
EXPECT_STREQ(buf, "#01 pc 12345678 MapFake (ProcFake+645)");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*frame = save_frame;
|
||||||
|
|
||||||
|
backtrace_destroy_context(&context);
|
||||||
|
}
|
||||||
|
|
@ -14,13 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
int test_level_four(int one, int two, int three, int four,
|
int test_level_four(int one, int two, int three, int four,
|
||||||
bool (*callback_func)(pid_t)) {
|
void (*callback_func)(void*), void* data) {
|
||||||
if (callback_func != NULL) {
|
if (callback_func != NULL) {
|
||||||
callback_func(-1);
|
callback_func(data);
|
||||||
} else {
|
} else {
|
||||||
while (1) {
|
while (1) {
|
||||||
}
|
}
|
||||||
|
|
@ -29,25 +28,25 @@ int test_level_four(int one, int two, int three, int four,
|
||||||
}
|
}
|
||||||
|
|
||||||
int test_level_three(int one, int two, int three, int four,
|
int test_level_three(int one, int two, int three, int four,
|
||||||
bool (*callback_func)(pid_t)) {
|
void (*callback_func)(void*), void* data) {
|
||||||
return test_level_four(one+3, two+6, three+9, four+12, callback_func) + 3;
|
return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
int test_level_two(int one, int two, int three, int four,
|
int test_level_two(int one, int two, int three, int four,
|
||||||
bool (*callback_func)(pid_t)) {
|
void (*callback_func)(void*), void* data) {
|
||||||
return test_level_three(one+2, two+4, three+6, four+8, callback_func) + 2;
|
return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int test_level_one(int one, int two, int three, int four,
|
int test_level_one(int one, int two, int three, int four,
|
||||||
bool (*callback_func)(pid_t)) {
|
void (*callback_func)(void*), void* data) {
|
||||||
return test_level_two(one+1, two+2, three+3, four+4, callback_func) + 1;
|
return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int test_recursive_call(int level, bool (*callback_func)(pid_t)) {
|
int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
|
||||||
if (level > 0) {
|
if (level > 0) {
|
||||||
return test_recursive_call(level - 1, callback_func) + level;
|
return test_recursive_call(level - 1, callback_func, data) + level;
|
||||||
} else if (callback_func != NULL) {
|
} else if (callback_func != NULL) {
|
||||||
callback_func(-1);
|
callback_func(data);
|
||||||
} else {
|
} else {
|
||||||
while (1) {
|
while (1) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/ptrace.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
|
|
||||||
#include <cutils/log.h>
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
bool backtrace_read_word(const backtrace_t* backtrace, uintptr_t ptr,
|
|
||||||
uint32_t* out_value) {
|
|
||||||
if (ptr & 3) {
|
|
||||||
ALOGW("backtrace_read_word: invalid pointer %p", (void*)ptr);
|
|
||||||
*out_value = (uint32_t)-1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if reading from the current process, or a different process.
|
|
||||||
if (backtrace->tid < 0) {
|
|
||||||
const backtrace_map_info_t* map_info = backtrace_find_map_info(backtrace->map_info_list, ptr);
|
|
||||||
if (map_info && map_info->is_readable) {
|
|
||||||
*out_value = *(uint32_t*)ptr;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
ALOGW("backtrace_read_word: pointer %p not in a readbale map", (void*)ptr);
|
|
||||||
*out_value = (uint32_t)-1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
#if defined(__APPLE__)
|
|
||||||
ALOGW("read_word: MacOS does not support reading from another pid.\n");
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
// ptrace() returns -1 and sets errno when the operation fails.
|
|
||||||
// To disambiguate -1 from a valid result, we clear errno beforehand.
|
|
||||||
errno = 0;
|
|
||||||
*out_value = ptrace(PTRACE_PEEKTEXT, backtrace->tid, (void*)ptr, NULL);
|
|
||||||
if (*out_value == (uint32_t)-1 && errno) {
|
|
||||||
ALOGW("try_get_word: invalid pointer 0x%08x reading from tid %d, "
|
|
||||||
"ptrace() errno=%d", ptr, backtrace->tid, errno);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *backtrace_get_map_info(
|
|
||||||
const backtrace_t* backtrace, uintptr_t pc, uintptr_t* start_pc) {
|
|
||||||
const backtrace_map_info_t* map_info = backtrace_find_map_info(backtrace->map_info_list, pc);
|
|
||||||
if (map_info) {
|
|
||||||
if (start_pc) {
|
|
||||||
*start_pc = map_info->start;
|
|
||||||
}
|
|
||||||
return map_info->name;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void backtrace_format_frame_data(
|
|
||||||
const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size) {
|
|
||||||
uintptr_t relative_pc;
|
|
||||||
const char* map_name;
|
|
||||||
if (frame->map_name) {
|
|
||||||
map_name = frame->map_name;
|
|
||||||
} else {
|
|
||||||
map_name = "<unknown>";
|
|
||||||
}
|
|
||||||
if (frame->map_offset) {
|
|
||||||
relative_pc = frame->map_offset;
|
|
||||||
} else {
|
|
||||||
relative_pc = frame->pc;
|
|
||||||
}
|
|
||||||
if (frame->proc_name && frame->proc_offset) {
|
|
||||||
snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s (%s+%" PRIuPTR ")",
|
|
||||||
frame_num, (int)sizeof(uintptr_t)*2, relative_pc, map_name,
|
|
||||||
frame->proc_name, frame->proc_offset);
|
|
||||||
} else if (frame->proc_name) {
|
|
||||||
snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s (%s)", frame_num,
|
|
||||||
(int)sizeof(uintptr_t)*2, relative_pc, map_name, frame->proc_name);
|
|
||||||
} else {
|
|
||||||
snprintf(buf, buf_size, "#%02zu pc %0*" PRIxPTR " %s", frame_num,
|
|
||||||
(int)sizeof(uintptr_t)*2, relative_pc, map_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void free_frame_data(backtrace_t* backtrace) {
|
|
||||||
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
|
||||||
if (backtrace->frames[i].proc_name) {
|
|
||||||
free(backtrace->frames[i].proc_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
backtrace->num_frames = 0;
|
|
||||||
}
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <cutils/log.h>
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#include <corkscrew/backtrace.h>
|
|
||||||
|
|
||||||
#define __USE_GNU
|
|
||||||
#include <dlfcn.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "demangle.h"
|
|
||||||
|
|
||||||
bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
|
|
||||||
backtrace->num_frames = 0;
|
|
||||||
backtrace->tid = tid;
|
|
||||||
backtrace->private_data = NULL;
|
|
||||||
backtrace->map_info_list = backtrace_create_map_info_list(tid);
|
|
||||||
|
|
||||||
backtrace_frame_t frames[MAX_BACKTRACE_FRAMES];
|
|
||||||
ssize_t num_frames;
|
|
||||||
if (tid < 0) {
|
|
||||||
// Get data for the current thread.
|
|
||||||
num_frames = unwind_backtrace(frames, 0, MAX_BACKTRACE_FRAMES);
|
|
||||||
} else {
|
|
||||||
// Get data for a different thread.
|
|
||||||
ptrace_context_t* ptrace_context = load_ptrace_context(tid);
|
|
||||||
backtrace->private_data = ptrace_context;
|
|
||||||
|
|
||||||
num_frames = unwind_backtrace_ptrace(
|
|
||||||
tid, ptrace_context, frames, 0, MAX_BACKTRACE_FRAMES);
|
|
||||||
}
|
|
||||||
if (num_frames < 0) {
|
|
||||||
ALOGW("backtrace_get_data: unwind_backtrace_ptrace failed %d\n",
|
|
||||||
num_frames);
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace->num_frames = num_frames;
|
|
||||||
backtrace_frame_data_t* frame;
|
|
||||||
uintptr_t map_start;
|
|
||||||
for (size_t i = 0; i < backtrace->num_frames; i++) {
|
|
||||||
frame = &backtrace->frames[i];
|
|
||||||
frame->pc = frames[i].absolute_pc;
|
|
||||||
frame->sp = frames[i].stack_top;
|
|
||||||
frame->stack_size = frames[i].stack_size;
|
|
||||||
|
|
||||||
frame->map_offset = 0;
|
|
||||||
frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
|
|
||||||
if (frame->map_name) {
|
|
||||||
frame->map_offset = frame->pc - map_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame->proc_offset = 0;
|
|
||||||
frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void backtrace_free_data(backtrace_t* backtrace) {
|
|
||||||
free_frame_data(backtrace);
|
|
||||||
|
|
||||||
if (backtrace->map_info_list) {
|
|
||||||
backtrace_destroy_map_info_list(backtrace->map_info_list);
|
|
||||||
backtrace->map_info_list = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backtrace->private_data) {
|
|
||||||
ptrace_context_t* ptrace_context = (ptrace_context_t*)backtrace->private_data;
|
|
||||||
free_ptrace_context(ptrace_context);
|
|
||||||
backtrace->private_data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
|
||||||
uintptr_t* offset) {
|
|
||||||
const char* symbol_name = NULL;
|
|
||||||
*offset = 0;
|
|
||||||
if (backtrace->tid < 0) {
|
|
||||||
// Get information about the current thread.
|
|
||||||
Dl_info info;
|
|
||||||
const backtrace_map_info_t* map_info;
|
|
||||||
map_info = backtrace_find_map_info(backtrace->map_info_list, pc);
|
|
||||||
if (map_info && dladdr((const void*)pc, &info) && info.dli_sname) {
|
|
||||||
*offset = pc - map_info->start - (uintptr_t)info.dli_saddr + (uintptr_t)info.dli_fbase;
|
|
||||||
symbol_name = info.dli_sname;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get information about a different thread.
|
|
||||||
ptrace_context_t* ptrace_context = (ptrace_context_t*)backtrace->private_data;
|
|
||||||
const map_info_t* map_info;
|
|
||||||
const symbol_t* symbol;
|
|
||||||
find_symbol_ptrace(ptrace_context, pc, &map_info, &symbol);
|
|
||||||
if (symbol) {
|
|
||||||
if (map_info) {
|
|
||||||
*offset = pc - map_info->start - symbol->start;
|
|
||||||
}
|
|
||||||
symbol_name = symbol->name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* name = NULL;
|
|
||||||
if (symbol_name) {
|
|
||||||
name = demangle_symbol_name(symbol_name);
|
|
||||||
if (!name) {
|
|
||||||
name = strdup(symbol_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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 _DEMANGLE_H
|
|
||||||
#define _DEMANGLE_H
|
|
||||||
|
|
||||||
/* Called to demangle a symbol name to be printed. Returns an allocated
|
|
||||||
* string that must be freed by the caller.
|
|
||||||
*/
|
|
||||||
char* demangle_symbol_name(const char* name);
|
|
||||||
|
|
||||||
#endif /* _DEMANGLE_H */
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <cutils/log.h>
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
|
|
||||||
ALOGW("backtrace_get_data: unsupported architecture.\n");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void backtrace_free_data(backtrace_t* backtrace) {
|
|
||||||
ALOGW("backtrace_free_data: unsupported architecture.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool backtrace_read_word(const backtrace_t* backtrace, uintptr_t ptr,
|
|
||||||
uint32_t* out_value) {
|
|
||||||
ALOGW("backtrace_read_word: unsupported architecture.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *backtrace_get_map_info(const backtrace_t* backtrace,
|
|
||||||
uintptr_t pc, uintptr_t* start_pc) {
|
|
||||||
ALOGW("backtrace_get_map_info: unsupported architecture.\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
|
||||||
uintptr_t* offset) {
|
|
||||||
ALOGW("backtrace_get_proc_name: unsupported architecture.\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void backtrace_format_frame_data(
|
|
||||||
const backtrace_frame_data_t* frame, size_t frame_num, char *buf, size_t buf_size) {
|
|
||||||
ALOGW("backtrace_format_frame_data: unsupported architecture.\n");
|
|
||||||
buf[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
@ -14,20 +14,29 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include "thread_utils.h"
|
||||||
|
|
||||||
#include "demangle.h"
|
|
||||||
|
|
||||||
extern char* __cxa_demangle (const char* mangled, char* buf, size_t* len,
|
|
||||||
int* status);
|
|
||||||
|
|
||||||
char* demangle_symbol_name(const char* name) {
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// Mac OS' __cxa_demangle demangles "f" as "float"; last tested on 10.7.
|
|
||||||
if (name != NULL && name[0] != '_') {
|
#include <sys/syscall.h>
|
||||||
return NULL;
|
|
||||||
}
|
// Mac OS >= 10.6 has a system call equivalent to Linux's gettid().
|
||||||
#endif
|
pid_t gettid() {
|
||||||
// __cxa_demangle handles NULL by returning NULL
|
return syscall(SYS_thread_selfid);
|
||||||
return __cxa_demangle(name, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif !defined(__BIONIC__)
|
||||||
|
|
||||||
|
// glibc doesn't implement or export either gettid or tgkill.
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
pid_t gettid() {
|
||||||
|
return syscall(__NR_gettid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int tgkill(int tgid, int tid, int sig) {
|
||||||
|
return syscall(__NR_tgkill, tgid, tid, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -14,12 +14,17 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _COMMON_H
|
#ifndef _LIBBACKTRACE_THREAD_UTILS_H
|
||||||
#define _COMMON_H
|
#define _LIBBACKTRACE_THREAD_UTILS_H
|
||||||
|
|
||||||
#include <backtrace/backtrace.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Common routine to free any data allocated to store frame information. */
|
__BEGIN_DECLS
|
||||||
void free_frame_data(backtrace_t* backtrace);
|
|
||||||
|
|
||||||
#endif /* _COMMON_H */
|
int tgkill(int tgid, int tid, int sig);
|
||||||
|
|
||||||
|
pid_t gettid();
|
||||||
|
|
||||||
|
__END_DECLS
|
||||||
|
|
||||||
|
#endif /* _LIBBACKTRACE_THREAD_UTILS_H */
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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 <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "unwind.h"
|
|
||||||
|
|
||||||
bool backtrace_get_data(backtrace_t* backtrace, pid_t tid) {
|
|
||||||
backtrace->num_frames = 0;
|
|
||||||
backtrace->tid = tid;
|
|
||||||
|
|
||||||
backtrace->map_info_list = backtrace_create_map_info_list(tid);
|
|
||||||
if (tid < 0) {
|
|
||||||
return local_get_data(backtrace);
|
|
||||||
} else {
|
|
||||||
return remote_get_data(backtrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free any memory related to the frame data. */
|
|
||||||
void backtrace_free_data(backtrace_t* backtrace) {
|
|
||||||
free_frame_data(backtrace);
|
|
||||||
|
|
||||||
if (backtrace->map_info_list) {
|
|
||||||
backtrace_destroy_map_info_list(backtrace->map_info_list);
|
|
||||||
backtrace->map_info_list = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (backtrace->tid < 0) {
|
|
||||||
local_free_data(backtrace);
|
|
||||||
} else {
|
|
||||||
remote_free_data(backtrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* backtrace_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
|
||||||
uintptr_t* offset) {
|
|
||||||
if (backtrace->tid < 0) {
|
|
||||||
return local_get_proc_name(backtrace, pc, offset);
|
|
||||||
} else {
|
|
||||||
return remote_get_proc_name(backtrace, pc, offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,130 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <cutils/log.h>
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#define UNW_LOCAL_ONLY
|
|
||||||
#include <libunwind.h>
|
|
||||||
#include <libunwind-ptrace.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "demangle.h"
|
|
||||||
|
|
||||||
static bool local_get_frames(backtrace_t* backtrace) {
|
|
||||||
unw_context_t* context = (unw_context_t*)backtrace->private_data;
|
|
||||||
unw_cursor_t cursor;
|
|
||||||
|
|
||||||
int ret = unw_getcontext(context);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("local_get_frames: unw_getcontext failed %d\n", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = unw_init_local(&cursor, context);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("local_get_frames: unw_init_local failed %d\n", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace_frame_data_t* frame;
|
|
||||||
bool returnValue = true;
|
|
||||||
backtrace->num_frames = 0;
|
|
||||||
uintptr_t map_start;
|
|
||||||
unw_word_t value;
|
|
||||||
do {
|
|
||||||
frame = &backtrace->frames[backtrace->num_frames];
|
|
||||||
frame->stack_size = 0;
|
|
||||||
frame->map_name = NULL;
|
|
||||||
frame->map_offset = 0;
|
|
||||||
frame->proc_name = NULL;
|
|
||||||
frame->proc_offset = 0;
|
|
||||||
|
|
||||||
ret = unw_get_reg(&cursor, UNW_REG_IP, &value);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("get_frames: Failed to read IP %d\n", ret);
|
|
||||||
returnValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frame->pc = (uintptr_t)value;
|
|
||||||
ret = unw_get_reg(&cursor, UNW_REG_SP, &value);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("get_frames: Failed to read IP %d\n", ret);
|
|
||||||
returnValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frame->sp = (uintptr_t)value;
|
|
||||||
|
|
||||||
if (backtrace->num_frames) {
|
|
||||||
backtrace_frame_data_t* prev = &backtrace->frames[backtrace->num_frames-1];
|
|
||||||
prev->stack_size = frame->sp - prev->sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
|
|
||||||
|
|
||||||
frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
|
|
||||||
if (frame->map_name) {
|
|
||||||
frame->map_offset = frame->pc - map_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace->num_frames++;
|
|
||||||
ret = unw_step (&cursor);
|
|
||||||
} while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool local_get_data(backtrace_t* backtrace) {
|
|
||||||
unw_context_t *context = (unw_context_t*)malloc(sizeof(unw_context_t));
|
|
||||||
backtrace->private_data = context;
|
|
||||||
|
|
||||||
if (!local_get_frames(backtrace)) {
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void local_free_data(backtrace_t* backtrace) {
|
|
||||||
if (backtrace->private_data) {
|
|
||||||
free(backtrace->private_data);
|
|
||||||
backtrace->private_data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* local_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
|
||||||
uintptr_t* offset) {
|
|
||||||
unw_context_t* context = (unw_context_t*)backtrace->private_data;
|
|
||||||
char buf[512];
|
|
||||||
|
|
||||||
*offset = 0;
|
|
||||||
unw_word_t value;
|
|
||||||
if (unw_get_proc_name_by_ip(unw_local_addr_space, pc, buf, sizeof(buf),
|
|
||||||
&value, context) >= 0 && buf[0] != '\0') {
|
|
||||||
*offset = (uintptr_t)value;
|
|
||||||
char* symbol = demangle_symbol_name(buf);
|
|
||||||
if (!symbol) {
|
|
||||||
symbol = strdup(buf);
|
|
||||||
}
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2013 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "libbacktrace"
|
|
||||||
|
|
||||||
#include <sys/ptrace.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <cutils/log.h>
|
|
||||||
#include <backtrace/backtrace.h>
|
|
||||||
|
|
||||||
#include <libunwind.h>
|
|
||||||
#include <libunwind-ptrace.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "demangle.h"
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unw_addr_space_t addr_space;
|
|
||||||
struct UPT_info* upt_info;
|
|
||||||
} backtrace_private_t;
|
|
||||||
|
|
||||||
static bool remote_get_frames(backtrace_t* backtrace) {
|
|
||||||
backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
|
|
||||||
unw_cursor_t cursor;
|
|
||||||
int ret = unw_init_remote(&cursor, data->addr_space, data->upt_info);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("remote_get_frames: unw_init_remote failed %d\n", ret);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace_frame_data_t* frame;
|
|
||||||
bool returnValue = true;
|
|
||||||
backtrace->num_frames = 0;
|
|
||||||
uintptr_t map_start;
|
|
||||||
unw_word_t value;
|
|
||||||
do {
|
|
||||||
frame = &backtrace->frames[backtrace->num_frames];
|
|
||||||
frame->stack_size = 0;
|
|
||||||
frame->map_name = NULL;
|
|
||||||
frame->map_offset = 0;
|
|
||||||
frame->proc_name = NULL;
|
|
||||||
frame->proc_offset = 0;
|
|
||||||
|
|
||||||
ret = unw_get_reg(&cursor, UNW_REG_IP, &value);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("remote_get_frames: Failed to read IP %d\n", ret);
|
|
||||||
returnValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frame->pc = (uintptr_t)value;
|
|
||||||
ret = unw_get_reg(&cursor, UNW_REG_SP, &value);
|
|
||||||
if (ret < 0) {
|
|
||||||
ALOGW("remote_get_frames: Failed to read SP %d\n", ret);
|
|
||||||
returnValue = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frame->sp = (uintptr_t)value;
|
|
||||||
|
|
||||||
if (backtrace->num_frames) {
|
|
||||||
backtrace_frame_data_t* prev = &backtrace->frames[backtrace->num_frames-1];
|
|
||||||
prev->stack_size = frame->sp - prev->sp;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame->proc_name = backtrace_get_proc_name(backtrace, frame->pc, &frame->proc_offset);
|
|
||||||
|
|
||||||
frame->map_name = backtrace_get_map_info(backtrace, frame->pc, &map_start);
|
|
||||||
if (frame->map_name) {
|
|
||||||
frame->map_offset = frame->pc - map_start;
|
|
||||||
}
|
|
||||||
|
|
||||||
backtrace->num_frames++;
|
|
||||||
ret = unw_step (&cursor);
|
|
||||||
} while (ret > 0 && backtrace->num_frames < MAX_BACKTRACE_FRAMES);
|
|
||||||
|
|
||||||
return returnValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remote_get_data(backtrace_t* backtrace) {
|
|
||||||
backtrace_private_t* data = (backtrace_private_t*)malloc(sizeof(backtrace_private_t));
|
|
||||||
if (!data) {
|
|
||||||
ALOGW("remote_get_data: Failed to allocate memory.\n");
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
data->addr_space = NULL;
|
|
||||||
data->upt_info = NULL;
|
|
||||||
|
|
||||||
backtrace->private_data = data;
|
|
||||||
data->addr_space = unw_create_addr_space(&_UPT_accessors, 0);
|
|
||||||
if (!data->addr_space) {
|
|
||||||
ALOGW("remote_get_data: Failed to create unw address space.\n");
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
data->upt_info = _UPT_create(backtrace->tid);
|
|
||||||
if (!data->upt_info) {
|
|
||||||
ALOGW("remote_get_data: Failed to create upt info.\n");
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!remote_get_frames(backtrace)) {
|
|
||||||
backtrace_free_data(backtrace);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void remote_free_data(backtrace_t* backtrace) {
|
|
||||||
if (backtrace->private_data) {
|
|
||||||
backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
|
|
||||||
if (data->upt_info) {
|
|
||||||
_UPT_destroy(data->upt_info);
|
|
||||||
data->upt_info = NULL;
|
|
||||||
}
|
|
||||||
if (data->addr_space) {
|
|
||||||
unw_destroy_addr_space(data->addr_space);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(backtrace->private_data);
|
|
||||||
backtrace->private_data = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char* remote_get_proc_name(const backtrace_t* backtrace, uintptr_t pc,
|
|
||||||
uintptr_t* offset) {
|
|
||||||
backtrace_private_t* data = (backtrace_private_t*)backtrace->private_data;
|
|
||||||
char buf[512];
|
|
||||||
|
|
||||||
*offset = 0;
|
|
||||||
unw_word_t value;
|
|
||||||
if (unw_get_proc_name_by_ip(data->addr_space, pc, buf, sizeof(buf), &value,
|
|
||||||
data->upt_info) >= 0 && buf[0] != '\0') {
|
|
||||||
*offset = (uintptr_t)value;
|
|
||||||
char* symbol = demangle_symbol_name(buf);
|
|
||||||
if (!symbol) {
|
|
||||||
symbol = strdup(buf);
|
|
||||||
}
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Reference in a new issue