自己根据 https://github.com/zhuowei/libcorkscrew-ndk 上的库做了一个包装库并附带使用的例子(executable 分支),具体代码在自己的代码仓库里,名字叫 libbacktrace-ndk.tar。已经完美解决。但是它无法打印没有符号导出的函数(打印出来是 ???),但已经很全了。 如果要吹毛求疵打印call stack所有的函数,这时可以让程序崩溃(用static 变量控制崩溃的index,即调用几次后发生崩溃),根据打印出来的信息,再进行 ndk-stack 的crash log分析,得到某次(index)调用的完整调用栈。 还有另外一种更简便的方法,先用 ndk-stack 一直监听adb 的logcat输出,然后让程序崩溃一次(且仅一次),这时ndk-stack就已经进入crash打印模式(遇到 pc 001f4c18 /data/app-lib/com.csipsimple-2/libpjsipjni.so (unwind_backtrace+112) 即进行分析并打印callstack),以后任何时候调用libcorkscrew-ndk的dump_backtrace()的输出将被ndk-stack解析打印。
http://stackoverflow.com/questions/7710151/native-code-how-to-get-function-call-stack-backtrace-programatically
https://bitbucket.org/xg/android-game-base/src/c0d969d44a55/jni/NativeActivityJNI.cpp?fileviewer=file-view-default#cl-40
https://bitbucket.org/xg/android-game-base/src/c0d969d44a55/src/com/gmail/whittock/tom/Util/NativeActivity.java?fileviewer=file-view-default#cl-91
android-game-base/jni/NativeActivityJNI.cpp
#include <jni.h> #include <stdlib.h> #include <signal.h> #include "NativeActivity.hpp" #include <android/log.h> #define DO_TRY #define DO_CATCH(loc) extern "C" { static struct sigaction old_sa[NSIG]; static JNIEnv *g_sigEnv; static jobject g_sigObj; static jmethodID g_sigNativeCrashed; void android_sigaction(int signal, siginfo_t *info, void *reserved) { g_sigEnv->CallVoidMethod(g_sigObj, g_sigNativeCrashed); old_sa[signal].sa_handler(signal); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env = NULL; if (jvm->GetEnv((void **)&env, JNI_VERSION_1_2)) return JNI_ERR; jclass cls = env->FindClass("com/gmail/whittock/tom/Util/NativeActivity"); g_sigEnv = env; g_sigNativeCrashed = env->GetMethodID(cls, "onNativeCrashed", "()V"); return JNI_VERSION_1_2; } JNIEXPORT jint JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnCreate(JNIEnv *env, jobject this_, jbyteArray savedState) { DO_TRY { // Try to catch crashes... g_sigObj = env->NewGlobalRef(this_); struct sigaction handler; memset(&handler, 0, sizeof(struct sigaction)); handler.sa_sigaction = android_sigaction; handler.sa_flags = SA_RESETHAND; #define CATCHSIG(X) sigaction(X, &handler, &old_sa[X]) CATCHSIG(SIGILL); CATCHSIG(SIGABRT); CATCHSIG(SIGBUS); CATCHSIG(SIGFPE); CATCHSIG(SIGSEGV); CATCHSIG(SIGSTKFLT); CATCHSIG(SIGPIPE); // Create the C++ activity proxy. NativeActivity *a = CreateNativeActivity(); // Call onCreate with the savedState bytes. jbyte *stateBytes = NULL; jsize stateSize = 0; if (savedState != NULL) { stateBytes = env->GetByteArrayElements(savedState, 0); stateSize = env->GetArrayLength(savedState); } a->onCreate(stateBytes, stateSize); if (stateBytes) env->ReleaseByteArrayElements(savedState, stateBytes, JNI_ABORT); return (jint)a; } DO_CATCH("onCreate"); } JNIEXPORT jbyteArray JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnSaveInstanceState(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; // Query app for instance data. void *data = NULL; int dataLen = 0; a->onSaveInstanceState(&data, &dataLen); // Write instance data out. jbyteArray outArray = NULL; if (data) { outArray = env->NewByteArray(dataLen); env->SetByteArrayRegion(outArray, 0, dataLen, (const jbyte*)data); a->onDeleteInstanceState(data); } return outArray; } DO_CATCH("onSaveInstanceState") } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnDestroy(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onDestroy(); // Uninstall the signal handlers. #define REMOVESIG(X) sigaction(X, &old_sa[X], NULL) REMOVESIG(SIGILL); REMOVESIG(SIGABRT); REMOVESIG(SIGBUS); REMOVESIG(SIGFPE); REMOVESIG(SIGSEGV); REMOVESIG(SIGSTKFLT); REMOVESIG(SIGPIPE); env->DeleteGlobalRef(g_sigObj); } DO_CATCH("onDestroy"); } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnPause(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onPause(); } DO_CATCH("onPause"); } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnResume(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onResume(); } DO_CATCH("onResume"); } JNIEXPORT jboolean JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnTouchEvent(JNIEnv *env, jobject this_, jint handle, jobject ev) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onTouchEvent(); } DO_CATCH("onTouchEvent"); } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnDrawFrame(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onDrawFrame(); } DO_CATCH("onDrawFrame"); } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnSurfaceChanged(JNIEnv *env, jobject this_, jint handle, jint w, jint h) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onSurfaceChanged(w, h); } DO_CATCH("onSurfaceChanged"); } JNIEXPORT void JNICALL Java_com_gmail_whittock_tom_Util_NativeActivity_nativeOnSurfaceCreated(JNIEnv *env, jobject this_, jint handle) { DO_TRY { NativeActivity *a = (NativeActivity*)handle; a->onSurfaceCreated(); } DO_CATCH("onSurfaceCreated"); } }
android-game-base/src/com/gmail/whittock/tom/Util/NativeActivity.java
package com.gmail.whittock.tom.Util; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.app.Activity; import android.util.Log; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.MotionEvent; public class NativeActivity extends Activity { private static final String NATIVE_STATE = "NATIVE_STATE"; private static final String META_DATA_LIB_NAME = "LIB_NAME"; private GLSurfaceView mGLView; private int mNativeHandle; @Override public void onCreate(Bundle savedInstanceState) { try { ActivityInfo ai = getPackageManager().getActivityInfo(getIntent().getComponent(), PackageManager.GET_META_DATA); String libName; if (ai.metaData != null) { libName = ai.metaData.getString(META_DATA_LIB_NAME); System.loadLibrary(libName); } else { throw new RuntimeException("Can't get meta data"); } } catch (PackageManager.NameNotFoundException e) { throw new RuntimeException("Error getting activity info", e); } mGLView = new NativeGLSurfaceView(this); setContentView(mGLView); byte[] array = null; if (savedInstanceState != null) array = savedInstanceState.getByteArray(NATIVE_STATE); mNativeHandle = nativeOnCreate(array); Log.e("GameBase", "Post create: " + mNativeHandle); super.onCreate(savedInstanceState); } @Override public void onSaveInstanceState(Bundle outState) { byte[] array = nativeOnSaveInstanceState(mNativeHandle); if (array != null) outState.putByteArray(NATIVE_STATE, array); } @Override public void onPause() { super.onPause(); mGLView.onPause(); nativeOnPause(mNativeHandle); } @Override public void onResume() { super.onResume(); mGLView.onResume(); Log.e("GameBase", "Pre resume: " + mNativeHandle); nativeOnResume(mNativeHandle); } public boolean onTouchEvent(MotionEvent event) { return nativeOnTouchEvent(mNativeHandle, event); } public void onDrawFrame(GL10 gl) { nativeOnDrawFrame(mNativeHandle); } public void onSurfaceChanged(GL10 gl, int w, int h) { nativeOnSurfaceChanged(mNativeHandle, w, h); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { nativeOnSurfaceCreated(mNativeHandle); } public void onNativeCrashed() { // http://stackoverflow.com/questions/1083154/how-can-i-catch-sigsegv-segmentation-fault-and-get-a-stack-trace-under-jni-on-a new RuntimeException("crashed here (native trace should follow after the Java trace)").printStackTrace(); startActivity(new Intent(this, CrashHandler.class)); } private native int nativeOnCreate(byte[] savedState); private native byte[] nativeOnSaveInstanceState(int handle); private native void nativeOnDestroy(int handle); private native void nativeOnPause(int handle); private native void nativeOnResume(int handle); private native boolean nativeOnTouchEvent(int handle, MotionEvent ev); private native void nativeOnDrawFrame(int handle); private native void nativeOnSurfaceChanged(int handle, int w, int h); private native void nativeOnSurfaceCreated(int handle); } class NativeGLSurfaceView extends GLSurfaceView { private NativeActivity mNativeActivity; private NativeRenderer mRenderer; public NativeGLSurfaceView(NativeActivity nativeActivity) { super(nativeActivity); mRenderer = new NativeRenderer(nativeActivity); mNativeActivity = nativeActivity; setRenderer(mRenderer); } public boolean onTouchEvent(final MotionEvent event) { return mNativeActivity.onTouchEvent(event); } } class NativeRenderer implements GLSurfaceView.Renderer { private NativeActivity mNativeActivity; public NativeRenderer(NativeActivity nativeActivity) { mNativeActivity = nativeActivity; } public void onSurfaceCreated(GL10 gl, EGLConfig config) { mNativeActivity.onSurfaceCreated(gl, config); } public void onSurfaceChanged(GL10 gl, int w, int h) { mNativeActivity.onSurfaceChanged(gl, w, h); } public void onDrawFrame(GL10 gl) { mNativeActivity.onDrawFrame(gl); } }