signal 11(SIGSEGV) fault addr deadbaad错误处理

首先,你得承认经常看到这样的log是要崩溃的。。。。
没有任何debug信息,你不知道是哪句出错了,你只知道,深层的so调用出问题了。。。
而你基本不可能再去修改这些文件去适应你的程序,因为
1. 能力不足
2. 即使实力超群,投入产出比也相当的不经济,有这些精力解决自己的程序才是正道~
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadbaad,在使用box2d extension的时候你有可能会经常遇到这样的问题,尤其是当你的应用比较大,比较复杂的时候~
在下才疏学浅,只好用最土的办法,隔行打log。。。。
经过无数次的研究尝试,总结出如下规律~

它通常发生在
1. 创建Body
2. 移动Body
3. 删除Body
4. ContactListener
5. 手动设置Body对象为null
6. 还没遇到。。。。

深层次原因

同样经过无数次的试验和失败,几次挑灯夜战,越过重重**,翻阅无数资料,发现这类问题,基本都是深层代码的NullPointerReference所导致的


Bug解释
Box2D里有一个arraylist来维护当前所有的body,是在某个线程(可能是当前工作线程)运行的for循环,如果你在某个线程对body做了一些操作(如destory body)就会导致问题出现。。。

如何避免
1. 不要频繁大规模的创建/删除body对象,尽可能复用可以复用的对象,暂时不用的应当设置为不可见,使body sleep
2. 管理好你的线程,永远使用一个相同的线程来创建/管理/修改body对象
3. ContactListener中不要放实际方法,尽量异步调用
4. 设计模式很重要。。。

解决实例
下面是我的解决实例,仅供参考

protected static Queue<Object> deleteQueue=new LinkedList<Object>();
IUpdateHandler deleteUpdateHandler=new IUpdateHandler() {
    @Override
    public void onUpdate(float pSecondsElapsed) {
        if (!deleteQueue.isEmpty()) {
            //Do the delete body work
        }
    }
}
Scene.registerUpdateHandler(deleteUpdateHandler);
同样,你也可以创建多个队列,分别管理你的创建/删除/碰撞检测等操作~

最后,永远不要指望什么灵丹妙药,这些解决方法也不是狗皮膏药,贴了就好,小心的设计你的模式,认真调试代码,否则这个问题会像牛皮癣一样难以彻底根除



2.错误可以用arm-eabi-addr2line工具找到signal错误时的代码行,但需要出错时的pc指针值, 

============Log info=============

01-01 23:59:09.196: I/DEBUG(91): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadd00d
01-01 23:59:09.196: I/DEBUG(91):  r0 00000000  r1 00f92310  r2 00000000  r3 00000000
01-01 23:59:09.196: I/DEBUG(91):  r4 deadd00d  r5 40aadc78  r6 0000020c  r7 5287794c
01-01 23:59:09.196: I/DEBUG(91):  r8 4bf7b940  r9 4bf7be50  10 0106c080  fp 01132bc0
01-01 23:59:09.196: I/DEBUG(91):  ip 00000000  sp 528776e0  lr 40a45f8f  pc 40a45f8e  cpsr 60000030
01-01 23:59:09.326: I/DEBUG(91):          #00  pc 00050f8e  /system/lib/libdvm.so (dvmAbort)
01-01 23:59:09.326: I/DEBUG(91):          #01  pc 00055bd4  /system/lib/libdvm.so (_Z20dvmDecodeIndirectRefP6ThreadP8_jobject)
01-01 23:59:09.327: I/DEBUG(91):          #02  pc 000589f8  /system/lib/libdvm.so
01-01 23:59:09.329: I/DEBUG(91):          #03  pc 0004b44a  /system/lib/libchromium_net.so
01-01 23:59:09.330: I/DEBUG(91):          #04  pc 0004b4aa  /system/lib/libchromium_net.so

============Log info============== 

===========Command=============

./prebuilt/linux-x86/toolchain/arm-eabi-4.4.3/bin/arm-eabi-addr2line -C -f -e ./out/target/product/a2_3g_data/symbols/system/lib/liblewebcore.so 0x0004b44a
================================

BFD: Dwarf Error: mangled line number section.
WebCore::MediaPlayerPrivate::setVisible(bool)
diy-fp.cc:0



关注我的技术公众号,查看更多优质技术文章推送

微信扫一扫下方二维码即可关注:

关注我的技术公众号,查看更多优质技术文章推送




