Native code - how to get function call stack (backtrace) programatically 附带源代码

自己根据 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);
    }
}

 

posted @ 2015-10-30 09:49  微信公众号--共鸣圈  阅读(1014)  评论(0编辑  收藏  举报