Core Dump Pattern 두번째

Code Dump Pattern에 대해서는 지난 번 글에서 얘기한 바 있습니다. 한동안 잠잠하다가 다시 core dump가 발생했는데 이번에도 제가 담당하는 부분이 아니어서 보지 않고 있다가 다른 분이 pstack을 구해 공지한 것을 보니 대략 원인이 보이더군요.

그럼 먼저 증상을 보여 드릴테니 원인을 찾아보세요. :-)

먼저 log 파일에는 pure virtual function called라는 문장이 출력되었으며 해당 pstack은 다음과 같았습니다.


Thread 127 (process 18441):
...
#17 0x01089514 in std::terminate () from /usr/lib/libstdc++.so.5
#18 0x01089a17 in __cxa_pure_virtual () from /usr/lib/libstdc++.so.5
#19 0x08ebc0d9 in DpOMidCallActiveInitial::process (this=0x555ff748,
bcsm=0x89faec1c)
#20 0x08e2bd77 in Bcsm (this=0x89faec1c,
bcsmState=_DpOMidCallActiveInitial, legId=1,
destinationRoutingAddress=0x0, cs=0x8f88aa14,
isArmedTDPExist=true)
#21 0x08e50e51 in BcsmKo (this=0x89faec1c, st=_DpOMidCallActiveInitial,
lt=1, ds=0x0, cs=0x8f88aa14)
#22 0x081eda42 in NPFactory::makeNewBcsm (st=_DpOMidCallActiveInitial,
lt=1, ds=0x0, cs=0x8f88aa14, isArmedTDBExist=true)
...


.

찾으셨나요?

사실 몇주 전에 pure virtual function called 에러에 관한 글을 쓰려고 한적이 있었는데 마침 똑같은 주제의 글이 The C++ Source에 올라오더군요. 여기의 글을 읽으시는 분들이면 대부분 구독하고 있는 사이트일 것 같아 쓰지 않기로 했었는데 이번에 이 내용의 core dump가 발생했네요.

이 에러의 원인에 대해 자세히 알고 싶으신 분들은 위의 링크를 참고하세요. 이해하기 쉽게 그림까지 곁들여 설명해 주고 있습니다. ((예전에 썼던 Study About Double Delete글에도 비슷한 내용이 있었습니다.))

이번에 발생한 문제는 위 글의 Two of the Classic Blunders 부분에 나와 있는 내용입니다. 그리고 Effective C++ 3rd Ed.의 Item 9: "Never call virtual functions during construction or destruction."에서도 설명하고 있는 내용이죠. 바로 클래스의 생성자에서 virtual function을 호출한 경우입니다. 다만 직접 호출한 것이 아니라 다른 함수를 한단계 거쳐 호출한 것이라 컴파일러가 warning을 발생시키지 않았습니다.

위의 pstack을 보면 BcsmKo는 Bcsm을 상속받은 클래스로 BcsmKo가 new되면서 먼저 Bcsm의 생성자가 호출되고 있습니다. 여기서 DpOMidCallActiveInitial 클래스의 process()라는 함수를 호출하면서 생성중인 자신의 this (Base*)를 넘기고 있습니다. 이 process()함수에서는 이 this (Base*)를 사용하여 Base 클래스의 어떤 함수를 호출하는데 이 함수가 바로 pure virtual function이었습니다.

원래 있었던 코드였는데 새로운 기능이 추가되기 전까진 한번도 실행되지는 않던 코드의 조합이었다고 하더군요. 에러가 발생한 원인을 설명해주자 관련 개발자들이 모여 얘기하는걸 들었는데 아마 다른 조합도 있을 것 같다며 괴로워하더군요. :-|

이번에도 지난번 글에서 만들었던 패턴의 형식으로 만들어 보았습니다.


  1. Name
    • Core on Constructor with "pure virtual function called" message


  2. Problem
    • pstack에서 생성자가 보이며 "pure virtual function called"라는 메시지가 출력된다.

      Thread 127 (process 18441):
      ...
      #17 0x01089514 in std::terminate () from /usr/lib/libstdc++.so.5
      #18 0x01089a17 in __cxa_pure_virtual () from /usr/lib/libstdc++.so.5
      #19 0x08ebc0d9 in bar(base=0x89faec1c)
      #20 0x08e2bd77 in Base (this=0x89faec1c)
      #21 0x08e50e51 in Derived (this=0x89faec1c)
      ...


  3. Cause
    • 원인은 다음과 같이 생성자에서 pure virtual function을 호출했을 가능성이 매우 높다.

      void bar(Base* );

      struct Base
      {
      Base() {
      bar(this);
      }
      virtual ~Base() {}
      virtual void foo() = 0;
      };

      struct Derived : Base
      {
      virtual ~Derived();
      };

      void bar(Base* b)
      {
      b->foo(); // oops! this is pure virtual function.
      }

      Base* obj = new Derived; // core dump


  4. Solution

    • 생성자에서 해당 pure virtual function을 호출하지 않아도 되는 방법이 있다면 그 방법을 사용한다.

    • 생성자외에 init()와 같은 별도의 초기화 함수를 만들어 사용한다. 이 방법으로 위의 코드를 수정해 보면 다음과 같다.

      struct Base
      {
      void init() {
      bar(this);
      do_init();
      }
      virtual ~Base() {}
      virtual void foo() = 0;
      virtual void do_init() {}
      };

      Base* obj = new Derived;
      obj->init();
      ((여기서 do_init()은 C++ of the Day #39 - State with NVI (or Template Method)에서 설명했던 NVI 함수입니다. 이렇게 함으로써 Base를 상속받는 클래스는 자신을 위한 초기화 내용을 추가할 수 있습니다.))



  5. Other Possible Causes
    • 없음




패턴으로 만들고 보니 "pstack에 생성자가 보이고 pure virual function called 에러가 발생한다"라는 원인에 해답이 다 있군요. 다음 패턴을 위해 core dump가 더 자주 발생하길 바래야 하는걸까요? ;-)

Comments

  1. 비슷한 주제의 글을 만나게 되어 반갑습니다. 저도 http://www.buggymind.com/84 여기에 같은 문제에 대한 글을 올렸습니다만... 트랙백남길까 하다가 못찾아서 덧글 남기고 갑니다. 좋은 하루 되세요 ^^

    ReplyDelete

Post a Comment

Popular Posts