Перейти к содержанию
    

Поделитесь опытом - как делаете обработку ошибок

6 минут назад, aaarrr сказал:

где прекрасно сохранится дамп.

В таком случае разве что самым значимым будет содержимое статусных регистров fault CPU.

Содержимое регистров РОН - на втором месте.

А на указатель стека уже ориентироваться, в общем-то, нельзя. А ведь по нему можно было бы развернуть какой-никакой стек вызовов. А это было бы куда интереснее, чем первый пункт.
 

4 минуты назад, makc сказал:

Есть разные варианты, типа сначала прерывание, а потом ресет, если не отпустило. Зависит от модели МК, но в целом это по-моему есть приблизительно везде.

Ну вот, например, STM32🙂 Вроде только WWDG имеет прерывание. Но программно настраиваемый вачдог - такое себе)) Около МК всегда (у меня) имеется аппаратный.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 часов назад, Arlleex сказал:

или вовсе улетели по адресу, который допустим для исполнения прошивки (MPU не увидит в этом проблем), но в конечном счете все равно улетит в какой-нибудь хэндлер или lockup. Или банально улетит по адресу куска памяти, где разблокируется Flash и что-то перепрограммирует (такой кусок кода имеется практически в любом проекте). Или сразу улетит по адресу NVIC_SysytemReset().

Что-то странное рассказываете... При неконтролируемой передаче управления (например - вследствие разрушения стека) вероятность попасть в точку старта прошивки (это всего от 0 до нескольких байт после точки) - близка к вероятности выигрыша в лотерею суперприза. Просто достаточно посчитать - какова вероятность случайного выпадения заданного адреса в случайном 32-битном значении?

"Разблокируется флешь" - вообще равно нулю. Так как в используемых мной сейчас XMC4xxx модифицировать внутреннюю флешь может только код, находящийся в ОЗУ. И копирует его туда бутлоадер после множества проверок и программирований MPU.

 

Вероятность попасть в какой-то удачный адрес при неконтролируемой передаче управления - крайне мала. В 99% будет попадание в такое место, при котором будет сгенерирован HF (а соответственно - и последующий trap) или по недопустимой команде или по недопустимому обращению к памяти. Пишу это не от фонаря, а потому что использую это давно и много раз помогало. В том числе и при улётах неизвестно куда. Да, fault сработает не сразу, но по содержимому регистров CPU или дампов стеков как правило - можно быстро найти место начальной проблемы.

5 часов назад, Arlleex сказал:

Аппаратный вачдог заводится напрямую на RESET МК. Да и внутренние вачдоги дергают ресет. Последний раз видел прерывание от вачдога в AVR, что еще 10 лет назад показалось мне не очень надежным 😃

Срабатывание сторожевика - это ещё один источник возбуждения trap(). И такой случай должен быть расследован программистом при отладке прошивки в обязательном порядке.

4 часа назад, Arlleex сказал:

Но программно настраиваемый вачдог - такое себе)) Около МК всегда (у меня) имеется аппаратный.

Наличие внешнего WDT нисколько не мешает использовать и внутренний. Почти во всех проектах имею внешний WDT. И везде он у меня работает совместно с внутренним WDT.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 hours ago, Arlleex said:

В таком случае разве что самым значимым будет содержимое статусных регистров fault CPU.

Наиболее значим адрес сбоя, затем содержимое РОН, затем всё остальное.

 

8 hours ago, Arlleex said:

А на указатель стека уже ориентироваться, в общем-то, нельзя.

Почему нельзя?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, aaarrr сказал:

Почему нельзя?

Не прав, указатель будет в порядке. А вот сам стек уже может быть вполне затерт - и что там смотреть?
 

5 часов назад, jcxz сказал:

Просто достаточно посчитать - какова вероятность случайного выпадения заданного адреса в случайном 32-битном значении?

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

Цитата

"Разблокируется флешь" - вообще равно нулю. Так как в используемых мной сейчас XMC4xxx модифицировать внутреннюю флешь может только код, находящийся в ОЗУ. И копирует его туда бутлоадер после множества проверок и программирований MPU.

Хорошо, что Ваш МК имеет такую возможность. А в том же STM32 работать с флешкой можно прямо из того же кода во флеш.
 

Цитата

Да, fault сработает не сразу, но по содержимому регистров CPU или дампов стеков как правило - можно быстро найти место начальной проблемы

Быстро? Не верю. Если мы говорим об очень частых проявлениях бага, то не составит труда под отладчиком с вероятностью 99.9% выловить этот баг на столе, без дампов и т.д. Если же девайс сбоит при непонятных обстоятельствах за тридевять земель от дома, то взгляд на дамп регистров ничего не скажет с очень большой вероятностью. Мне штатный перехватчик переполнения стека в FreeRTOS гораздо чаще помогал, чем дампы. И еще: чтобы иметь хоть какой-то адекватный шанс что-то понять из дампа регистров, нужно изначально обладать вполне объемным контекстом и предысторией: что в процессоре задействовано, что не задействовано, и т.д., чтобы в голове уже представлять потенциальный сценарий развития и проявления бага.

Я однажды неделю отлавливал всяческими дампами и перехватчиками баг в Cortex-A9 на цинке, который возникал от силы пару раз в день - и это еще очень повезло, можно сказать. И да - я улетал в DataAbortHandler. Ну отматывал я указатель стека, смотрел что вызывалось - и каждый раз - разное. И так можно было бы сидеть искать до бесконечности. Не обладая минимальными сведениями проблему не сыскать. А я помнил, что тогдашний порт FreeRTOS-а был довольно древним, и что возможно индусы как-то криво запилили переключатель контекста. Полез смотреть - бинго - FPU-контекст вообще не сохранялся, а он вообще-то задействуется библиотечными функциями memcpy() и т.д. Это невозможно понять взглянув на регистры. Из них максимум будет понятно - что что-то где-то нагадило в память и не более.

Еще один пример. В МК поднимался SPI и его задача была пару раз в секунду запрашивать со слейва данные. В функцию записи передавался массив куда писать и длина. Смотрю - раз в минуту стабильно срывает крышу и на CLK сплошной непрерывный поток данных - а девайс "подвис" (не заморачивался я с "правильным" SPI на прерываниях или DMA - мне надо было провериться на макетке). Сети fault-ов МК - пустые. Ставлю оптимизацию повыше - все работает. -О0 - не работает. "WTF? Обычно же наоборот!" - думал я. Ну полез я отладчиком смотреть регистры - все в порядке - на входе в функцию все ок. А дальше происходит интересное: кейловский 5 компилятор (ныне устаревший) иногда (от незначительных изменений в коде) в спонтанных местах асм-листинга этой функции SPI_Read() вставлял инструкцию "отката" указателя стека. Т.е. на входе в функцию резервировал себе память под локальные переменные, а далее где-то в середине функции - откатывался обратно! И поэтому перед циклом SPI-транзакций len в for(u32 i = 0; i < len; ++i) брался из памяти "сверху" стека, а там периодически лежало что-то большое, из-за чего вместо цикла на 10 транзакций он получал миллион. Позже эту проблему описали к следующей ревизии компилятора.

Случаев у меня было много разных и инетресных, когда я пялился в монитор (поняв уже проблему) и говоря "да какой нафиг дамп такое обнаружит?". Для себя решил, что регистры процессора нужно смотреть когда уже под отладчиком запускаешь девайс и ловишь баг, а не писать дампы на флешки. Они лишь подогревают надежду, но их результативность околонулевая.

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

Все это мое субъективное мнение на этот счет.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, Arlleex сказал:

Не прав, указатель будет в порядке. А вот сам стек уже может быть вполне затерт - и что там смотреть?
 

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

Хорошо - предположим перед ней 100 байт линейного кода. Достаточно? Причём такого, что даже нигде к памяти не обращается (иначе скорей всего случился бы вылет по MPU или HF из-за обращения по недопустимым адресам) - что само по себе очень редкий случай.

Получаем: 100/2^32 = ~2.3e-8 - вероятность попадания в такой регион при переходе по случайному адресу. С такой вероятностью можно считать это событие вообще невероятным.  :unknw:

3 часа назад, Arlleex сказал:

Быстро? Не верю. Если мы говорим об очень частых проявлениях бага, то не составит труда под отладчиком с вероятностью 99.9% выловить этот баг на столе, без дампов и т.д.

