jcxz 245 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Единственное, для чего мне это понадобилось, это скинуть дамп регистров при возникновении Fault. Пришлось написать вставку на ASM для копирования регистров в память (структуру). Остальная работа на C. И где гарантия, что компилятор при очередной перекомпиляции, не разместит какие-то переменные в регистрах которые Вы хотели сохранить до вашей вставки? Такое надо делать целиком в asm-функциях. Напишите ISR fault-ов полностью на asm, там всё элементарно. Реально, по значениям LR, PC, R0-R4 в подавляющем большинстве случаев можно найти причину фэйла. Не знаю не знаю... У меня сохраняются все регистры + дамп стека. И то часто не хватает. У меня алгоритм такой: Исключение -> запись контекста исключения в RAM -> перезапуск -> сохранение контекста на внешнем носителе или передача на отладочный сервер -> возобновление работы У меня все fault-ы примерно так же обрабатываются, только кроме дампа регистров сохраняю ещё и дамп текущего стека. Отличия: Для release-сборки: всё так же, кроме передачи на отладочный сервер. Для debug-сборки: вместо перезапуска - переинициализация железа в минимальный дефолт-конфиг trap-режима и зацикливание в цикле периодического вывода инфы о событии в лог (UART). Чтобы не пропускать такие ошибки, а исправлять сразу. Этот-же механизм используется для обработки программных критических ошибок (через SVC). На удаленном оборудовании пару раз выручало. Всех подробностей не вспомню, но потребовалось Помогает очень часто в обнаружении редко проявляющихся ошибок. Когда тестируем сразу несколько десятков (а то и сотни) устройств в течение длительного времени (дни/недели непрерывной работы). С JTAG-ом тут не посидишь, а вот устройства защёлкнувшиеся в trap-цикле сразу видны. А потом - по регистрам-стеку ищем причину. И если такие ошибки проявились уже на объектах заказчика - тут другого выбора нет, очень помогает. Без дампа стека вообще мало когда эта инфа помогает. Типичная ситуация: срабатывание исключения MPU защиты памяти при попытке обращения к недопустимой памяти изнутри memcpy(). От регистров почти никакого толку - в каком месте кода произошёл сбой? Зато по стеку можно легко проследить цепочку вызовов. Уже сколько так багов нашли. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба И где гарантия, что компилятор при очередной перекомпиляции, не разместит какие-то переменные в регистрах которые Вы хотели сохранить до вашей вставки? Такое надо делать целиком в asm-функциях. Напишите ISR fault-ов полностью на asm, там всё элементарно. С легкой руки (для IAR) __stackless void FaultHandler(void * p_base, unsigned long EXC_RETURN) { SaveContext(p_base, EXC_RETURN); EXPT.VERSION = APP_Get_FwVersion(); gMarker = EXCEPTION_MARKER; #ifdef NDEBUG SYS_Reset(); #else while (1); #endif } __stackless __irq void HardFaultHandler(void) { if (SCB->HFSR & SCB_HFSR_DEBUGEVT_Pos) return; FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()), __get_LR()); } __stackless __irq void MemoryFaultHandler(void) { FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()), __get_LR()); } __stackless __irq void BusFaultHandler(void) { FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()), __get_LR()); } __stackless __irq void UsageFaultHandler(void) { FaultHandler((void*)( (__get_LR() & BIT(2)) ? __get_PSP() : __get_MSP()), __get_LR()); // while(1); } void SaveContext(void * p_base, unsigned long EXC_RETURN) { EXPT.Registers = *(struct exception_saved_context * )p_base; EXPT.SP = (DWORD)p_base + sizeof(struct exception_saved_context); EXPT.Status.dwRaw = SCB->CFSR; EXPT.Exception = (int_source_t)(SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk); EXPT.EXC_RETURN = ( EXPT.Status.BFSR.BFARVALID) ? SCB->BFAR : EXC_RETURN; SCB->CFSR = 0; EXPT.HeapFree = OS_MEMORY_AVAILABLE(); EXPT.ShedulerState = OS_GET_SHEDULER_STATE(); EXPT.Context.StackFree = OS_GET_TASK_STACK_WATERMARK(OS_GET_CURRENT_TASK_HANDLE()); strncpy(EXPT.Context.Name, (char*)OS_GET_CURRENT_TASK_NAME() , sizeof(EXPT.Context.Name)); EXPT.Context.Name[sizeof(EXPT.Context.Name)-1] = '\0'; Для CORTEX ядра все получается и без ASM функций. А вот для ARM делал вставку на ASM с обработчиками исключений и функцией сохранения регистров. В большинстве случаев эта информация помогает на столе. В основном в исключения валимся при переполнении стека. Поэтому место возникновения исключения, при наличии RTOS, мало что скажет. Главное состояние планировщика, текущая задача(последняя в случае переключения контекста) и свободное место в стеке задачи. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба __stackless void FaultHandler(void * p_base, unsigned long EXC_RETURN) { SaveContext(p_base, EXC_RETURN); ... Для CORTEX ядра все получается и без ASM функций. Ещё раз: Где гарантия, что компилятор не вставит перед Вашей SaveContext() например создание стекового фрейма с занесением указателя на него в любой из R4-R11? Типа: PUSH {R7, LR} ADD R7, SP, #N ... или ещё чего, чего ему вздумается. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
scifi 1 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Ещё раз: Где гарантия, что компилятор не вставит перед Вашей SaveContext() например создание стекового фрейма с занесением указателя на него в любой из R4-R11? Как где? __stackless же. У gcc есть аналогичная штука - naked. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Как где? __stackless же. У gcc есть аналогичная штука - naked. А это обязательное или только рекомендуемое как inline? А функции, которые из FaultHandler() вызываются тоже все __stackless? И стек - это для примера. Что угодно, например - в какой-либо регистр может заранее положить указатель на секцию данных, либо какие-то другие данные. Использование регистров внутри си-функции - по усмотрению компилятора, любые домыслы по их содержимому - потенциальные грабли. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба jcxz, если делать что-то бездумно - то возможны любые грабли. А если подумать - компилятор пытается лишь сделать то, что от него просят. И не больше. Головой думать надо, прежде что-то делать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба И не больше. Ну, ну. Я как-то на Си осмелился пописать под AVR. На меге8 килобайты флеш закончились настолько быстро, что захотелось узнать куда. Листинг показал, что компилятор сохраняет в стек все регистры при вызове функций, даже неиспользуемые. Отдельным приключением было сделать программный UART на Си - по тактам реализация очень неустойчива - на том краю планеты бабочка махнет рукой (добавим, например, переменную в проект), а у нас пару лишних тактов в цикле задержки. Что касается регистров, тактов и байтов - компилятор очень непредсказуемая штука. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Копейкин 0 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Листинг показал, что компилятор сохраняет в стек все регистры при вызове функций, даже неиспользуемые. Компилятор IAR так поступает при выключенной оптимизации (или невысоком уровне). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба adnega, речь немного не про такой случай. Использование стека еще как компиляторозависимо. От того, как он будет оптимизировать код, и какую оптимизацию ему разрешено использовать. Уверен, и Вашему случаю нашлось бы объяснение в мануалах. Здесь о том, что __stackless говорит компилятору, что стека НЕТ! То есть только регистры. И ничего он в них выделять не будет, пока не попросишь. Да, и определитесь, что у вас закончилось, стек или флеш.... не вяжется как-то. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Компилятор IAR так поступает при выключенной оптимизации (или невысоком уровне). Тут советовали головой думать, но моя не может придумать - "зачем"? Зачем неиспользуемые регистры складывать в стек?? Хотелось, чтобы основные "просьбы" к компилятору были в исходнике, а не различных ключах. Именно поэтому есть задачки, не решаемые на С гарантированно, рассчитывать на здравый смысл компилятора я бы не стал. Да, и определитесь, что у вас закончилось, стек или флеш.... не вяжется как-то. Прошу без эмоций определить, что быстрее заканчивается при таком подходе 11e: 1f 92 push r1 120: 0f 92 push r0 122: 0f b6 in r0, 0x3f; 63 124: 0f 92 push r0 126: 11 24 eor r1, r1 128: 2f 93 push r18 12a: 3f 93 push r19 12c: 4f 93 push r20 12e: 5f 93 push r21 130: 6f 93 push r22 132: 7f 93 push r23 134: 8f 93 push r24 136: 9f 93 push r25 138: af 93 push r26 13a: bf 93 push r27 13c: ef 93 push r30 13e: ff 93 push r31 Это еще годный код, т.к. не все регистры убираются в стек, а только используемые. Я описывал ситуацию, когда банальная установка переменной оборачивалась push-ами и pop-ами для всех регистров. В моем случае резко закончилась флеш, хотя и к глубине стека требования резко возрастают. Перед использованием С этот же проект был реализован на asm. При 10% asm-функционала на С закончился флеш. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 17 мая, 2016 Опубликовано 17 мая, 2016 · Жалоба Зачем неиспользуемые регистры складывать в стек?? Затем что оптимизации НЕТ. Делаем так, чтобы 110% все работало и отладка была удобной. Определитесь для начала, какой результат хотите: быстрый, мало памяти занимал, или для отладки. Программа не работала? Так какие претензии к компилятору? Прошу без эмоций определить, что быстрее заканчивается при таком подходе Так да. Закончится и то, и другое. На AVR почти не писал. А в ARM,как тут уже приводили, - одна инструкция хооть для всех регистров. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 245 18 мая, 2016 Опубликовано 18 мая, 2016 · Жалоба jcxz, если делать что-то бездумно - то возможны любые грабли. А если подумать - компилятор пытается лишь сделать то, что от него просят. И не больше. Головой думать надо, прежде что-то делать. Ну-ну. И что же он пытается сделать? Расскажите-ка нам какие именно регистры задействуют все возможные компиляторы во всех возможных режимах оптимизации. К Вашему сведению: компилятор имеет право использовать внутри функции любые регистры как ему заблагорассудится если это не противоречит соглашениям вызова. Если думать именно головой, то как раз не надо делать никаких гаданий как именно будет построен код внутри си-функции. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alechek 0 18 мая, 2016 Опубликовано 18 мая, 2016 · Жалоба jcxz, сравнивать неопределеность, исходящую от компилятора с определенностью, полученой от ручного кодирования машинных кодов (ASM) бессмысленно. ASM в выигрыше однозначно. Но, вероятность получить "пасхальное яйцо" от компилятора примерно равна вероятности получить неработоспособный код со вполне работоспособных исходников. Выбор всегда есть: - хочешь сложное кодирование но 110% результат - пиши в машинных кодах, - хочешь более легкое программирование и 100% результат - попытайся думать так, как думает компилятор. И когда я говорю про компилятор, я имею ввиду конкретный, а не абстрактную сущность всех компиляторов. К слову, ASM файлы тоже в большинстве случаев не переносимы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться