ALWAYS_INLINE staticinlinevoid WritePrefix(std::ostream& os, constchar* prefix, bool odd) { if (prefix != nullptr) {
os << prefix;
}
os << " "; if (!odd) {
os << " ";
}
}
// The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin // and prints the result to stdout. This struct keeps the state of the open connection. struct Addr2linePipe {
Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid)
: in(in_fd, false), out(out_fd, false), file(file_name), child_pid(pid), odd(true) {}
~Addr2linePipe() {
kill(child_pid, SIGKILL);
}
File in; // The file descriptor that is connected to the output of addr2line.
File out; // The file descriptor that is connected to the input of addr2line.
const std::string file; // The file addr2line is working on, so that we know when to close // and restart. const pid_t child_pid; // The pid of the child, which we should kill when we're done. bool odd; // Print state for indentation of lines.
};
static std::unique_ptr<Addr2linePipe> Connect(const std::string& name, constchar* args[]) { int caller_to_addr2line[2]; int addr2line_to_caller[2];
if (pipe(caller_to_addr2line) == -1) { return nullptr;
} if (pipe(addr2line_to_caller) == -1) {
close(caller_to_addr2line[0]);
close(caller_to_addr2line[1]); return nullptr;
}
constexpr size_t kMaxBuffer = 128; // Relatively small buffer. Should be OK as we're on an // alt stack, but just to be sure... char buffer[kMaxBuffer];
memset(buffer, 0, kMaxBuffer); int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1)); if (bytes_read <= 0) { // This should not really happen...
pipe->reset(); return;
}
buffer[bytes_read] = '\0';
char* tmp = buffer; while (*tmp != 0) { if (!prefix_written) {
WritePrefix(os, prefix, (*pipe)->odd);
prefix_written = true;
} char* new_line = strchr(tmp, '\n'); if (new_line == nullptr) {
os << tmp;
staticvoid Addr2line(const std::string& map_src,
uintptr_t offset,
std::ostream& os, constchar* prefix,
std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
std::array<constchar*, 3> kIgnoreSuffixes{ ".dex", ".jar", ".vdex" }; for (constchar* ignore_suffix : kIgnoreSuffixes) { if (map_src.ends_with(ignore_suffix)) { // Ignore file names that do not have map information addr2line can consume. e.g. vdex // files are special frames injected for the interpreter so they don't have any line // number information available. return;
}
} if (map_src == "[vdso]") { // addr2line will not work on the vdso. return;
}
if (*pipe == nullptr || (*pipe)->file != map_src) { if (*pipe != nullptr) {
Drain(0, prefix, pipe, os);
}
pipe->reset(); // Close early.
// Send the offset. const std::string hex_offset = StringPrintf("0x%zx\n", offset);
if (!pipe_ptr->out.WriteFully(hex_offset.data(), hex_offset.length())) { // Error. :-(
pipe->reset(); return;
}
// Now drain (expecting two lines).
Drain(2U, prefix, pipe, os);
}
staticbool RunCommand(const std::string& cmd) {
FILE* stream = popen(cmd.c_str(), "r"); if (stream) { // Consume the stdout until we encounter EOF when the tool exits. // Otherwise the tool would complain to stderr when the stream is closed. char buffer[64]; while (fread(buffer, 1, sizeof(buffer), stream) == sizeof(buffer)) {}
pclose(stream); returntrue;
} else { returnfalse;
}
}
// Remove method parameters by finding matching top-level parenthesis and removing them. // Since functions can be defined inside functions, this can remove multiple substrings.
std::string StripParameters(std::string name) {
size_t end = name.size(); int nesting = 0; for (ssize_t i = name.size() - 1; i > 0; i--) { if (name[i] == ')' && nesting++ == 0) {
end = i + 1;
} if (name[i] == '(' && --nesting == 0) {
name = name.erase(i, end - i);
}
} return name;
}
void DumpNativeStack(std::ostream& os,
unwindstack::AndroidLocalUnwinder& unwinder,
pid_t tid, constchar* prefix, void* ucontext_ptr, bool skip_frames) { // Historical note: This was disabled when running under Valgrind (b/18119146).
unwindstack::AndroidUnwinderData data(!skip_frames /*show_all_frames*/); bool unwind_ret; if (ucontext_ptr != nullptr) {
unwind_ret = unwinder.Unwind(ucontext_ptr, data);
} else {
unwind_ret = unwinder.Unwind(tid, data);
} if (!unwind_ret) {
os << prefix << "(Unwind failed for thread " << tid << ": "
<< data.GetErrorString() << ")" << std::endl; return;
}
// Check whether we have and should use addr2line. bool use_addr2line; if (kUseAddr2line) { // Try to run it to see whether we have it. Push an argument so that it doesn't assume a.out // and print to stderr.
use_addr2line = (gAborting > 0) && RunCommand(FindAddr2line() + " -h");
} else {
use_addr2line = false;
}
std::unique_ptr<Addr2linePipe> addr2line_state;
data.DemangleFunctionNames(); for (const unwindstack::FrameData& frame : data.frames) { // We produce output like this: // ] #00 pc 000075bb8 /system/lib/libc.so (unwind_backtrace_thread+536) // In order for parsing tools to continue to function, the stack dump // format must at least adhere to this format: // #XX pc <RELATIVE_ADDR> <FULL_PATH_TO_SHARED_LIBRARY> ... // The parsers require a single space before and after pc, and two spaces // after the <RELATIVE_ADDR>. There can be any prefix data before the // #XX. <RELATIVE_ADDR> has to be a hex number but with no 0x prefix.
os << prefix << StringPrintf("#%02zu pc ", frame.num); bool try_addr2line = false; if (frame.map_info == nullptr) {
os << StringPrintf("%08" PRIx64 " ???", frame.pc);
} else {
os << StringPrintf("%08" PRIx64 " ", frame.rel_pc); const std::shared_ptr<unwindstack::MapInfo>& map_info = frame.map_info; if (map_info->name().empty()) {
os << StringPrintf("<anonymous:%" PRIx64 ">", map_info->start());
} else {
os << map_info->name().c_str();
} if (map_info->elf_start_offset() != 0) {
os << StringPrintf(" (offset %" PRIx64 ")", map_info->elf_start_offset());
}
os << " ("; if (!frame.function_name.empty()) { // Remove parameters from the printed function name to improve signal/noise in the logs. // Also, ANRs are often trimmed, so printing less means we get more useful data out. // We can still symbolize the function based on the PC and build-id (including inlining).
os << StripParameters(frame.function_name.c_str()); if (frame.function_offset != 0) {
os << "+" << frame.function_offset;
} // Functions found using the gdb jit interface will be in an empty // map that cannot be found using addr2line. if (!map_info->name().empty()) {
try_addr2line = true;
}
} else {
os << "???";
}
os << ")";
std::string build_id = map_info->GetPrintableBuildID(); if (!build_id.empty()) {
os << " (BuildId: " << build_id << ")";
}
}
os << std::endl; if (try_addr2line && use_addr2line) { // Guaranteed that map_info is not nullptr and name is non-empty.
Addr2line(frame.map_info->name(), frame.rel_pc, os, prefix, &addr2line_state);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.