OpenHarmony在适配新的某型开发板时,遇到了launcher应用崩溃的现象,主要表现为:ps –ef查看进程列表,发现launcher进程成为僵尸进程,再次查询进程列表时,launcher进程已经消失。
在hilog日志中搜索com.ohos.launcher,发现了和launcher进程相关的cppcrash日志
初步确认,launcher应用在启动过程中崩溃,SIGSEGV表示是因为段错误退出,出错的位置是在ld-musl-x86_64.so.1中,但日志无法看出程序崩溃的具体位置。针对这种情况,可以用gdb调试launcher进程,在进程崩溃时查看调试栈定位到出错的具体位置。
调试代码修改
HAP应用在初始化过程中由系统拉起,需要延缓其启动过程,方便使用gdb工具进程跟踪调试,修改下述部分代码,以方便gdb的使用。(以下代码在OpenHarmony 3.2Beta3版本上验证,红色字体表示增加,蓝色字体表示修改)
1.修改appspwn的代码让应用启动慢一些,防止应用快速退出,导致无法输入gdb命令。
/base/startup/appspawn/standard/appspawn_process.cstatic int SetProcessName(struct AppSpawnContent_ *content, AppSpawnClient *client,char *longProcName, uint32_t longProcNameLen){prctl(PR_SET_DUMPABLE, 1);AppSpawnClientExt *appPropertyExt = (AppSpawnClientExt *)client;。。。APPSPAWN_CHECK(!isRet, return -EINVAL, "strncpy_s long name error: %d longProcNameLen %u", errno, longProcNameLen);sleep(15);return 0;}
2.因为增加了sleep,所以socket可能会建立失败,主动杀掉进程,这里需要做如下修改
base/startup/appspawn/interfaces/innerkits/client/appspawn_socket.cppint AppSpawnSocket::ReadSocketMessage(int socketFd, void *buf, int len){。。。APPSPAWN_CHECK(memset_s(buf, len, 0, len) == EOK, return -1, "Failed to memset read buf");ssize_t rLen = 0;do {rLen = read(socketFd, buf, len);} while ((rLen == -1) && (errno == EINTR || errno == EAGAIN));APPSPAWN_CHECK(rLen >= 0, return -EFAULT, "Read message from fd %d error %zd: %d",socketFd, rLen, errno);return rLen;}
3.延长元能力模块中的进程保护机制时间100倍,防止gdb还没有attach应用进程就被原能力保护功能杀掉。
foundation/ability/ability_runtime/interfaces/kits/native/appkit/app/watchdog.hconstexpr uint32_t CHECK_INTERVAL_TIME = 300000;constexpr uint32_t INI_TIMER_FIRST_SECOND = 1000000;foundation/ability/ability_runtime/services/abilitymgr/include/ability_manager_service.hstatic constexpr uint32_t COLDSTART_LOAD_TIMEOUT = 1000000; // msstatic constexpr uint32_t LOAD_TIMEOUT = 1000000; // msstatic constexpr uint32_t ACTIVE_TIMEOUT = 500000; // msstatic constexpr uint32_t INACTIVE_TIMEOUT = 50000; // msstatic constexpr uint32_t TERMINATE_TIMEOUT = 1000000; // msstatic constexpr uint32_t CONNECT_TIMEOUT = 300000; // msstatic constexpr uint32_t DISCONNECT_TIMEOUT = 50000; // msstatic constexpr uint32_t COMMAND_TIMEOUT = 500000; // msstatic constexpr uint32_t RESTART_TIMEOUT = 500000; // msstatic constexpr uint32_t RESTART_ABILITY_TIMEOUT = 50000; // msstatic constexpr uint32_t FOREGROUND_TIMEOUT = 500000; // msstatic constexpr uint32_t BACKGROUND_TIMEOUT = 300000; // msstatic constexpr uint32_t DUMP_TIMEOUT = 100000; // msstatic constexpr uint32_t KILL_TIMEOUT = 300000; // msfoundation/ability/ability_runtime/services/abilitymgr/src/ability_manager_service.cppconstexpr auto DATA_ABILITY_START_TIMEOUT = 500s;foundation/ability/ability_runtime/services/abilitymgr/src/user_controller.cppnamespace {const int64_t USER_SWITCH_TIMEOUT = 300 * 1000; // 3s}foundation/ability/ability_runtime/services/appmgr/include/app_mgr_service_event_handler.hstatic constexpr uint32_t TERMINATE_ABILITY_TIMEOUT = 300000; // ms static constexpr uint32_t TERMINATE_APPLICATION_TIMEOUT = 1000000; // msstatic constexpr uint32_t ADD_ABILITY_STAGE_INFO_TIMEOUT = 300000; // msstatic constexpr uint32_t START_SPECIFIED_ABILITY_TIMEOUT = 300000; // msstatic constexpr uint32_t START_PROCESS_SPECIFIED_ABILITY_TIMEOUT = 500000; // msstatic constexpr uint32_t KILL_PROCESS_TIMEOUT = 300000; // ms
系统准备
系统镜像编译打包时会使用strip去掉符号,导致gdb调试时无法看到具体的代码信息,为了方便调试,可拷贝unstriped库文件,替换system镜像中的同名库文件(具体拷贝动态库依问题实际情况而定,此处仅示例替换方法)。
1.在编译服务器上,将编译好的system.img挂载到/mnt目录
注意:此步骤只能在虚拟机或者物理机上执行,docker环境不支持。
2.替换带符号的动态库
开始调试
**1.系统启动后,使用 ps –ef命令查询com.ohos.launcher进程ID
**
2.启动gdb attatch
红色字体显示的(no debugging symbols found),表示该库文件没有debug信息,可以按照系统准备第二步的方法将该库的unstriped版本拷贝到系统中就可以看到debug信息。
3. 输入 set follow-fork-mode child 命令,使gdb可以跟踪所有子进程。至此就可以使用gdb调试应用。
小型系统的处理思路类似且更简单:在APPSPAWN孵化器生成业务进程ID后,增加延时,方便挂上gdb;代码见base/startup/appspawn/lite/appspawn_process.c中函数CreateProcess。此处不做详述。
本文描述了gdb跟踪调试lanucher业务进程方法,详细描述了这一方法涉及到的调试代码适配修改、系统准备以及调试命令操作等细节处理,指导开发者提高定位业务程序问题的效率。