Неправильная трассировка стека в ядре по необработанному std :: exception - PullRequest
4 голосов
/ 05 января 2012

Вот мой код:

#include <string>
#include <tr1/functional>

using namespace std;
using namespace std::tr1;
using namespace std::tr1::placeholders;

class Event
{
public:
  typedef std::tr1::function<void()> Handler;

  void set(Handler h)
  {
    m_handler = h;
  }

  template<typename T, typename F>
  void set(T * obj, F memfn)
  {
    set(std::tr1::bind(memfn, obj));
  }

  void operator()()
  {
    m_handler();
  }

  static void fire(Event * event) throw ()
  {
    (*event)();
  }

  Handler m_handler;
};

class BuggyHandler
{
public:

  BuggyHandler()
  {
  }

  BuggyHandler(Event * b) :
      bar(b)
  {
    bar->set(this, &BuggyHandler::HandleEvent);
  }

  void HandleEvent()
  {
    // throw std::length_error
    std::string().append(std::numeric_limits<size_t>::max(), '0');
  }

private:

  Event * bar;
};

void get_correct_stacktrace()
{
  Event bar;
  BuggyHandler handler(&bar);
  bar();
}

void get_incorrect_stacktrace()
{
  Event bar;
  BuggyHandler handler(&bar);
  Event::fire(&bar);
}

int main(int argc, char **argv)
{
  int opt = atoi(argv[1]);
  if (opt)
    get_correct_stacktrace();
  else
    get_incorrect_stacktrace();
}

Когда я вызываю ./test 1, я могу получить правильную трассировку стека от ядра:

#0  0xffffe410 in __kernel_vsyscall ()
#1  0xf7d028d0 in raise () from /lib/libc.so.6
#2  0xf7d03ff3 in abort () from /lib/libc.so.6
#3  0xf7ede880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4  0xf7edc2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5  0xf7edc2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6  0xf7edc41a in __cxa_throw () from /usr/lib/libstdc++.so.6
#7  0xf7e73c6f in std::__throw_length_error () from /usr/lib/libstdc++.so.6
#8  0xf7eb9a17 in std::string::append () from /usr/lib/libstdc++.so.6
#9  0x08049b96 in BuggyHandler::HandleEvent (this=0xffc26c9c) at /home/liangxu/release/server_2.0/test/src/test.cc:54
#10 0x08049857 in get_correct_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:67
#11 0x080498e0 in main (argc=Cannot access memory at address 0x5ac6) at /home/liangxu/release/server_2.0/test/src/test.cc:81

Местоположение исключения для броска - test.cc:54

.

Когда я вызываю ./test 0, я могу получить неверную трассировку стека от ядра:

#0  0xffffe410 in __kernel_vsyscall ()
#1  0xf7d508d0 in raise () from /lib/libc.so.6
#2  0xf7d51ff3 in abort () from /lib/libc.so.6
#3  0xf7f2c880 in __gnu_cxx::__verbose_terminate_handler () from /usr/lib/libstdc++.so.6
#4  0xf7f2a2a5 in std::exception::what () from /usr/lib/libstdc++.so.6
#5  0xf7f2a2e2 in std::terminate () from /usr/lib/libstdc++.so.6
#6  0xf7f2a305 in std::exception::what () from /usr/lib/libstdc++.so.6
#7  0xf7f29d98 in __cxa_call_unexpected () from /usr/lib/libstdc++.so.6
#8  0x080497eb in get_incorrect_stacktrace () at /home/liangxu/release/server_2.0/test/src/test.cc:30
#9  0x080498f5 in main (argc=Cannot access memory at address 0x5adf) at /home/liangxu/release/server_2.0/test/src/test.cc:83

Нет места для исключения броска.

Мой компилятор - "gcc (GCC) 4.1.2 20070115 (предварительная версия) (SUSE Linux)"

Если скомпилировано с "-fno-exceptions", оба метода генерируют правильную трассировку стека.

В чем причина?

1 Ответ

4 голосов
/ 05 января 2012

Обе трассировки стека верны.

Когда вы вызываете Event::fire, исключение выдается в HandleEvent и раскрутка стека происходит до тех пор, пока не встретится fire с , спецификация исключений.

Если вы не знаете реального поведения спецификаций исключений, вы можете прочитать об этом здесь: http://www.gotw.ca/publications/mill22.htm

По сути, спецификация throw () действительно гарантирует, что этот методне выбрасывает, терпя неудачу, если один из содержащих вызовов бросает.Когда разматывание стека пытается выйти из этого метода, оно проверяет спецификацию исключения, видит, что оно не совпадает, и вызывает std::unexpected из текущего местоположения раскручивания, таким образом, __cxa_call_unexpected () в вашей трассировке стека сразу после get_incorrect_stacktrace ().

В большинстве случаев использование спецификаций исключений в C ++ бесполезно, потому что гарантия предоставляется за счет общего сбоя программы, который выдает и не должен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...