Chinaunix首页 | 论坛 | 博客
  • 博客访问: 843113
  • 博文数量: 190
  • 博客积分: 2991
  • 博客等级: 少校
  • 技术积分: 2400
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-24 18:11
文章分类

全部博文(190)

文章存档

2015年(3)

2014年(1)

2013年(65)

2012年(121)

我的朋友

分类: C/C++

2012-11-06 09:35:57

  很诡异,竟然在delete 指针时候crash,这个很少见~~~分析这个问题费了不少周折,记录在此。如能帮助到他人,不胜开心。

    拿到的运行时候堆栈包含如下部分:

.......

Back chain word     : 183831f8
        b930a150  [1003a150],CXC1734416%2_R1E       ??:0  heap_free

  Back chain word     : 18383208
        b92ff0dc  [1002f0dc],CXC1734416%2_R1E       ??:0  free

  Back chain word     : 18383218
        ba161414  [10e91414],CXC1734416%2_R1E       ??:0  operator delete


  开始以为是对指针重复释放,代码走读后发现排除此种可能。然后怀疑多线程内存同步有问题,后排除。

  有同事怀疑是OS的问题,不过一般我不会怀疑OS的,比较OS一般都是很稳定的,而且这次OS没有重大升级。为了排除这种可能,还是解码了一下堆栈的顶部信息

注:下面的信息其实是在 heap_free之上,所以同事怀疑是OS问题 

Stacktrace :
  Using stackpointer  : 0041b9cc

  Back chain word     : 097873b0
        008136d8  [008136d8],CXC1729957_R81B13      ??:0  Cs_reportUnexpectedSignal_v

  Back chain word     : 097873f0
        00828ac0  [00828ac0],CXC1729957_R81B13      /vobs/cello/cls_src/CLS_CRX90145_1/ERROR-MANAGER_CNX9011053_2/src/swu/error/error_handler.c:768  eman_install_appl_exten
  Back chain word     : 09787408
        009d9f10  [009d9f10],CXC1729957_R81B13      ??:0  splaycmp_hunt_from

  Back chain word     : 09787448
        009dc700  [009dc700],CXC1729957_R81B13      ??:0  zzkrn_get_fsem

  Back chain word     : 09787450
        00a1d258  [00a1d258],CXC1729957_R81B13      ??:0  zzkrn_create_context

  Back chain word     : 09787460
        00a1b414  [00a1b414],CXC1729957_R81B13      ??:0  zzkrn_create_context


  Back chain word     : 09787498
        009b2514  [009b2514],CXC1729957_R81B13      ??:0  ose_hfmi_get_heap_reference

  Back chain word     : 097874c0
        009b410c  [009b410c],CXC1729957_R81B13      ??:0  zzheap_realloc

  Back chain word     : 09787500
        009b910c  [009b910c],CXC1729957_R81B13      ??:0  l_buddy_query_req

  Back chain word     : 09787540
        009b4784  [009b4784],CXC1729957_R81B13      ??:0  buf_check_header

  Back chain word     : 097875a0
        009b1bc8  [009b1bc8],CXC1729957_R81B13      ??:0  l_heap_error

  Back chain word     : 11c8ef68
        00a1b414  [00a1b414],CXC1729957_R81B13      ??:0  zzkrn_create_context

  Back chain word     : 11c8ef70
        009b11c8  [009b11c8],CXC1729957_R81B13      ??:0  ose_handle_create_heap

简单推测,应该是delete抛出异常被OS捕获,然后发出某种signal终止应用程序,由此应该不是OS的问题。既然不是OS的问题,还是从自身找原因:)

因为这是个必现的问题,单步跟踪是个很好的方法。但由于程序跑在嵌入式OS上面,之前一直不太清楚怎么单步,幸运的是终于有老外同事共享了单步的方法^_^

虽然编译和上传load module有点麻烦,没办法,谁让单步比较好用呢。

      设置断点后单步跟踪,设置指针为watch point,其实意义不大。单步观察调用栈,果然如同之前设想,在析构函数中逐个调用各个attribute的析构函数。

在单步过程中还有一个插曲,就是inline 函数的调用。开始没有注意是inline的,发现GDB对某些函数不能单步,直接尝试GDB中运行会导致GDB收到SIGTRAP,异常退出,开始以为是这个地方的原因,仔细一看发现是inline函数。哎。。。重新单步跟踪后,发现所有析构函数执行完成后程序crash,开始怀疑最后一个析构函数的地方。检查后发现并没有可以怀疑的地方,然后仔细检查内存数据的内容终于发现端倪:

开始的数据:

{ = { = {_vptr.EcEcmiD = 0xb9f580c0}, clientProcessId = {_vptr.EcClientProcessIdD = 0xb9f8b288,
      static rtg_EcClientProcessIdD_fields = {{name = 0xb9f05814 "myValue", offset = 4, type = 0xb9f343d4, modifier = 0x0}}, myValue = 66851}, clientId = {
      _vptr.EcClientIdD = 0xb9f8b298, static rtg_EcClientIdD_fields = {{name = 0xb9f05814 "myValue", offset = 4, type = 0xb9f343a4, modifier = 0x0}, {
          name = 0xb9f0597c "valid", offset = 8, type = 0xb9f342e4, modifier = 0x0}}, myValue = 0, valid = true}}, result = 4294967295, auxPiuType = {myValue = 49152},
  operState = 4294967295, availStatus = {static rtg_RbsAvailStatusCollectionD_fields = 0xb9f1e07c, myValue = 0}, admState = 4294967295, ledInfo = {
    redLed = EQC_LED_NOT_APPLICABLE, greenLed = EQC_LED_NOT_APPLICABLE, yellowLed = EQC_LED_NOT_APPLICABLE, blueLed = EQC_LED_NOT_APPLICABLE}, errorCode = {myValue = 1},
  hubPosition = {_vptr.BlibStringD = 0xb9f8c790, static rtg_BlibStringD_fields = {{name = 0xb9f1a040 "m_capacity", offset = 4, type = 0xb9f343a4, modifier = 0x0}, {
        name = 0xb9f1a04c "m_contents", offset = 8, type = 0xb9f34314, modifier = 0xb9f1c64c}, {name = 0xb9f1a058 "m_size", offset = 12, type = 0xb9f343a4,
        modifier = 0x0}}, m_capacity = 3, m_contents = 0x4085e680 "NA", m_size = 2, static emptystring = ""},uniqueHwId = {uniqueHwIdLength = 65 'A',
    uniqueHwId = "\000\0000ecMtrCdciTimer\000\022"}}

core dump前:

{ = {_vptr.EcEcmiD = 0xb9f8b200}, clientProcessId = {_vptr.EcClientProcessIdD = 0x0, static rtg_EcClientProcessIdD_fields = {{
          name = 0xb9f05814 "myValue", offset = 4, type = 0xb9f343d4, modifier = 0x0}}, myValue = 0}, clientId = {_vptr.EcClientIdD = 0x0,
      static rtg_EcClientIdD_fields = {{name = 0xb9f05814 "myValue", offset = 4, type = 0xb9f343a4, modifier = 0x0}, {name = 0xb9f0597c "valid", offset = 8,
          type = 0xb9f342e4, modifier = 0x0}}, myValue = 0, valid = false}}, result = RBS_OK, auxPiuType = {myValue = 0}, operState = RBS_OPER_DISABLED, availStatus = {
    static rtg_RbsAvailStatusCollectionD_fields = 0xb9f1e07c, myValue = 0}, admState = RBS_ADMIN_LOCKED, ledInfo = {redLed = EQC_LED_NOT_APPLICABLE,
    greenLed = EQC_LED_NOT_APPLICABLE, yellowLed = EQC_LED_NOT_APPLICABLE, blueLed = EQC_LED_NOT_APPLICABLE}, errorCode = {myValue = 0}, hubPosition = {
    _vptr.BlibStringD = 0x0, static rtg_BlibStringD_fields = {{name = 0xb9f1a040 "m_capacity", offset = 4, type = 0xb9f343a4, modifier = 0x0}, {
        name = 0xb9f1a04c "m_contents", offset = 8, type = 0xb9f34314, modifier = 0xb9f1c64c}, {name = 0xb9f1a058 "m_size", offset = 12, type = 0xb9f343a4,
        modifier = 0x0}}, m_capacity = 0, m_contents = 0x0, m_size = 0, static emptystring = ""}, uniqueHwId = {uniqueHwIdLength = 0 '\0',
    uniqueHwId = '\0' }}

难道uniqueHwId 是定长数组?检查代码后果然如此!!!看到uniqueHwId = "\000\0000ecMtrCdciTimer\000\022" ,推测应该是随机值。在分配内存后,对数据进行赋值时使用长度65做终止条件,导致写越界。后检查在出问题的时候,uniqueHwId 确实没有初始化,从而导致随机值。分析道这里,立刻响起Inside c++ object model里面关于new/delete的深入说明,实际上编译器会在分配的内存首位处加上格外的一些数据,比较这块内存的大小,起始地址等其他必要信息,而这些信息就是给delete使用的,主要可以保证释放内存。而且还可能在内存尾巴处填写“保护数据”,一旦检测到这些数据被擦写,即认为有错可以激活异常或者其他错误机制。这样的话,就可以解释core dump的原因了,程序由于越界写导致new的尾巴保护数据被擦写,在delete时候抛出异常导致crash.

   分析到这来,其实就是一个普通的越界写的问题,但是由于掩盖在new/delete的内幕之下,又多少带了些神秘色彩。一般来说,越界写会导致随机crash,当出现这种情况时候很容易怀疑越界写。但这次,却只有在delete的时候crash,让我忽略了忘这方面考虑。

  总结:

     一些看似复杂的问题其实只是小问题的组合,适当的排除,分解逐步细化问题,直至发现真相

   不要轻易怀疑OS和成熟的library,宁肯多怀疑自己:)

    注意积累(就像new/delete的原理一样,还以为在这种高度集成的产品了不需要这些知识一样,说不准以前觉得没用的东西就会跳出来告诉你“我很有用的”)

   多用google, 英文搜索关键词,呵呵~~

阅读(891) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~