使用Eclipse调试Android Native Application

由于最近需要调试C++程序,以后都是暴力调试的,暴力调试就是在源码中把想要得到的内容通过log的形式输出,这就调试速度快精准度高,但是得到的内容有限,所以才开始使用GDB来高度C++。

现在如果想要通过Google搜索关于Debug Android Ndk的内容很多都是旧版本的,如今ADT插件的发展已经支持NDK的调试的,不需要安装Sequoyah插件与一大堆配置,只需要简单几步即可调试应用,下面记录下调试的步骤,方便以后查阅。

  1. 系统: OS X 10.8.3
  2. Eclipse: Juno 4.2.1
  3. ADT: r21.1.0
  4. NDK: android-ndk-r8e
一、创建工程

二、添加Native支持

三、切换至C/C++视图,方便C/C++代码编写

四、检查工程目录结构是否正常,如果没有includes文件夹可以关闭工程再重新打开

五、这样一个完整的工程就创建完成了,接下来我们要实现的功能是点击一下按键,显示从Jni返回的字符串。
六、修改activity_main.xml布局文件

    

    
七、修改MainActivity.java
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void call_jni(View v) {
        ((TextView) findViewById(R.id.textView1)).setText(jni_call());
    }

    private native String jni_call();

    static {
        System.loadLibrary("DebugNdkTest");
    }
}
八、获取jni_call的jni头文件
$ cd $DebugNdkTest/jni
$ javah -classpath ../bin/classes  com.linguofeng.debugndktest.MainActivity
九、修改DebugNdkTest.cpp
#include "com_linguofeng_debugndktest_MainActivity.h"

JNIEXPORT jstring JNICALL Java_com_linguofeng_debugndktest_MainActivity_jni_1call
  (JNIEnv * env, jobject) {

    return env->NewStringUTF("Hello From JNI");
}
十、运行点击按键将会看到

接下来就是debug了,debug前需要修改工程属性

在DebugNdkTest.cpp文件第6行打个断点

现在就可以debug了

当点击访问JNI按钮的时候就会停在断点处了,现在就可以像java的debug一样调试了,很方便。

brew安装gdb后的代码签名

OS X自带的gdb版本有点低了,就使用brew安装了一个最新版本,可是遇到了

please check gdb is codesigned - see taskgated(8)

首先创建一个证书,用于给gdb签名用的,没图片的选项保持默认即可

用刚刚创建的证书给gdb签名

$ codesign -s gdb-cert /usr/local/bin/gdb

输入密码即可

参考: http://sourceware.org/gdb/wiki/BuildingOnDarwin

SpiderMonkey是mozilla开发的js引擎

1. 安装

$ brew install SpiderMonkey

2. Hello World

#include "jsapi.h"

static JSClass global_class = {
    "global", JSCLASS_GLOBAL_FLAGS,
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};

void reportError(JSContext *cx, const char *message, JSErrorReport *report)
{
    fprintf(stderr, "%s:%u:%s\n",
            report->filename ? report->filename : "<no filename=\"filename\">",
            (unsigned int) report->lineno,
            message);
}

int main(int argc, const char *argv[])
{
    JSRuntime *rt;
    JSContext *cx;
    JSObject  *global;

    // 创建新的运行时
    rt = JS_NewRuntime(8 * 1024 * 1024);
    if (rt == NULL)
        return 1;

    // 创建新的上下文并与运行时绑定
    cx = JS_NewContext(rt, 8192);
    if (cx == NULL)
        return 1;

    JS_SetOptions(cx, JSOPTION_VAROBJFIX | JSOPTION_JIT | JSOPTION_METHODJIT);
    JS_SetVersion(cx, JSVERSION_LATEST);
    JS_SetErrorReporter(cx, reportError);

    global = JS_NewCompartmentAndGlobalObject(cx, &global_class, NULL);
    if (global == NULL)
        return 1;

    // 实例化内置全局对象
    if (!JS_InitStandardClasses(cx, global))
        return 1;

    const char *script = "'Hello ' + 'World!'";
    jsval rval;
    JSString *str;
    JSBool ok;
    const char *filename = "noname";
    uintN lineno = 0;

    ok = JS_EvaluateScript(cx, global, script, strlen(script),
            filename, lineno, &rval);
    if (!rval | rval == JS_FALSE)
        return 1;

    str = JS_ValueToString(cx, rval);
    printf("%s\n", JS_EncodeString(cx, str));


    JS_DestroyContext(cx);
    JS_DestroyRuntime(rt);
    JS_ShutDown();
    return 0;
}

3. 编译与执行

$ g++ -o bin/HelloJS -I/usr/local/include/js -lmozjs185 src/HelloJS.cpp
$ ./bin/HelloJS

4. 看来以后有时间还是要学习javascript

使用ndk-gdb调试hello-jni

$ cd $NDK_ROOT/sample/hello-jni
$ ndk-build NDK_DEBUG=1
$ android update project -p . -t android-17
$ ant debug install
$ ndk-gdb --start

这时会启动应用,接着就可以使用gdb的命令了

(gdb) list
(gdb) break 30
(gdb) info breakpoints
(gdb) continue
Continuing.     // 然后手动重启应用就会停在30行断点处了

Cocos2d-x ndkgdb 调试记录

由于目前Cocos2d-x中的libtiff.a静态库中存在main主函数,导致gdb调试时会出现问题,现在需要重新编译一个没有main函数的libtiff静态库

$ git clone git://github.com/dumganhar/libtiff.git
$ cp -r libtiff $NDK_ROOT/samples/hello-jni/jni/tiff
$ cd libtiff
$ ./configure 
    # 目的是生成tif_config.h和tiffconf.h两个文件
$ cp libtiff/{tif_config.h,tiffconf.h} $NDK_ROOT/samples/hello-jni/jni/tiff/libtiff
$ vim $NDK_ROOT/samples/hello-jni/jni/tiff/libtiff/tif_config.h
    # 注释120行的#define HAVE_SEARCH_H 1与206行的#define LZMA_SUPPORT 1
$ vim $NDK_ROOT/samples/hello-jni/jni/tiff/libtiff/mkg3states.c
    # 注释掉main函数
$ vim $NDK_ROOT/samples/hello-jni/jni/Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE         := tiff

LOCAL_TIFF_SRC_FILES := \
        tiff/libtiff/tif_dirread.c \
        tiff/libtiff/tif_zip.c \
        tiff/libtiff/tif_flush.c \
        tiff/libtiff/tif_next.c \
        tiff/libtiff/tif_ojpeg.c \
        tiff/libtiff/tif_dirwrite.c \
        tiff/libtiff/tif_dirinfo.c \
        tiff/libtiff/tif_dir.c \
        tiff/libtiff/tif_compress.c \
        tiff/libtiff/tif_close.c \
        tiff/libtiff/tif_tile.c \
        tiff/libtiff/tif_open.c \
        tiff/libtiff/tif_getimage.c \
        tiff/libtiff/tif_pixarlog.c \
        tiff/libtiff/tif_warning.c \
        tiff/libtiff/tif_dumpmode.c \
        tiff/libtiff/tif_jpeg.c \
        tiff/libtiff/tif_jbig.c \
        tiff/libtiff/tif_predict.c \
        tiff/libtiff/mkg3states.c \
        tiff/libtiff/tif_write.c \
        tiff/libtiff/tif_error.c \
        tiff/libtiff/tif_version.c \
        tiff/libtiff/tif_print.c \
        tiff/libtiff/tif_color.c \
        tiff/libtiff/tif_read.c \
        tiff/libtiff/tif_extension.c \
        tiff/libtiff/tif_thunder.c \
        tiff/libtiff/tif_lzw.c \
        tiff/libtiff/tif_fax3.c \
        tiff/libtiff/tif_luv.c \
        tiff/libtiff/tif_codec.c \
        tiff/libtiff/tif_unix.c \
        tiff/libtiff/tif_packbits.c \
        tiff/libtiff/tif_aux.c \
        tiff/libtiff/tif_fax3sm.c \
        tiff/libtiff/tif_swab.c \
        tiff/libtiff/tif_strip.c

LOCAL_TIFF_SRC_FILES += tiff/port/lfind.c

LOCAL_SRC_FILES              := $(LOCAL_TIFF_SRC_FILES)
LOCAL_C_INCLUDES             := $(LOCAL_PATH)/tiff/libtiff
LOCAL_WHOLE_STATIC_LIBRARIES := cocos_jpeg_static

include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE           := hello-jni
LOCAL_SRC_FILES        := hello-jni.c
LOCAL_STATIC_LIBRARIES := tiff

include $(BUILD_SHARED_LIBRARY)

$(call import-module,libjpeg)

参考: https://code.google.com/p/tiffonandroid/source/browse/tiffviewer/project/jni/Android.mk

$ ndk-build NDK_MODULE_PATH=$COCOS2DX_ROOT/cocos2dx/platform/third_party/android/prebuilt
$ cp obj/local/armeabi/libtiff.a $COCOS2DX_ROOT/cocos2dx/platform/third_party/android/prebuilt/libtiff/libs/armeabi

首先编译时要加NDK_DEBUG=1

$ cd $COCOS2DX_ROOT/samples/Cpp/HelloCpp/proj.android
$ ./build_native.sh NDK_DEBUG=1

AndroidManifest.xml增加android:debuggable="true"


    ...

安装应用到模拟器

$ android update project -p . -t android-17
$ ant debug install

开始进行调试

$ ./ndkgdb.sh
(gdb) b HelloWorldScene.cpp:83
(gdb) c     # 这时点击退出按键将会断点到83行
Breakpoint 1, HelloWorld::menuCloseCallback (this=0x2a19b3a0, pSender=0x2a14b8d0) at jni/../../Classes/HelloWorldScene.cpp:83
83      CCDirector::sharedDirector()->end();
(gdb) q