Недавно, наш большой проект начал отказывать на необработанном делении на нуль. Никакой недавний код, кажется, не содержит вероятных элементов, таким образом, это могут быть новые наборы данных, влияющие на старый код. Проблемой является кодовая база, является довольно большим, и работа встроенного устройства без удобного доступа отладки (отладка сделана большим количеством printf () s по последовательной консоли, нет никакого gdb для устройства и даже если бы было, то двоичный файл, скомпилированный с отладочными символами, не соответствовал бы).
Самый жизнеспособный путь состоял бы в том, чтобы, вероятно, найти все операции деления (они являются относительно нечастыми), и проанализируйте код, окружающий каждого из них, чтобы видеть, оставили ли какую-либо из переменных делителя неосторожной.
Вопрос состоит тогда также в том, как найти все операции деления в большом (~200 файлов, некоторые большой) проектом C++, или, если у Вас есть лучшая идея, как определить местоположение ошибки, дайте им.
дополнительная информация: проект работает на встроенном ARM9, маленьком пользовательском дистрибутиве Linux, кросс-скомпилированном с Cygwin/Windows crosstools, IDE является Eclipse, но существует также Cygwin со всеми соответствующими положительными героями. Вещью является проект, является очень определенным для аппаратных средств, и катастрофические отказы происходят только при выполнении на полную мощность, все существенные взаимосвязанные активные модули. Ограниченный "режим отказа", где только скелет активны, не создает их.
Найти все подразделения не составит труда с помощью специального поиска grep . Вы можете легко отличить такое использование от других случаев использования символа /
и %
в C ++.
Кроме того, если вы знаете, что разделяете, вы можете глобально перегрузить оператор /
и %
, чтобы получить __ FILE __
и __ LINE __
информирующее утверждение. При использовании файла makefile несложно включить код настраиваемого оператора во все связанные файлы, не касаясь кода.
Вы должны использовать это как оправдание для инвестирования в улучшение отладочной способности вашего устройства - как для этой проблемы, так и для будущих проблем. Даже если вы не можете получить вживую отладку, вы должны иметь возможность найти способ генерировать и сохранять выключать ядро для отладки по послемерной отладке (немедленно указывать источник или любое необработанное исключение).
Думаю, самым прямым шагом будет попытка поймать необработанное исключение и сгенерировать дамп или информацию о стеке printf или что-то подобное.
Взгляните на этот вопрос или просто поищите в Google информацию, относящуюся к перехвату исключений в вашей конкретной среде.
Кстати, я думаю, что деление может произойти в результате вызова внешней библиотеки , поэтому не на 100% уверен, что вы найдете виновника только путем смазывания вашего кода.
Если я правильно помню, ARM9 не имеет аппаратного разделения, поэтому он будет реализован в вызов функции, который компилятор делает всякий раз, когда ему нужно выполнить деление.
Посмотрите, реализует ли ваш набор инструментов разделение на ноль так же, как набор инструментов ARM (вероятно, он делает что-то похожее). Если это так, вы можете установить обработчик, который вызывается при возникновении проблемы, и вы можете printf ()
регистрировать и стек, чтобы вы могли определить, где возникает проблема. Возможная аналогичная альтернатива - ваш небольшой дистрибутив Linux подает сигнал, который вы можете уловить.
Я не уверен, как вы получаете информацию о том, что происходит деление на ноль, но если это из-за того, что среда выполнения выдает сообщение об этом, у вас всегда есть возможность узнать, где это обрабатывается. во время выполнения и заменив его вашим собственным более информативным сообщением. Однако я бы предположил, что есть более «продуманный» способ запустить ваш код (обработчик сигналов или техника ARM).
Обращайтесь с исключением.
Обычно исключение будет вручено структуру, содержащую адрес, который вызвал исключение и другую информацию. Возможно, вам придется познакомиться с таблицей данных микроконтроллера или Руководство по RTOS.
PC-Lint может помочь, это похоже на FindBugs для C ++. Это коммерческий продукт, но есть гарантия возврата 30 денег.
Единственный способ найти эти условия - обычный:
Используйте TEMPS для GCC и найдите соответствующую сборку для разделения в сгенерированном файле .s. Если вам повезло, это будет что-то довольно отличительное, возможно, даже функция вызова. Если это функция, вы можете использовать слабую ссылку, чтобы переопределить его собственной проверенной версией. В противном случае распознавание подразделений в Собрании должна дать вам очень хорошую идею, где они находятся в коде C / C ++, и вы можете непосредственно их инструментировать.
Исключение уже имеет местоположение адреса на оскорбления по нулевому коду. CPU сохраняет содержимое регистров, когда происходит исключение, включая компьютер (счетчик программы). Ваша ОС должна пройти эту информацию (я предполагаю, что это то, что вы знаете, это разделить на ноль). Распечатайте адрес и посмотрите в свой код. Если вы можете распечатать трассировку стека, было бы еще проще решить.
Другой вариант будет проверять различия в вашем программное обеспечении управления версиями между последней рабочей версией и первой неработающей версией. Это должно дать вам ограниченный изменение, установленные в соответствии с которым искать проблему.
обычно вы можете изменить/переопределить обработчик исключений деления на ноль, если у вас есть доступ к рутинам обработчика исключений. В случае ARM, деление выполняется библиотечной рутиной. и есть механизмы информирования пользовательского кода, когда происходит деление на ноль.
см. http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka4061.html
Я бы предложил предусмотреть __rt_raise(), как сказано на странице выше.
__rt_raise(2,2) будет вызываться, когда процедура деления обнаружит деление на ноль. Таким образом, вы можете вывести регистр LR. и затем использовать addr2line для перекрестной ссылки на исходную строку