Да ладно? Предположим - работаете вы не один, а есть другие люди, тестирующие ваш девайс или работающие над проектом. В девайсе несколько десятков настроек + довольно богатый функционал. Далее к вам обращается тестировщик: вот тестировал вчера вечером девайс, вдруг он завис (или перегрузился; а скорее даже скажет: "Контроллер крутил мотор и вдруг мотор остановился (девайс - инвертор для PMSM-моторов" (так как тестировщик даже не заметил, что девайс просто молча перезагрузился)). Спрашиваете его: "Какую последовательность действий выполнял перед этим событием?" В ответ - "Не помню, много что делал, параллельно ещё меня отвлекали". Т.е. - воспроизвести баг вы не можете. А если ещё девайс - мощный мотор на стенде, в несколько сотен кВт, который на своём столе просто не запустите.  Как собираетесь искать баг? :unknw: 

А в моём случае я просто попрошу его показать содержимое ловушки. И с большой вероятностью сразу найду причину.

3 часа назад, Arlleex сказал:

Если же девайс сбоит при непонятных обстоятельствах за тридевять земель от дома, то взгляд на дамп регистров ничего не скажет с очень большой вероятностью.

Опять не соглашусь. Как раз в последнее время много приходится отлаживать девайсов "за тридевять земель". В 9-и случаев из 10-и благодяря системе ловушек, баг нахожу сразу, за несколько секунд. Большинство из таких багов:

1. Впаяли не тот чип (например - не ту SPI-флешку). Драйвер SPI-флешь при старте читает ID флешки и падает в ловушку. Или читает регистры защиты флешь - и также падает в ловушку из-за установленной защиты, которой не должно быть. Причина определяется сразу по дампу ловушки.

2. Непропай - опять ошибка инициализации какого-либо драйвера и падение в ловушку. Опять определяется сразу по дампу ловушки.

3. Подали не то питание или что-то не подключили или подключили неправильно - опять ловушка. И опять причина находится сразу.

4. Сильный перегрев платы - уходит частота генератора на плате - падаем в ловушку. Причина находится быстро.

... и таких багов - большинство. И реально ловушки помогают. Так как проверками у меня обложены все потенциально опасные места.

3 часа назад, Arlleex сказал:

Еще один пример. В МК поднимался SPI и его задача была пару раз в секунду запрашивать со слейва данные.

Я не понимаю вашу логику. Вы пытаетесь доказать, что моя система ловушек не пригодна по отдельным, частным случаям, когда она бы не сработала. Это примерно то же самое, что говорить: "Вот я вчера шёл не заметил кочку и споткнулся, упал. Получается - глаза мне не помогли её увидеть. Значит глаза не нужны". Это если следовать вашей логике. Странная логика....  :wacko2:

3 часа назад, Arlleex сказал:

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

Ок - раскомментировали вы отладочный вывод. Время выполнения кода изменилось, а также изменилось распредление памяти - проявление бага пропало. И будете искать баг до морковкина заговения. Так что такой способ - так себе способ.

3 часа назад, Arlleex сказал:

Все это мое субъективное мнение на этот счет.

Хорошо, я понял - вы почему-то против моего способа. Тогда предложите ваше решение.

Вот случай (исходя из вопроса ТС): Вызываете сервис ОС, он возвращает вам ошибку (например какой-то ресурс недоступен). Ваши дествия? Повиснуть? Перегрузиться? Что будете делать. Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага). Но что будете делать вы?

Или другой случай: У меня есть множество функций с assert-ами (проверками входных аргументов на входе). Если проверка не проходит - trap(TRAP_ASSERT, регион_assert-а, ...). И сразу вижу причину и место. Каково ваше решение в этом случае (без механизма ловушек)? Повиснуть? Перегрузиться?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, jcxz сказал:

А в моём случае я просто попрошу его показать содержимое ловушки. И с большой вероятностью сразу найду причину.

Цитата

В 9-и случаев из 10-и благодяря системе ловушек, баг нахожу сразу, за несколько секунд. Большинство из таких багов:

Цитата

Хорошо, я понял - вы почему-то против моего способа. Тогда предложите ваше решение.

Цитата

trap(TRAP_OS, ...)

Цитата

trap(TRAP_ASSERT, регион_assert-а, ...)

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

Цитата

1. Впаяли не тот чип (например - не ту SPI-флешку).

содержимое процессорных регистров мне ни о чем не скажет совершенно. А вот выведенный в консоль или записанный в лог флешки текст расшифрованной ловушки в виде

...
00:10:22:765 [ ERROR ] Error SPI-Flash ID code: 0x7545
...

еще как скажет.
 

Цитата

Или другой случай: У меня есть множество функций с assert-ами (проверками входных аргументов на входе). Если проверка не проходит - trap(TRAP_ASSERT, регион_assert-а, ...). И сразу вижу причину и место. Каково ваше решение в этом случае (без механизма ловушек)? Повиснуть? Перегрузиться?

А Вы, по-Вашему, не повиснете? Не перезагрузитесь? Вы не отвечаете на вопрос, что будете делать дальше с точки зрения выполнения ПО.
 

Цитата

Ваши дествия? Повиснуть? Перегрузиться? Что будете делать. Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага). Но что будете делать вы?

Мои действия: выдать лог или записать его на флешку если девайс работал автономно, а потом да - либо перезагрузиться, либо повиснуть, ожидая действий - все ситуативно.

Вы тоже повиснете или перезагрузитесь - потому что

Цитата

Я просто вызову trap(TRAP_OS, ...) (и по выводу сразу найду место бага)

это не ответ на вопрос

Цитата

Ваши дествия? Повиснуть? Перегрузиться? Что будете делать.

То, что Вы дальше будете разбираться, что произошло с девайсом - не отвечает на вопрос - что будет делать программа. А программа, как Вы ранее сами писали, войдет в режим циклического вывода дампа регистров в консоль. Т.е. с точки зрения "боевого" режима изделия - оно повиснет. Вот и все.

Я не говорю что вообще ничего ловить не надо - я говорю, что представленный пример дампа содержимого регистров - в реальной моей практике ни о чем не говорил. Ну - половина регистров хранит мусор, другая - тоже какой-то мусор. И что дальше с этим делать?

Те примеры ошибок (не правильная флешка припаяна, например) - не есть критические ошибки, которые невозможно предусмотреть обработкой в ПО. Считали кривой ID? Ок - рапортуем лампочкой TOTAL ERROR встроенной системой контроля и отдаем на завод. Драйвер должен поджечь эту лампочку или выдать лог - как угодно на вкус и цвет. Это все можно предусмотреть в коде драйвера. С такими ситуациями вообще проблем нет - за них даже не интересно. Интересно как раз - когда проц падает по каким-то совсем не внятным причинам - я выше приводил примеры. Вот словил я в цинке исключение "отказ данных", вот вывел содержимое всех процессорных регистров - даже стек размотал, чтобы вызовы посмотреть - а толку нет - каждый раз баг в новом месте. Как ловить такие ошибки ловушками?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за информацию, оба подхода очень интересны!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

38 минут назад, Arlleex сказал:

содержимое процессорных регистров мне ни о чем не скажет совершенно. А вот выведенный в консоль или записанный в лог флешки текст расшифрованной ловушки в виде

...
00:10:22:765 [ ERROR ] Error SPI-Flash ID code: 0x7545
...

еще как скажет.

Я вроде сразу в исходном посте ещё писал:

В 29.05.2024 в 13:34, jcxz сказал:

По логу виден номер trap (3) + набор аргументов данного номера (в квадратных скобках) + регистры + вершина стека.

Номер ловушки + набор аргументов (определяемый номером ловушки) как раз это и есть.

38 минут назад, Arlleex сказал:

А Вы, по-Вашему, не повиснете? Не перезагрузитесь? Вы не отвечаете на вопрос, что будете делать дальше с точки зрения выполнения ПО.

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

38 минут назад, Arlleex сказал:

Мои действия: выдать лог или записать его на флешку если девайс работал автономно, а потом да - либо перезагрузиться, либо повиснуть, ожидая действий - все ситуативно.

Ну т.е. - сделаете примерно то же самое, но только хуже (так как без предварительного реинита программы)? А почему тогда критикуете мой способ?

38 минут назад, Arlleex сказал:

То, что Вы дальше будете разбираться, что произошло с девайсом - не отвечает на вопрос - что будет делать программа. А программа, как Вы ранее сами писали, войдет в режим циклического вывода дампа регистров в консоль. Т.е. с точки зрения "боевого" режима изделия - оно повиснет. Вот и все.

