Почему это вызвало бы сег. ошибка, и как я могу использовать GDB для его отладки? - PullRequest
0 голосов
/ 19 марта 2020

Сам код чрезвычайно простой. Я использую Catch2 для модульного тестирования (мне очень нравится его интерфейс) и разбиваю на gdb, но не получаю никакой полезной информации для сега. ошибка, вызванная указанным простым кодом.

Я точно знаю что вызывает проблему, но я не знаю почему , или как я мог бы получить строку кода с ошибкой из gdb (Я широко использую эквивалент Python, pdb, но ошибки в Python кажутся гораздо более простыми).

Flop.hpp

#ifndef FLOP
#define FLOP
class Flop {
     private:
        int tiles_[200][200][200];
     public:
          Flop();
}
#endif

Флоп. cpp

#include "Flop.hpp"
Flop::Flop() { }

test_Flop. cpp

#include "catch.hpp"
#include "Flop.hpp"
SCENARIO("I bang my head against a wall") {
    Flop flop;
    WHEN("I try to run this test") {
        THEN("This program SEGFAULTs") {
            REQUIRE(1==1);
        }
    }
}

main. cpp содержит все, что должно, наряду с загруженным catch.hpp (как указано в руководстве).

Я компилирую это с помощью: g++ Flop.cpp test_Flop.cpp main.cpp -o run_test и запускаю это с gdb -ex run --args ./run_test -b, что позволяет Catch2 взломать отладчик. Результат таков:

Program received signal SIGSEGV, Segmentation fault.
0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()

С обратным ходом:

#0  0x0000555555566e9e in ____C_A_T_C_H____T_E_S_T____0() ()
#1  0x000055555557e15e in Catch::TestInvokerAsFunction::invoke() const ()
#2  0x000055555557d7b1 in Catch::TestCase::invoke() const ()
#3  0x0000555555577f0a in Catch::RunContext::invokeActiveTestCase() ()
#4  0x0000555555577c59 in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) ()
#5  0x000055555557671b in Catch::RunContext::runTest(Catch::TestCase const&) ()
#6  0x00005555555797cc in Catch::(anonymous namespace)::TestGroup::execute() ()
#7  0x000055555557ab49 in Catch::Session::runInternal() ()
#8  0x000055555557a853 in Catch::Session::run() ()
#9  0x00005555555b6195 in int Catch::Session::run<char>(int, char const* const*) ()
#10 0x000055555558fdf0 in main ()

ОК. Итак, SIGSEGV указывает, что мы пытались читать / записывать в память, к которой у процесса нет доступа. Если в Flop.hpp я вместо этого скажу int tiles_[10][10][10], то все работает нормально. Таким образом, установка tiles_ на больший размер как-то резервирует часть памяти, к которой нет доступа? Я новичок в C ++ (и, следовательно, плохо знаком с тем, что на самом деле думает о том, что происходит на компьютере, когда я что-то программирую), поэтому поправьте меня, если я ошибаюсь, но int tiles_[200][200][200] не должно занимать намного больше 32 МБ памяти, верно ?

Итак, у меня есть пара вопросов:

  • Почему это вызывает ошибку сегментации?
  • Как я могу использовать gdb, чтобы получить меня обидеть строку кода? Не упрощенная версия этого кода - всего несколько сотен строк. К счастью, моя проблема была в начале определения класса, но комментирование всего и (кропотливо) посткомментирование строки все еще занимало время, и это то, что gdb должно было предотвратить!

1 Ответ

3 голосов
/ 19 марта 2020

Размер массива

int tiles_[200][200][200];

составляет около 30 МБ, при условии sizeof(int) == 4.

Это больше, чем типичные пределы размера стека, и поэтому вы будете писать вне стека пространство, которое вам разрешено использовать при создании автоматической переменной c этого типа с

Flop flop;

Объем стека, используемого программой, обычно ограничен несколькими МБ или около того, в зависимости от ОС и настройки.

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

Не сохраняйте большие объекты напрямую как члены или с автоматическим c сроком хранения (т.е. в стеке). Вместо этого выделяйте большие объекты динамически (т. Е. В свободном хранилище / куче) через косвенное указание.

Самый простой способ сделать это - использовать std::vector вместо встроенных массивов. Это также, как правило, предпочтительнее встроенных массивов и должно быть вашим выбором по умолчанию, если вам нужно хранить несколько объектов одного типа.

В определенных c случаях, когда невыделенные массивы размером во время компиляции std::array превосходит встроенные массивы. Таким образом, вы можете вообще не использовать встроенные массивы.

В качестве альтернативы std::unique_ptr<...> позволяет вам оборачивать объекты любого типа (в том числе встроенные массивы) в динамически распределяемые косвенные ссылки.

...