<think>我们正在处理一个关于段错误SIGSEGV)的具体问题,错误类型为SEGV_ACCERR,错误地址为0x000000709acfc000。根据引用[1]和[2],我们可以知道: - 段错误信号(SIGSEGV)通常由内存访问违规引起。 - 具体到SEGV_ACCERR,它表示访问权限错误(access permission fault),即进程试图访问没有权限的内存区域。 引用[2]中的fsr_info数组给出了ARM架构下不同错误类型的处理方式。其中,SEGV_ACCERR对应两种情况: 1. 段权限错误(section permission fault): 由二级页表(段级别)的权限错误引起,处理函数为`do_sect_fault`。 2. 页权限错误(page permission fault): 由页级别的权限错误引起,处理函数为`do_page_fault`。 因此,SEGV_ACCERR表示进程试图访问一个内存地址,但是该地址的页表项(PTE)中规定的访问权限不允许当前操作(例如,试图写入只读内存,或从不可执行的内存区域执行指令等)。 错误地址0x000000709acfc000是一个用户空间的虚拟地址。我们需要分析该地址的访问权限。 可能的原因及解决方法: 1. **空指针解引用或未初始化指针**:虽然地址0x000000709acfc000不是典型的空指针(NULL通常是0x0),但可能是未初始化或已释放的指针。 - 检查代码中指针的使用,确保指针被正确初始化并且指向有效的内存区域。 2. **访问已释放的内存**:如果该地址对应的内存已经被释放(free或delete),再次访问会导致段错误。 - 使用内存调试工具(如Valgrind)来检测内存访问错误。 3. **内存越界访问**:数组越界或缓冲区溢出可能会覆盖指针,导致其指向无效地址,或者直接越界访问到没有权限的区域。 - 检查数组和缓冲区的边界。 4. **权限问题**:尝试写入只读内存区域,或尝试执行非执行区域。 - 只读内存区域包括:代码段(text段)、常量数据段(rodata段)以及通过mmap映射为只读的内存。 - 例如,如果程序试图修改一个字符串常量,就会触发SEGV_ACCERR。 ```c char *str = "hello"; // str指向只读内存 str[0] = 'H'; // 试图修改只读内存,将导致段错误 ``` - 解决方法:确保字符串常量赋值给`const char*`类型,或者使用字符数组。 5. **栈溢出**:可能导致访问了不可访问的栈空间。 - 检查是否有无限递归或过大的局部变量。 6. **多线程同步问题**:多个线程同时访问同一内存区域,其中一个线程释放了内存,而另一个线程还在使用。 - 使用同步机制(如互斥锁)保护共享资源。 7. **使用mmap映射的内存区域权限不足**:例如,映射时指定了PROT_READ,但试图写入。 - 检查mmap调用时的保护标志(prot参数)。 8. **自定义信号处理程序**:引用[1]中设置了自定义的SIGSEGV处理程序(_segv_handler)。如果自定义处理程序没有正确处理信号,或者处理后程序继续执行到错误地址,也会导致问题。 - 检查自定义信号处理程序是否正确地处理了错误,或者是否应该终止程序。 **调试方法:** 1. **使用调试器**(如gdb): - 运行程序直到崩溃,查看崩溃时的调用栈(backtrace)和寄存器状态。 - 使用命令:`info registers`查看寄存器,特别是崩溃地址附近的寄存器值,判断指针来源。 - 使用命令:`x/x 0x000000709acfc000`查看该地址的映射情况(是否有效,权限如何)。 2. **使用Valgrind**:运行`valgrind --tool=memcheck ./your_program`,检查内存错误。 3. **检查/proc文件系统**:在程序崩溃时,可以通过/proc/<pid>/maps查看该地址是否在进程的地址空间中,以及权限如何。 - 例如,在信号处理程序中,可以打印maps文件内容,或者使用gdb的`info proc mappings`命令。 4. **查看内核日志**:使用`dmesg`命令查看内核日志,可能会提供更多关于错误的信息。 **解决方法:** 根据上述可能的原因,逐一排查。具体到本例,由于错误地址是0x000000709acfc000,我们可以通过gdb查看该地址的映射信息: 在gdb中,当程序崩溃后,使用: ``` (gdb) info proc mappings ``` 或者 ``` (gdb) shell cat /proc/$(pidof your_program)/maps ``` 查找0x000000709acfc000所在的映射区域,查看其权限(读、写、执行)。 如果该地址不在任何映射区域内,或者权限不足(例如,你试图写入一个只有读权限的映射区域),那么就需要检查代码中对该地址的操作。 如果该地址是堆内存,检查堆分配和释放的日志。如果是栈内存,检查栈大小。 如果问题发生在动态链接库中,检查是否使用了不兼容的库版本。 最后,如果问题难以定位,考虑使用AddressSanitizer(ASan)进行编译,它可以在运行时检测内存错误。 编译时添加`-fsanitize=address`选项,然后运行程序,ASan会提供详细的错误报告。 示例编译命令: ```bash gcc -g -fsanitize=address -o program program.c ``` 总结:SEGV_ACCERR错误通常是由于内存访问权限问题导致的,需要仔细检查代码中的内存访问操作,并使用调试工具辅助定位。 §§相关问题§§ 1. 如何分析段错误SIGSEGV)的产生原因? 2. 使用Valgrind检测内存错误的具体步骤是什么? 3. 在Linux中,如何查看进程的内存映射? 4. 什么是AddressSanitizer(ASan)?如何使用它?</think>### 段错误 SIGSEGV SEGV_ACCERR 的原因与解决方法 #### 错误分析 信号 11 (`SIGSEGV`) 表示**段错误**(Segmentation Fault),具体错误类型 `SEGV_ACCERR` 表示**内存访问权限错误**。当进程试图访问没有操作权限的内存地址时触发(如尝试写入只读内存、执行非可执行内存等)。错误地址 `0x000000709acfc000` 提供了关键线索。 根据引用[2],`SEGV_ACCERR` 对应两种具体场景: 1. **页权限错误**(Page Permission Fault):访问内存页时违反页表项(PTE)权限 2. **段权限错误**(Section Permission Fault):访问内存段时违反二级页表权限 #### 常见原因 1. **非法写入操作** - 尝试修改只读内存(如代码段 `.text` 或常量区 `.rodata`) - 示例:`char *str = "常量"; str[0] = 'X';` // 修改字符串常量 2. **执行非可执行内存** - 尝试执行标记为不可执行的内存(如堆栈或数据段) - 常见于缓冲区溢出攻击或错误函数指针调用 3. **内存映射权限冲突** - 通过 `mmap()` 映射内存时指定了 `PROT_READ`,但尝试写入 - 示例:`void *p = mmap(..., PROT_READ, ...); *(int*)p = 42;` 4. **硬件/内核异常**(引用[2]) - ARM架构中 `fsr_info[]` 数组处理 `SEGV_ACCERR` 时调用 `do_page_fault` 或 `do_sect_fault` - 可能由内存管理单元(MMU)权限校验失败引发 #### 调试与解决方法 ##### 1. 定位错误地址(关键步骤) ```bash # 查看进程内存映射 cat /proc/$(pidof 程序名)/maps | grep 709acfc000 # 使用gdb检查地址属性 (gdb) info proc mappings # 列出所有内存区域 (gdb) x/10x 0x709acfc000 # 检查地址内容 ``` - 若地址属于 `[vdso]` 或 `[vsyscall]`:通常是内核空间误访问 - 若地址在 `heap`/`stack` 区域:检查缓冲区溢出或野指针 ##### 2. 代码检查点 ```c // 典型错误示例 const int *p = &constant_var; *p = 42; // 尝试修改常量 void (*func_ptr)() = (void*)0x709acfc000; func_ptr(); // 执行非可执行地址 ``` ##### 3. 动态诊断工具 - **Valgrind** 检测非法访问: ```bash valgrind --tool=memcheck ./your_program ``` - **GDB** 捕获信号: ```bash (gdb) catch signal SIGSEGV (gdb) run # 崩溃时用 bt 查看调用栈 ``` ##### 4. 信号处理(引用[1]) 可注册自定义信号处理程序捕获错误: ```c #include <signal.h> void _segv_handler(int sig, siginfo_t *info, void *ucontext) { printf("SEGV at %p\n", info->si_addr); // 打印故障地址 exit(1); } // 注册处理函数 struct sigaction sa = {0}; sa.sa_sigaction = &_segv_handler; sa.sa_flags = SA_SIGINFO; sigaction(SIGSEGV, &sa, NULL); ``` ##### 5. 特殊场景处理 - **环境变量问题**(引用[3]):确保 `TERM` 设置正确 ```bash export TERM=vt100 # 避免终端兼容性问题 ``` - **内核模块问题**:若发生在驱动中,检查 `ioremap()` 的缓存属性和访问权限 #### 总结流程 ```mermaid graph TD A[发生 SEGV_ACCERR] --> B{定位错误地址} B --> C[检查内存映射权限] C -->|只读内存| D[检查常量修改/字符串操作] C -->|非可执行内存| E[检查函数指针/缓冲区溢出] C -->|未映射区域| F[检查野指针/内存释放] D --> G[改用可写内存] E --> H[添加执行权限或修正指针] F --> I[使用Valgrind检测] ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值