Это не повисание. Это режим ловушки. Повиснуть - это значит молча и без какой-либо индикации войти в непонятное состояние устройства. Которое невозможно определить по внешним признакам. Вот это и есть повисание.

А режим ловушки штатный и известный пользователям (на время отладки). Даёт возможность надёжно обнаружить ошибку (а не замести её под ковёр, перегрузившись) и дать возможность программисту устранить её.

38 минут назад, Arlleex сказал:

Я не говорю что вообще ничего ловить не надо - я говорю, что представленный пример дампа содержимого регистров - в реальной моей практике ни о чем не говорил. Ну - половина регистров хранит мусор, другая - тоже какой-то мусор. И что дальше с этим делать?

В вашем может ничего не говорил, а я по этому дампу неоднократно находил причину. Так как можно увидеть адрес, увидеть содержимое регистров. Этого чаще всего уже достаточно для нахождения причины. Не всегда конечно, но в большинстве случаев.

Если следовать вашей логике, то непонятно зачем в отладчиках выводят содержимое регистров? или памяти, стека? Ведь там тоже - половина - мусор.

Чтобы понять где мусор, а где полезные данные - на то у программиста есть голова.

38 минут назад, Arlleex сказал:

Интересно как раз - когда проц падает по каким-то совсем не внятным причинам - я выше приводил примеры. Вот словил я в цинке исключение "отказ данных", вот вывел содержимое всех процессорных регистров - даже стек размотал, чтобы вызовы посмотреть - а толку нет - каждый раз баг в новом месте. Как ловить такие ошибки ловушками?

Вы бы исходный вопрос темы прочитали, что-ли. Где там про ваш цинк что-то написано??  :wacko2:

 

Я предложил свой способ для решения вопроса ТС. Если вы с ним не согласны - предложите свой. Но к чему тут проблемы цинка (причём только определённые) - совсем не ясно. И всегда, для любого способа борьбы с багами, можно придумать такой сценарий, который не будет работать. Предложите свой способ, а мы придумаем кучу ситуаций, когда он не сработает.  :biggrin:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо за интересную информацию!

У меня вопрос к jcxz - как я понял у Вас вызывается макрос по нештатной ситуации, скажем

if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
        {
            MY_TRAP( 3 )
        }

Этот макрос (или функция) сливает все регистры и стек с указателем в лог в fram или в UART и рестатует МК?

Затем Вы "на базе" анализируете этот лог. Верно?
Только я не понял, как это технически делается дебаггером, если баг не воспроизводится каждый раз?
Доходите до этой функции дебаггером на работающем девайсе, и сравниваете содержимое стека и регистров с тем что в дампе?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 минут назад, Mty сказал:

Спасибо за информацию, оба подхода очень интересны, прямо супер!

У меня вопрос к jcxz - как я понял вызывается макрос по нештатной ситуации, скажем

if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
        {
            MY_TRAP( 3 )
        }

Этот макрос (или функция) сливает все регистры и стек с указателем в лог в fram или в UART и рестатует МК?

Не совсем. Если trap генерируется программный (например: в результате проверки каких-то переменных в программе обнаружено их недопустимое состояние), то делается вызов trap() (это не макрос и не функция в обычном виде, это возбуждение прерывания SVC, оформленное в виде функции (IAR умеет такое)). Внутри ISR SVC защёлкивается в ОЗУ дамп = id_trap+аргументы+регистры+стек, потом делается реинит всей периферии и CPU с активацией только нужной для trap-режима. МК можно рестартовать или не рестартовать. Главное - отключить все работающие DMA и перевести все интерфейсы работающие с внешней периферией в дефолтное выключенное состояние (выключить питание моторов, отключить Ethernet, USB, CAN и т.п.).

Если trap не программный, а вход в ловушку идёт из ISR HF или ISR MPU или ISR шинных ошибок или ошибок чётности памяти (и т.п.), которые сами являются по факту событиями ошибки, то симулируется как будто была получена SVC (повышение приоритета прерывания до максимума и т.д.), и содержимое регистров и стека берётся таким, каким оно было до входа в этот ISR (HF, MPU, ...) Чтобы причина trap указывала на причину, вызвавшую исходный ISR, а не на код внутри ISR. Также для HF декодируется причина fault-а (по соответствующим регистрам), и эта инфа передаётся как номер и аргументы trap().

Для таких ловушек у меня есть целый класс номеров:

#define TRAP_EXC_HARD           0x100 //исключение Hard Fault (неуточнённое)                                                                   
#define TRAP_EXC_HARD_VECTTBL   0x101 //ошибка при попытке считать содержимое таблицы векторов прерываний                                      
#define TRAP_EXC_HARD_FORCED    0x102 //произошла эскалациЯ прерываниЯ до HardFault (исходный fault неизвестен)                                
#define TRAP_EXC_MPU            0x110 //MEMORY MANAGER FAULT (неуточнённое)                                                                    
#define TRAP_EXC_MPU_IERR       0x111 //MM FAULT: нарушение прав доступа к коду                                                                
#define TRAP_EXC_MPU_DERR       0x112 //MM FAULT: нарушение прав доступа к данным                                                              
#define TRAP_EXC_MPU_MUSTKE     0x113 //MM FAULT: при выходе из обработчика прерываниЯ возникло порождённое прерывание MemFault                
#define TRAP_EXC_MPU_MSTKE      0x114 //MM FAULT: при входе в обработчик прерываниЯ возникло порождённое прерывание MemFault                   
#define TRAP_EXC_MPU_LSPE       0x115 //MM FAULT: при отложенном сохранении состоЯниЯ FPU возникла ошибка                                      
#define TRAP_EXC_BUS            0x120 //BUS FAULT (неуточнённое)                                                                               
#define TRAP_EXC_BUS_IBUS       0x121 //BUS FAULT: ошибка при попытке выборки инструкции                                                       
#define TRAP_EXC_BUS_PRECISE    0x122 //BUS FAULT: точнаЯ ошибка при обращении к данным                                                        
#define TRAP_EXC_BUS_IMPRECISE  0x123 //BUS FAULT: неточнаЯ ошибка при обращении к данным                                                      
#define TRAP_EXC_BUS_USTK       0x124 //BUS FAULT: при попытке выхода из обработчика прерываниЯ                                                
#define TRAP_EXC_BUS_STK        0x125 //BUS FAULT: при попытке входа в обработчик прерываниЯ                                                   
#define TRAP_EXC_BUS_LSPE       0x126 //BUS FAULT: при отложенном сохранении состоЯниЯ FPU                                                     
#define TRAP_EXC_USAGE          0x130 //USAGE FAULT (неуточнённое)                                                                             
#define TRAP_EXC_USAGE_DIV0     0x131 //USAGE FAULT: попытка делениЯ на 0                                                                      
#define TRAP_EXC_USAGE_UNALIGN  0x132 //USAGE FAULT: попытка невыровненного доступа к памЯти                                                   
#define TRAP_EXC_USAGE_NOCP     0x133 //USAGE FAULT: попытка доступа к отсутствующему или отключенному сопроцессору                            
#define TRAP_EXC_USAGE_INVPC    0x134 //USAGE FAULT: ошибочное значение в EXC_RETURN                                                           
#define TRAP_EXC_USAGE_INVSTAT  0x135 //USAGE FAULT: попытка выполнениЯ инструкции при неверном значении бита T или полЯ IT регистра EPSR      
#define TRAP_EXC_USAGE_UNDEF    0x136 //USAGE FAULT: попытка выполнить неизвестную или привилегированную инструкцию                            

у некоторых ещё есть аргументы.

Вообще типичное содержимое trap.h из текущего проекта:

Спойлер
#define TRAP_NONE               0x00
#define TRAP_MISC               0x01
#define TRAP_INTERNAL           0x02
#define TRAP_ASSERT             0x03
#define TRAP_OVERBUF            0x04
#define TRAP_OS                 0x05
#define TRAP_UART               0x09
#define TRAP_PRG_IMAGE          0x0A  //размер образа программы больше размера, контролируемого CRC в IAR. Либо невернаЯ CRC образа
#define TRAP_SERV               0x0B
#define TRAP_ISR_NESTED         0x0C  //нарушение уровнЯ вложенности ISR-ов
#define TRAP_TOUT_ACT           0x0D  //таймаут обмена с периферией
#define TRAP_SETRATE            0x0E  //ошибка установки скорости
#define TRAP_OS_HOOK            0x0F  //ошибка в хуках ОС
#define TRAP_FCE                0x10  //ошибка Flexible CRC Engine
#define TRAP_DMA                0x11  //DMA bus error is pending
#define TRAP_DMA_NESTED         0x12  //повторное разрешение/запрет уже разрешённого/запрещённого DMA-канала
#define TRAP_ERU_NESTED         0x13  //превышение кол-ва включений или кол-ва выключений ERU
#define TRAP_TERMINAL           0x14
#define TRAP_SIMCOM             0x15
#define TRAP_GYRO               0x16  //ошибка службы гироскопа
#define TRAP_RF_ISR_OVER        0x17  //переполнение FIFO ISR RF-приёмника
#define TRAP_ADC_OVER           0x18  //не успели обработаться предыдущие данные slow АЦП когда уже поступили новые
#define TRAP_I2C_BUSY_SCL       0x20  //линия SCL залипла в состоянии "0"
#define TRAP_I2C_BUSY_SDA       0x21  //линия SDA залипла в состоянии "0"
#define TRAP_I2C_ERROR          0x22
#define TRAP_I2C_RXOVER         0x23
#define TRAP_I2C_USIC           0x24
#define TRAP_HIB                0x25  //ошибка в Hibernation-драйвере
#define TRAP_PBA0               0x26  //ошибка Peripheral Bridge0
#define TRAP_PBA1               0x27  //ошибка Peripheral Bridge1
#define TRAP_PARITY             0x28  //ошибка чётности памяти (аргумент - биткарта регионов ошибки)
#define TRAP_NMI                0x29  //NMI, причина неизвестна
#define TRAP_CCU_ON_NESTED      0x2B  //повторное разрешение уже разрешённого CCU-слайса
#define TRAP_CCU_OFF_NESTED     0x2C  //повторный запрет уже запрещённого CCU-слайса
#define TRAP_SPI                0x30
#define TRAP_DFLASH             0x31
#define TRAP_USB                0x32
#define TRAP_OSC                0x33
#define TRAP_CEDIT              0x34

#define TRAP_EXC_HARD           0x100 //исключение Hard Fault (неуточнённое)
#define TRAP_EXC_HARD_VECTTBL   0x101 //ошибка при попытке считать содержимое таблицы векторов прерываний
#define TRAP_EXC_HARD_FORCED    0x102 //произошла эскалациЯ прерываниЯ до HardFault (исходный fault неизвестен)
#define TRAP_EXC_MPU            0x110 //MEMORY MANAGER FAULT (неуточнённое)
#define TRAP_EXC_MPU_IERR       0x111 //MM FAULT: нарушение прав доступа к коду
#define TRAP_EXC_MPU_DERR       0x112 //MM FAULT: нарушение прав доступа к данным
#define TRAP_EXC_MPU_MUSTKE     0x113 //MM FAULT: при выходе из обработчика прерываниЯ возникло порождённое прерывание MemFault
#define TRAP_EXC_MPU_MSTKE      0x114 //MM FAULT: при входе в обработчик прерываниЯ возникло порождённое прерывание MemFault
#define TRAP_EXC_MPU_LSPE       0x115 //MM FAULT: при отложенном сохранении состоЯниЯ FPU возникла ошибка
#define TRAP_EXC_BUS            0x120 //BUS FAULT (неуточнённое)
#define TRAP_EXC_BUS_IBUS       0x121 //BUS FAULT: ошибка при попытке выборки инструкции
#define TRAP_EXC_BUS_PRECISE    0x122 //BUS FAULT: точнаЯ ошибка при обращении к данным
#define TRAP_EXC_BUS_IMPRECISE  0x123 //BUS FAULT: неточнаЯ ошибка при обращении к данным
#define TRAP_EXC_BUS_USTK       0x124 //BUS FAULT: при попытке выхода из обработчика прерываниЯ
#define TRAP_EXC_BUS_STK        0x125 //BUS FAULT: при попытке входа в обработчик прерываниЯ
#define TRAP_EXC_BUS_LSPE       0x126 //BUS FAULT: при отложенном сохранении состоЯниЯ FPU
#define TRAP_EXC_USAGE          0x130 //USAGE FAULT (неуточнённое)
#define TRAP_EXC_USAGE_DIV0     0x131 //USAGE FAULT: попытка делениЯ на 0
#define TRAP_EXC_USAGE_UNALIGN  0x132 //USAGE FAULT: попытка невыровненного доступа к памЯти
#define TRAP_EXC_USAGE_NOCP     0x133 //USAGE FAULT: попытка доступа к отсутствующему или отключенному сопроцессору
#define TRAP_EXC_USAGE_INVPC    0x134 //USAGE FAULT: ошибочное значение в EXC_RETURN
#define TRAP_EXC_USAGE_INVSTAT  0x135 //USAGE FAULT: попытка выполнениЯ инструкции при неверном значении бита T или полЯ IT регистра EPSR
#define TRAP_EXC_USAGE_UNDEF    0x136 //USAGE FAULT: попытка выполнить неизвестную или привилегированную инструкцию

