NDK开发历程(一):android native code的调试方法
引用:http://www.cnblogs.com/ychellboy/archive/2013/02/22/2922683.html
使用NDK在android上做开发是一件“痛并快乐着”的差事,之所以“快乐”是因为可以将一些原有的C/C++库直接移植到android上,而不需要用java再开发一套功能相同的库。然而这同时也是一件“痛苦”的事件,因为android本身是裁减过的linux,好些system call不能使用,另外由于没有采用glibc(用的是Bionic libc,原因见wiki),好些函数所在的头文件位置也有变化,这都给移植工作带来困难。更为坑爹的是一些函数在头文件里能找到定义在具体库里确没有实现(比如:pthread_mutex_timedlock)。
android native开发在编译链接阶段会遇到上述“惨痛”经历,但更为痛苦的是好不容易变成可执行文件,一运行就crash没有任何信息。遇到这种情况,在排除了代码有低级错误的情况后,最终只能想办法做debug。(本文余下篇幅在不特殊注明的情况下都是指使用NDK在android上做native code的开发)。
在android上NDK开发的程序进行查错主要有两种方法:
(1)使用log进行查错:在程序源代码上加log,根据log信息来排查错误。这种方式应该是最为常用的,因为其普适性很高。不过作为在VxWorks上移植过网络库的苦逼,深知用log排错的效率是多么的低,特别是在排查底层库时。而遇到多线程的程序,log排错是多么的无力。
(2)使用ndk-gdb调试程序:用过gdb的都知道它多么的强大,但是想要使用ndk-gdb需要做很多的配置,还会碰到很多坑,因此想真正使用起来也不是件容易的事(毕竟是开源项目,和VxWorks这种高富帅是没法比的)。
本文主要介绍如何配置使用ndk-gdb进行debug,所使用android-ndk-r8d/samples/hello-jni作为入口调用一个static library。 —— Here we go!
一、开发环境
1. ubuntu 12.04 x86_64
2. eclipse 3.7(只是为了方便启动android模拟器)
3. android NDK r8d
4. android SDK 2.2 ~ 4.2
5. ant (打包程序使用)
在windows环境下可以配置cygwin来实现ndk-gdb,本人在windows上使用相同方法也达到了效果,对cygwin的配置这里不再讨论,有疑问可以找google老师。
二、准备阶段
1. 下载linux平台的NDK,并解压到相应目录。这里需要注意的是:虽然google网站上写着NDK for Linux 32/64-bit(x86),但是ndk中的一些工具(比如NDK自带的awk,make,sed)在64bit的ubuntu上并不能直接运行,因为这些工具是32bit的程序,需要32bit的运行时库。解决方法是:sudo apt-get install libc6-i386, sudo apt-get install lib32asound2 lib32z1 lib32stdc++6 lib32bz2-1.0 安装这些常用的32位库。如果是CentOS则需要:yum install libgcc.i686 yum install glibc-static.i686 yum install glibc-devel.i686
2. 下载SDK,在下载页面的”DOWNLOAD FOR OTHER PLATFORMS“ –>“ADT Bundle”找到对应的版本下载,并解压到相应的目录。这时SDK下的platforms会有最新版本的android,下载历史版本的android就要使用tools下的工具:./android list sdk 根据列举出来的编号执行如下: ./android update sdk –t 1 –u 则更新编号是1的包。使用android update把所需要的历史版本都下载下来。
3. 根据实际情况在~/.profile(或~/.bash_profile)中设置如下环境变量,设置完毕后执行source ~/.profile使之生效:
# ---- NDK ----
NDK_ROOT=~/mysoftware/NDK/android-ndk-r8d
PATH=$PATH:$NDK_ROOT
export NDK_ROOT# ---- android-SDK ----
ANDROID_SDK_ROOT=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk
PATH=$PATH:$ANDROID_SDK_ROOT
export ANDROID_SDK_ROOT# ---- adb ----
ADB_PATH=~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/platform-tools
PATH=$PATH:$ADB_PATH# ---- tools/android ----
PATH=$PATH:~/mysoftware/SDK/adt-bundle-linux-x86_64/sdk/toolsexport PATH
三、修改hello-jni
由于项目使用c++编程,做这个实验的时候就将jni/hello-jni.c 改为hello-jni.cpp,代码如下:
1: #include <string.h>
2: #include <jni.h>
3: #include <unistd.h>
4: #include "shared/thread.h"
5:
6: /* This is a trivial JNI example where we use a native method
7: * to return a new VM String. See the corresponding Java source
8: * file located at:
9: *
10: * apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
11: */
12:
13: using namespace shared;
14:
15: extern "C" {
16:
17: void* StartThread(void* obj)
18: {
19: return NULL;
20: }
21:
22: jstring
23: Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
24: jobject thiz )
25: {
26: volatile int bGo = 0;
27: while(!bGo) {
28: sleep(1);
29: }
30:
31: Thread mythread(&StartThread, NULL);
32: mythread.Start();
33:
34: return env->NewStringUTF("Hello from JNI !");
35: //return (*env)->NewStringUTF(env, "Hello from JNI !");
36: }
37:
38: }
注意需要用extern “C”{ } 把Java_com_example_hellojni_HelloJni_stringFromJNI函数包起来,while (!bGo)是为了方便调试,因为ndk-gdb会先把程序run起来后再attach上去,这里需要一个while让程序等一会。上述代码中的Thread类是在libshared.a的静态库中,因此需要修改hello-jni目录下的jni/Android.mk文件。如下:
1: LOCAL_PATH := $(call my-dir)
2:
3: include $(CLEAR_VARS)
4: LOCAL_MODULE := shared
5: LOCAL_SRC_FILES := ../shared/obj/local/armeabi/libshared.a
6: LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/shared
7: include $(PREBUILT_STATIC_LIBRARY)
8:
9: include $(CLEAR_VARS)
10: LOCAL_MODULE := hello-jni
11: LOCAL_SRC_FILES := hello-jni.cpp
12: LOCAL_STATIC_LIBRARIES := shared
13: LOCAL_C_INCLUDES := $(LOCAL_PATH)/../
14:
15: include $(BUILD_SHARED_LIBRARY)
红色部分为添加或修改项,编译前需要在环境变量C_INCLUDE_PATH中加入jni.h的路径,比如:
1: C_INCLUDE_PATH=$C_INCLUDE_PATH:~/mysoftware/NDK/android-ndk-r8d/platforms/android-8/arch-arm/usr/include
2:
3: export C_INCLUDE_PATH
PS:libshared.a在build时需要加NDK_DEBUG=1的参数,即:ndk-build NDK_DEBUG=1,这么编译才能带上debug信息。
四、万事俱备
1. shell进入ndk/samples/目录,运行android update project --path hello-jni,生成build.xml用于apk打包。(也可以在hello-jni目录里运行:android update project -t 1 -p . --subprojects)
2. 进入ndk/samples/hello-jni,修改AndroidManifest.xml文件
1: <application android:label="@string/app_name" 2: android:debuggable="true">
3. 运行ndk-build
4. 运行ant debug
5. 启动android的模拟器(可以从eclipse启动)
6. 运行adb install –r bin/HelloJni-debug.apk
7. 运行ndk-gdb –start 开始debug,后续和使用gdb一样
8. 需要图形化界面进行debug,可以参考[2]
几点重要说明:
1. ndk-gdb用的是client/server形式对目标机器进行debug, gdb 调试器 与 gdbserver 的关系,就是 gdb 与 stub的关系,如下图所示[3] :

2. ndk-gdb最坑爹的是:gdb和gdbserver的版本必须是匹配的才能debug:
每一个模拟器在system/bin下都有gdbserver,这些gdbserver是和模拟器本身的android版本有关的,而下载的NDK的ndk-gdb一般都是最新的gdb,因此gdb和gdbserver的版本常常匹配不了。这时需要把对应版本的gdbserver push到emulator上,然后指定./gdbserver,必须指定“./”因为在linux下默认优先查找system目录。
References:
[1] 使用eclipse/ndk-gdb对java/native code联合调试
[2] Eclipse+CDT+GDB调试android NDK程序
[3] ndk-gdb对java/native code联合调试
[4] 使用eclipse/ndk-gdb对java/native code联合调试
NDK开发历程(一):android native code的调试方法的更多相关文章
- GDB + gdbserver 远程调试android native code
原文地址:GDB + gdbserver 远程调试android native code 作者:tq08g2z 以调试模拟器中的native library code为例. Host: ubuntuT ...
- ndk-gdb 对java/native code联合调试(升级版)
之前写过一篇 关于android native 开发,调试的文章(http://www.cnblogs.com/yaozhongxiao/archive/2012/03/13/2393959.html ...
- NDK环境搭建(Native Code的编译,不需要Cygwin)
分类: android2013-06-21 15:49 475人阅读 评论(0) 收藏 举报 Android NDK 目录(?)[-] System and Software Requirements ...
- [教程] Android Native内存泄漏检测方法
转载请注明出处:https://www.cnblogs.com/zzcperf/p/9563389.html Android 检测 C/C++内存泄漏的方法越来越简便了,下面列举一下不同场景下检测C/ ...
- android应用崩溃的调试方法(c++ lib so文件库崩溃)
android调试工具addr2line使用: 1.将ndk中的arm-linux-androideabi-addr2line可执行文件的路径加入配置文件~/.bashrc中,例如: export P ...
- 使用Unity开发Android的几种调试方法
前言 本文举例几种Android 调试的方法(PS:我是通过unity引擎来开发安卓游戏) Eclipse + adt 查看LOG 1.为Eclipse 装上adt 插件 2.打开Eclipse 的L ...
- android默认开启adb调试方法分析
用adb调试android时,每次接入usb线,都会提示一个确认打开usb调试功能的窗口,有时候,我们需要默认打开usb调试功能.或者无需弹出对话框,直接默认开启.这个我们需要分析adb的流程了. a ...
- android机顶盒真机调试方法
最近接触电视APP开发,之前对Android开发也不太了解还一直以为不能真机调试.最近静下心来想一想肯定能真机调试的,我是我不知道而已.现在讲述一下真机调试的步骤: 1.进入设置--关于,连续点击版本 ...
- Android平台 Psensor传感器调试方法
一. 驱动层调试1.在板级dts文件中增加相应的dts代码,如:调试sensor类,在代码中找到已经有的代码在修改,I2C使用那根(一般为I2C3),I2C地址(找datasheet或者FAE),哪个 ...
随机推荐
- JSHint Options 翻译
Enforcing options When set to true, these options will make JSHint produce more warnings about your ...
- js 类型转换学习
类型转换分为显示转换和隐式转换 参考http://www.cnblogs.com/mizzle/archive/2011/08/12/2135885.html 先事件显示的 通过手动进行类型转换,Ja ...
- 深入分析Java ClassLoader原理
一.什么是ClassLoader? 大家都知道,当我们写好一个Java程序之后,不是管是CS还是BS应用,都是由若干个.class文件组织而成的一个完整的Java应用程序,当程序在运行时,即会调用该程 ...
- delphi URL 编码的转换
先介绍一下,Delphi中处理Google的URL编码解码,其中就会明白URL编码转换的方法的 从delphi的角度看Google(谷歌)URL编码解码方式 在网上搜索了一下,似乎没有什么关于goog ...
- rabbitmq使用心得
因为公司项目需要使用消息中间件,实现相关业务的异步处理,所有选用了rabbitmq.通过看文档,爬过一个一个坑,终于还是实现了相关功能. 直接上配置文件: <?xml version=" ...
- swift-UserDefaults控制账号和密码
import UIKit class FiveVC: UIViewController { //MARK:-------- 全局 常量 设置 let IsFirstLaunch = "IsF ...
- nodejs如何储存一个GBK编码的文件
思路:utf-8 -> decode(to buffer) -> convert to gbk(buffer also) -> write buffer to file. var f ...
- Objective的字符串拼接 似乎没有Swift方便,但也可以制做一些较为方便的写法
NSString *str1 = @"字符串1"; NSString *str2 = @"字符串2"; //在同样条件下,Objective的字符串拼接 往往只 ...
- rabbitmq method之basic.consume
basic.consume指的是channel在 某个队列上注册消费者,那在这个队列有消息来了之后,就会把消息转发到给此channel处理,如果 这个队列有多个消费者,则会采用轮转的方式将消息分发给消 ...
- 开发者所需要知道的 iOS 10 SDK 新特性
转自:https://onevcat.com/2016/06/ios-10-sdk/ 写的很好啊.哈哈哈 总览 距离 iPhone 横空出世已经过去了 9 个年头,iOS 的版本号也跨入了两位数.在我 ...