Для этого используются приемы контрактного программирования, когда на входе и выходе процедур ставятся проверки, нарушение которых останавливает выполнение этой процедуры.
ASSERT(x < 16, 21);При этом вызывается обработчик аварийных остановок, который решает что дальше делать. Чаще всего он показывает развернутый отчёт о произошедшей аварийной остановке.
Тогда на этапе тестирования компонентов большая часть такого рода ошибок проявляется.
Как поступить на этапе эксплуатации программы, это уже вопрос архитектуры проекта и типа проекта.
На ПК обычно пользователь делает скриншот сообщение и отправляет по почте. Но можно сделать, чтобы отправлялся сам через Интернет.
Микроконтроллер обычно уходит в перезагрузку и начинает выполнение программы с начала.
При этом в специальных переменных сохраняется код аварийной остановки, начало модуля и положение в исходном тексте.
Вот писал заметку про обработку аварийных остановок в STM32:
http://wiki.oberon.org/ob/o7/debug