Вот некоторый рабочий и полный код, который реализует dump_stack (), начиная с ответа Евгения Шаповалова и выполняя поиск символов и имен C ++ прямо на устройстве. Это решение:
- работает с NDK r10e (вам не нужно полное дерево исходников Android AOSP)
- НЕ требует никаких дополнительных сторонних библиотек (без libunwind, libbacktrace, corkscrew, CallStack)
- НЕ зависит от каких-либо разделяемых библиотек, установленных на устройстве (например, corkscrew, получивший поддержку в Android 5)
- НЕ вынуждает вас сопоставлять адреса с символами на вашем компьютере разработчика; все имена символов отображаются на устройстве Android в вашем коде
Используются следующие средства, встроенные в NDK:
<unwind.h>
заголовок, который находится в наборе инструментов / директорий NDK (НЕ libunwind)
dladdr()
__cxxabiv1::__cxa_demangle()
из <cxxabi.h>
(см. Примечание STLport ниже)
До сих пор я проверял это только на устройстве Android 5.1 на основе руки и вызывал его только из основной программы (не из обработчика сигнала). Я использовал ndk-build по умолчанию, который выбирает gcc для платформы arm.
Пожалуйста, прокомментируйте, если вы можете сделать эту работу
- на других ОС Android
- из обработчика SIGSEGV при сбое (моей целью было просто напечатать трассировку стека при ошибке подтверждения)
- с использованием наборов инструментов clang вместо gcc
Обратите внимание, что R10e NDK имеет код <unwind.h>
для многих архитектур в наборах инструментов gcc и clang, поэтому поддержка выглядит широкой.
Поддержка разграничения имен символов C ++ зависит от функции __cxxabiv1::__cxa_demangle()
, которая поступает из C ++ STL, включенного в NDK. Это должно работать как есть, если вы выполняете сборку Android с GNU STL (APP_STL := gnustl_static
или gnustl_shared
в Application.mk
; см. на этой странице для получения дополнительной информации). Если вы в настоящее время вообще не используете STL, просто добавьте APP_STL := gnustl_static
или gnustl_shared
к Application.mk
. Если вы используете STLport, вы должны наслаждаться особым видом веселья (подробнее ниже).
ВАЖНО: , чтобы этот код работал, вы не должны использовать опцию компилятора -fvisibility=hidden
gcc (по крайней мере, в ваших отладочных сборках). Эта опция обычно используется, чтобы скрыть символы от посторонних глаз в релизных сборках.
Многие отмечают, что скрипт ndk-build удаляет символы из вашего NDK .so
, копируя его в каталог libs / вашего проекта. Это правда (использование nm
на двух копиях .so
дает очень разные результаты) ОДНАКО этот удивительный слой разметки не препятствует работе приведенного ниже кода. Каким-то образом даже после удаления остаются символы (если вы помните, что не нужно компилировать с -fvisibility=hidden
). Они появляются с nm -D
.
В других сообщениях на эту тему обсуждались другие параметры компилятора, такие как -funwind-tables
. Я не нашел, что мне нужно было установить такую опцию. Сработали опции ndk-build по умолчанию.
Чтобы использовать этот код, замените _my_log()
на вашу любимую функцию ведения журнала или строки.
Пользователи STLport видят специальные примечания ниже.
#include <unwind.h>
#include <dlfcn.h>
#include <cxxabi.h>
struct android_backtrace_state
{
void **current;
void **end;
};
_Unwind_Reason_Code android_unwind_callback(struct _Unwind_Context* context,
void* arg)
{
android_backtrace_state* state = (android_backtrace_state *)arg;
uintptr_t pc = _Unwind_GetIP(context);
if (pc)
{
if (state->current == state->end)
{
return _URC_END_OF_STACK;
}
else
{
*state->current++ = reinterpret_cast<void*>(pc);
}
}
return _URC_NO_REASON;
}
void dump_stack(void)
{
_my_log("android stack dump");
const int max = 100;
void* buffer[max];
android_backtrace_state state;
state.current = buffer;
state.end = buffer + max;
_Unwind_Backtrace(android_unwind_callback, &state);
int count = (int)(state.current - buffer);
for (int idx = 0; idx < count; idx++)
{
const void* addr = buffer[idx];
const char* symbol = "";
Dl_info info;
if (dladdr(addr, &info) && info.dli_sname)
{
symbol = info.dli_sname;
}
int status = 0;
char *demangled = __cxxabiv1::__cxa_demangle(symbol, 0, 0, &status);
_my_log("%03d: 0x%p %s",
idx,
addr,
(NULL != demangled && 0 == status) ?
demangled : symbol);
if (NULL != demangled)
free(demangled);
}
_my_log("android stack dump done");
}
Что если вы используете STLport STL вместо GNU STL?
Отстой, чтобы быть вами (и мной). Есть две проблемы:
Первая проблема заключается в том, что в STLport отсутствует вызов __cxxabiv1::__cxa_demangle()
из <cxxabi.h>
. Вам нужно будет загрузить два исходных файла cp-demangle.c
и cp-demangle.h
из этого хранилища и поместить их в подкаталог demangle/
вашего источника, а затем сделать это вместо #include <cxxabi.h>
:
#define IN_LIBGCC2 1 // means we want to define __cxxabiv1::__cxa_demangle
namespace __cxxabiv1
{
extern "C"
{
#include "demangle/cp-demangle.c"
}
}
Вторая проблема более неприятна. Оказывается, в NDK есть не один, не два, а ТРИ различных несовместимых типа <unwind.h>
. И вы уже догадались, что <unwind.h>
в STLport (на самом деле он находится в библиотеке gabi ++, которая подходит для поездки, когда вы выбираете STLport) несовместим. Тот факт, что STLport / gabi ++ включает в себя, предшествует тому, что включает в себя набор инструментов (смотрите параметры -I
вашего вывода ndk-build), означает, что STLport не позволяет вам использовать настоящий <unwind.h>
. Я не мог найти лучшего решения, чем зайти и взломать имена файлов в моем установленном NDK:
sources/cxx-stl/gabi++/include/unwind.h
до sources/cxx-stl/gabi++/include/unwind.h.NOT
sources/cxx-stl/gabi++/include/unwind-arm.h
до sources/cxx-stl/gabi++/include/unwind-arm.h.NOT
sources/cxx-stl/gabi++/include/unwind-itanium.h
до sources/cxx-stl/gabi++/include/unwind-itanium.h.NOT
Я уверен, что есть более элегантное решение, однако я подозреваю, что переключение порядка параметров компилятора -I
, вероятно, создаст другие проблемы, так как STL обычно хотят переопределить включаемые файлы набора инструментов.
Наслаждайтесь!