zoukankan      html  css  js  c++  java
  • dbtool一bug跟踪记

      注:这篇日志是好多年前,我还在从兴公司时写的。现在都从从兴公司离职很久了,从兴也没落了,可惜。看了一下,虽然出现了部分代码,但不至于泄漏什么机密,查bug过程的原理也有可以让新手借鉴的地方,就原文照搬上来了。

      dbtool是营帐研发部常用的一个类sqlplus数据库查询工具,它提供了较sqlplus更友好的输出界面,十分适合在命令行下操作,故在部门内部使用相当广泛。

           不过它一直有一个bug,使用过程中偶尔会出现执行某条sql后core down的情况。但是由于这种情况较少见,而且bug出现随机性太大,所以一直也没人去管它。

           今天早上加班过程中,居然又让我碰上这个bug了。不过这次bug很有规律,每次连接上数据库后立即执行“select userenv('language') from dual;”程序立即core down。

           由于是周日,比较有空,而且天赐良机,居然能重现bug,于是决定花点时间解决这个bug。

           首先使用dbx看一下程序在哪core

    Segmentation fault(coredump)
    bbkf:/home/report/c++/report/dbtool$dbx dbtool
    Type 'help' for help.
    [using memory image in core]
    reading symbolic information ...
    
    Segmentation fault in malloc_y at 0x90000000005bb10 ($t1)
    0x90000000005bb10 (malloc_y+0x5a4) 90050000         stw   r0,0x0(r5)
    (dbx) where
    malloc_y(0xa1, 0x0, 0x0, 0x7f7f7f7f, 0x2, 0x2d2d0049, 0x2d, 0x100233704) at 0x90000000005bb10
    malloc_common_79_63(??) at 0x900000000058948
    _Fancy_malloc__FUl(??) at 0x900000000451ae4
    __nw__FUl(??) at 0x9000000004517c0
    _Allocate__3stdHc_UlPc_Pc(??, ??) at 0x9000000004c5834
    allocate__Q2_3std9allocatorXTc_FUlPCv(??, ??, ??) at 0x9000000004c57c4
    _Copy__Q2_3std12basic_stringXTcTQ2_3std11char_traitsXTc_TQ2_3std9allocatorXTc__FUl(??, ??) at 0x9000000004c564c
    _Grow__Q2_3std12basic_stringXTcTQ2_3std11char_traitsXTc_TQ2_3std9allocatorXTc__FUlb(??, ??, ??) at 0x9000000004c4ec8
    assign__Q2_3std12basic_stringXTcTQ2_3std11char_traitsXTc_TQ2_3std9allocatorXTc__FPCcUl(??, ??, ??) at 0x9000000004c4bac
    assign__Q2_3std12basic_stringXTcTQ2_3std11char_traitsXTc_TQ2_3std9allocatorXTc__FPCc(??, ??) at 0x9000000004c4974
    __ct__Q2_3std12basic_stringXTcTQ2_3std11char_traitsXTc_TQ2_3std9allocatorXTc__FPCc(??, ??) at 0x9000000004c4880
    GetColNameDisplay(std::basic_string<char,std::char_traits<char>,std::allocator<char> >&,std::basic_string<char,std::char_traits<char>,std::allocator<char> >&)(this = 0x0fffffffffffaba8, colsName = &(...), prompt = &(...)), line 884 in "cmdDeal.cpp"
    GetDBData(int)(this = 0x0fffffffffffaba8, bAutoContinue = 0), line 580 in "cmdDeal.cpp"
    CCmdDeal::Run()(this = 0x0fffffffffffaba8), line 273 in "cmdDeal.cpp"
    main(argc = 2, argv = 0x0ffffffffffff338), line 249 in "dbtool.cpp"
    (dbx)

      从上面可以看到,出错位置在string里面,根据经验,这种bug好解决,一般是string操作错误,例如将一个没有结尾的字符数组赋值给string

           用vi打开cmdDeal.cpp,定位到884行,发现如下代码

      +875  int CCmdDeal::GetColNameDisplay(string &colsName, string &prompt)
      +876  {
      +877          char chSplit = ' ';
      +878          int i, len = 0;
      +879          int rows = 0;
      +880          int maxNameLen = 0;
      +881          int lineSize = 0;
      +882          int displayMode = 0;
      +883          string s;
      +884          string str45 = "-------------------------------------------------------------------"
      +885                                     "-------------------------------------------------------------------";

           居然是正常的字符串初始化出错! 这就郁闷了,这行代码怎么看都是正常的,根据经验,这种正常的代码出错,一般是由于前面某个位置出现了越界操作导致的。这种错误相对难查很多。

           浏览了一下附近的代码,都没发现什么异常的代码,于是只能加上一些调试代码看能不能找出问题。经过一番尝试后发现,在GetColNameDisplay里面初始化长度较大的字符串就会导致程序core down。并且这个规律在GetDBData(调用GetColNameDisplay的函数)里面也有效。

           这就好办了,只要找出在哪段代码执行后会出现这种奇怪的现象,问题代码应该就在那。

      于是在程序里面加了N多个下面的代码片段

    1 {
    2     string str45 = "-------------------------------------------------------------------"
    3                                        "-------------------------------------------------------------------";
    4 string str451 = "-------------------------------------------------------------------"
    5                                        "-------------------------------------------------------------------";
    6     printf("[%s:%d]
    ", __FILE__, __LINE__);
    7 }

           上面的调试代码特意括在大括号里面的,目的有两个,

      1 括号使得str45变成局部变量,这样不会出现变量冲突

      2 保证str45尽快析构,以免影响错误定位

      加上代码后编译运行,看程序在哪两行代码之间挂掉,接着在那两行代码中间增加更多调试代码,反复运行,很快就定位到了出错函数GetColumnInf。程序在调用这个函数之前调试代码执行不会有问题,在之后就挂了。GetColumnInf这个函数比较短,仔细看了一下,发现下面这段代码可能有问题。

          

             

     1 vector<ColInf>::iterator it = m_vecColInf.begin();
     2     int Index = 1;
     3     COciCursor        cur( *m_pOracle );
     4     COciColumnDesc    coldesc;
     5     
     6     cur.Parse(strSql.c_str());
     7     
     8     do
     9     {
    10         cur.DescribeColumn( Index, coldesc );
    11         if (it->type != 12 && it->type != 1)
    12         {
    13             it->prec = it->type==12 ? it->prec : coldesc.m_Prec;
    14         }
    15         it++;
    16         Index++;
    17     } while ( !cur.EndOfDesc() );
    18     cur.Close();

      代码里面对it操作时,没有判断it是否越界,整个函数看起来就这个最可能出现问题了。试着给while循环加了一个判断条件变成“while ( !cur.EndOfDesc() && it != m_vecColInf.end() );”

    重新编译运行。

    1 jmzw@boss15test >select userenv('language') from dual; 
    2 
    3 USERENV('LANGUA      
    4 -------------------------
    5 AMERICAN_AMERICA.ZHS16GBK
    6 
    7 1 rows selected.

      猜对了,程序能正确输出,不会再core掉! 接下只需要分析这段代码为何会越界,修复即可。不过一看时间已经十一点多,不知不觉浪费了近半个上午,而且这段代码看起来作用只是获取数据库字段精度,不是很重要,所以此次根跟踪到此为止吧。

      此次收获经验:写代码还是谨慎点,加多一些边界判断比较好,不然贪图一时方便可能导致花大把时间在定位bug

  • 相关阅读:
    Flutter 复制文本到剪切板、从剪切板读取文本【转】
    CSS元素超出部分滚动,并隐藏滚动条【转】
    Flutter解决按钮请求防重复提交【转】
    CSS文字超出宽度自动换行【转】
    flutter 使用 url_launcher 唤起三方应用【转】
    Dart 基本数据类型与类型归属判断【转】
    JS 生成唯一字符串UUID【转】
    gtk+笔记
    Win32 API笔记
    utf-8与utf-16的区别
  • 原文地址:https://www.cnblogs.com/kingstarer/p/9638143.html
Copyright © 2011-2022 走看看