Я уверен, что многие из вас испытали это. Иногда при отладке сбоя вы добавляете строку трассировки, и вуаля сбой исчезает. Обычно это указывает на повреждение памяти, и вы ищете переполнения буфера и т. д. вокруг кода. Чтобы быть более конкретным, то же самое происходит даже для однопоточных приложений.
Как линия трассировки предотвращает сбой?
Строка трассировки - это фрагмент кода, поэтому следует перейти к сегменту кода, который доступен только для чтения, верно?
Это не должно влиять на локальный стек выполнения? Как это влияет на смещения указателя, чтобы перезаписать какую-то другую часть памяти, которая не проявляет себя (все еще плохо).
Ответ 1 · рейтинг 1 · Mark Bessey
Несколько человек уже упомянули об изменении времени. Даже если ваш код однопоточный, вы можете вызывать API, который либо запускает потоки от вашего имени, либо реализует какую-то другую форму асинхронного взаимодействия.
Что касается этого утверждения:
Строка трассировки - это часть кода, поэтому она должна идти в сегмент кода.
Это зависит от того, что на самом деле делает ваш код отслеживания. Даже что-то такое простое, как:
write(0, "Hello, World!\n", 14);
Как минимум добавит дополнительное хранилище данных для константной строки, что изменит расположение других константных данных и, возможно, изменит начало других сегментов памяти, что также повлияет на расположение кода и/или кучи. Если это первая ссылка на эту функцию, она добавит запись о перемещении в объектный файл, что может вызвать другие сдвиги в структуре памяти.
Например, более сложный вызов трассировки, который вызывает fwrite или printf, почти наверняка приведет к выделению некоторой памяти для временных буферов и т. д.
Если вы можете воспроизвести ошибку (очевидно, с исходным кодом) при работе в отладчике, то вы сможете, по крайней мере, определить, какой код вызывает segfault. Если из-за отладчика ошибка также не возникает, вы можете рассмотреть возможность включения дампов ядра и отладки на основе дампа.
В противном случае вы можете попробовать запустить код с отладочным пакетом malloc (доступно несколько). Даже если "ошибка" не воспроизводится, вы, вероятно, обнаружите, что таким образом неправильно манипулируете указателем.
Последнее предложение: перекомпилируйте свой код со всеми предупреждениями, включенными в компиляторе, и серьезно рассмотрите все предупреждения, сгенерированные таким образом. Обратите внимание, что в gcc параметр -Wall не включает все предупреждения.
Ответ 2 · рейтинг 1 · R Samuel Klatchko
Во-первых, трассировка может влиять не только на сегмент кода, но и на сегмент данных, если трассировка включает какую-то строку. Это может полностью изменить структуру вашей памяти.
Если ваш segfault вызван тем, что вы разыменовываете некоторую случайную память, конкретное значение могло измениться из-за того, как данные были сдвинуты. Там, где старая структура памяти заставляла вас читать недопустимый адрес памяти, новая структура памяти теперь указывает на что-то действительное.
Одна из моих рекомендаций - прекратить отладку, добавив трассировки, и вместо этого использовать отладчик.
Ответ 3 · t0mm13b
См. здесь за мой ответ на другой ТАК вопрос. Я не уверен на 100%, в какой среде вы работаете, но я думаю, что это один из вариантов Unix... Ссылка, которую я дал, демонстрирует простой трюк для перехвата обработчика SIGSEGV путем подключения к обработчику signal и в любое время во время выполнения. кода обработчик перехватывается и выгружает трассировку стека в файл для чтения.
Надеюсь, это поможет вам в отслеживании проблемы, С уважением, Том.