#define TRAP_UNEXPECTED_INT     0x140 //неожиданное прерывание
#define TRAP_UNEXPECTED_DMA     0x141 //неожиданное DMA-событие
#define TRAP_INTDIS_OVER        0x142 //слишком длинный запрет прерываниЯ
#define TRAP_SVC_UNKNOWN        0x143 //неизвестная функция SVC
#define TRAP_VIC                0x144 //недопустимаЯ таблица приоритетов VIC

//регионы ловушек
#define TRAPR_ANY               0
#define TRAPR_DFLASH            1
#define TRAPR_LOG               2
#define TRAPR_TERM              3
#define TRAPR_PWM               4
#define TRAPR_SIMCOM            5
#define TRAPR_I2C               6
#define TRAPR_GYRO              7
#define TRAPR_DTE               8     //отладочная консоль
#define TRAPR_HIB               9     //Hibernate-драйвер
#define TRAPR_TIME              10    //Служба Времени
#define TRAPR_MOTOR             11    //Служба управления мотором
#define TRAPR_CONFIG            12
#define TRAPR_USB               13    //USB
#define TRAPR_SENSOR            14    //
#define TRAPR_OSC               15

 

 

14 минут назад, Mty сказал:

Только я не понял, как это технически делается дебаггером если баг не воспроизводится каждый раз?
Доходите до этой функции дебаггером на работающем девайсе, и сравниваете содержимое стека и регистров с тем что в дампе?

А зачем его воспроизводить? Адрес есть, значит можно встать на этот адрес и проанализировать, что там не так. А дальше действовать по обстоятельствам.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

35 минут назад, jcxz сказал:

Не совсем. Если trap генерируется программный (например: в результате проверки каких-то переменных в программе обнаружено их недопустимое состояние), то делается вызов trap() (это не макрос и не функция в обычном виде, это возбуждение прерывания SVC, оформленное в виде функции (IAR умеет такое)).

Пока что это выглядит как обычный LOG() только вместо вменяемого сообщения сохраняется константа, а сообщение надо глазами читать в комментариях к этой константе в  trap.h - файле. Удовольствие по части объяснения произошедшего в программе - то еще.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

46 минут назад, artemkad сказал:

Пока что это выглядит как обычный LOG() только вместо вменяемого сообщения сохраняется константа, а сообщение надо глазами читать в комментариях к этой константе в  trap.h - файле.

Ну если вам нравится текстом - кто ж вам мешает? А я буду делать как удобнее мне и пользователям моих устройств.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

Лог хранится в RAM в области которая не обнуляется на старте. В любой момент его можно прочитать или очистить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

6 hours ago, jcxz said:

А зачем его воспроизводить? Адрес есть, значит можно встать на этот адрес и проанализировать, что там не так. А дальше действовать по обстоятельствам.

А адрес функции вызвавшей прерывание (PC) лежит последний или предпоследний в стеке?

3 hours ago, amaora said:

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

Лог хранится в RAM в области которая не обнуляется на старте. В любой момент его можно прочитать или очистить.

Благодарю, интересный вариант.  А эту область объявляете что то типа static char массив в C?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, Mty said:

А эту область объявляете что то типа static char массив в C?

В отдельную .noinit секцию, которая никак не обрабатывается в startup коде.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...