vv_ 0 29 ноября, 2016 Опубликовано 29 ноября, 2016 · Жалоба Доброго времени! Пишу проект на C++ в Keil, процессор STM32F4**, ось FreeRTOS V8.2.3, менеджер памяти heap_4. Проект сгенерирован в CubeMX. Столкнулся с ситуацией, когда при обработке исключений на C++ происходит неопределенное поведение. class MemTest{ public: MemTest(){ _p = new char[10000]; } ~MemTest(){ delete _p; } private: char * _p; }; class exc{ public: exc(){} ~exc(){} }; /* таска в которой локализую ошибку */ void exception_debug(void const * argument){ while(true){ try{ MemTest mt; throw exc(); } catch(const exc& e){ osDelay(100); } catch(...){ osDelay(500); } } vTaskDelete(NULL); } Операторы new и delete выглядят так: void *operator new(size_t size){ void *p=pvPortMalloc(size); #ifdef __EXCEPTIONS if (p==0) throw std::bad_alloc(); #endif return p; } void operator delete(void *p){ vPortFree( p ); } Сценарий выполнения выглядит следующим образом: 1. некоторое количество раз (в среднем от 1 до 25) выполняется в цикле try ... catch и мы успешно падаем в блок catch(const exc& e). 2. после последнего удачного раза отработки конструкторов - деструкторов типов MemTest и exc, на следующей итерации, когда я попадаю в деструктор MemTest, выполняю операцию delete и в этот момент у меня выполнение кода передаётся на функцию HAL_TIM_IRQHandler и дальше попадаю в функции HAL для обработки сравнений итп, что касается таймера. Вопрос такой, как определить в какую сторону копать? У меня есть предположение, что в некоторой ситуации крашится шедулер FreeRTOS. Но отловить в какой именно пока не получилось. PS1: Я отслеживаю переполнение хипа и стека. Эти хуки не вызываются, из чего можно сделать вывод, что использование памяти не выходит за границы.. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Johnny81 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 · Жалоба А кроме как в этом месте у вас new нигде не дергается? Если дергается и там вылетает bad_alloc - вы его перехватываете? Для начала можно вставить отладочные метки в код. В простейшем виде: volatile int debug; class MemTest{ public: MemTest(){ debug=1; _p = new char[10000]; debug=2; } ~MemTest(){ debug=3; delete _p; debug=4; } private: char * _p; }; и т.п. и глянуть где именно вылетает. Дальше можно debug превратить в массив и запоминать несколько последних шагов. ЗЫ у меня есть проект для gcc со сторонней плюсовой либой, которая кидает исключения. Полет пока нормальный... ЗЫЫ вы ж понимаете, что если исключение вылетит в конструкторе, то деструктор вызван не будет. И если у вас в вашем классе будет два указателя на динамически выделенную память, то надо будет использовать std::unique_ptr или аналоги? :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vv_ 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 (изменено) · Жалоба Спасибо за советы! Покрыл отладочными состояниями. конструкторы и деструкторы отрабатываются правильно. Предсказуемо) volatile int debug; class MemTest{ public: MemTest() : _p(0) { debug = 0; try{ debug = 1; _p = new char[10000]; debug = 2; } catch(...){ debug = 3; } HAL_GPIO_TogglePin(PAPER_LED_GPIO_Port, PAPER_LED_Pin); MemTest(){ debug = 4; if(_p != 0) delete _p; debug = 5; } private: char * _p; }; class exc{ public: exc(){ debug = 6; } ~exc(){ debug = 7; } }; void exception_debug(void const * argument){ while(true){ try{ MemTest mt; throw exc(); } catch(const exc& e){ osDelay(500); debug = 8; } catch(...){ osDelay(500); } } vTaskDelete(NULL); debug = 10; Error_Handler(); } Но в какой то "случайный" момент (около 20 итераций, каждый раз по разному), поток выполнения программы падает в: /** * @brief This function handles TIM interrupts requests. * @param htim: pointer to a TIM_HandleTypeDef structure that contains * the configuration information for TIM module. * @retval None */ void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim) ... /* TIM Update event */ if(__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) { if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) !=RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); HAL_TIM_PeriodElapsedCallback(htim); } ... } В файле startup_stm32f427xx.s вижу, что у меня вызывается прерывание TIM6_DAC_IRQHandler и это значит "TIM6 and DAC1&2 underrun errors" DCD TIM6_DAC_IRQHandler ; TIM6 and DAC1&2 underrun errors Стек вызовов выглядти так: рис 1 Заметил еще, что при вызове в void vPortFree( void *pv ){...} функции vTaskSuspendAll(); я постоянно попадаю в HAL_TIM_IRQHandler. Если в дебаге пошагать дальше, то можно добраться до следующего виде стека вызовов: рис 2 Интересно то, что после всего этого мы снова возвращаемся к конструктору MemTest, выделяем память, а в деструкторе MemTest при освобождении "_p" история повторяется. При всём этом, если я отслеживаю процесс не в дебаге, то диод у меня или загарается и уже не гаснет или наоборот. Но больше никогда после падения не изменяет своего состояния. А если в дебаге хожу, то после краша я дохожу до места где диод меняет своё значение. Не могу понять как это отловить и как локализовать причину. Сейчас работает только одна таска отладочная и Idle. Больше ничего. Если есть идеи, с удовольствием готов принять и проверять!) Изменено 30 ноября, 2016 пользователем vv_ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Johnny81 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 · Жалоба Похоже на повреждение кучи. Кто ее повреждает - вопрос. Попробуйте поотключать все части программы, добейтесь, чтобы она работала без зависаний. Затем постепенно включайте Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vv_ 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 · Жалоба Я исключил все модули, не относящиеся к эксперименту и действительно, пример заработал. Значит что то ломало кучу. Сейчас буду восстанавливать модуль за модулем. Как выясню в чем было дело, отпишусь. Спасибо =) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
vv_ 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 · Жалоба Возможна ли такая ситуация, если я выполняю оператор new еще до того, как запустился шедулер ( osKernelStart();)? У меня есть один участок кода, в котором статический член класса в своём конструкторе вызывается new. Но этот код выполняется еще перед тем, как зайти в main() {...}. Можно ли работать с памятью до запуска ОС? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Johnny81 0 30 ноября, 2016 Опубликовано 30 ноября, 2016 · Жалоба Возможна ли такая ситуация, если я выполняю оператор new еще до того, как запустился шедулер ( osKernelStart();)? У меня есть один участок кода, в котором статический член класса в своём конструкторе вызывается new. Но этот код выполняется еще перед тем, как зайти в main() {...}. Можно ли работать с памятью до запуска ОС? Можно работать, только надо проверить когда куча инициализируется. Помнится, при первом обращении, но лучше посмотрите, что у вас Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться