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

Случайное падение шедулера. Как отследить?

Доброго времени!

Пишу проект на 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: Я отслеживаю переполнение хипа и стека. Эти хуки не вызываются, из чего можно сделать вывод, что использование памяти не выходит за границы..

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


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

А кроме как в этом месте у вас 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 или аналоги? :)

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


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

Спасибо за советы!

Покрыл отладочными состояниями. конструкторы и деструкторы отрабатываются правильно. Предсказуемо)

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. Больше ничего.

Если есть идеи, с удовольствием готов принять и проверять!)

post-82043-1480494500_thumb.png

post-82043-1480496914_thumb.png

Изменено пользователем vv_

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


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

Похоже на повреждение кучи. Кто ее повреждает - вопрос. Попробуйте поотключать все части программы, добейтесь, чтобы она работала без зависаний. Затем постепенно включайте

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


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

Я исключил все модули, не относящиеся к эксперименту и действительно, пример заработал. Значит что то ломало кучу. Сейчас буду восстанавливать модуль за модулем. Как выясню в чем было дело, отпишусь.

Спасибо =)

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


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

Возможна ли такая ситуация, если я выполняю оператор new еще до того, как запустился шедулер ( osKernelStart();)?

У меня есть один участок кода, в котором статический член класса в своём конструкторе вызывается new. Но этот код выполняется еще перед тем, как зайти в main() {...}. Можно ли работать с памятью до запуска ОС?

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


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

Возможна ли такая ситуация, если я выполняю оператор new еще до того, как запустился шедулер ( osKernelStart();)?

У меня есть один участок кода, в котором статический член класса в своём конструкторе вызывается new. Но этот код выполняется еще перед тем, как зайти в main() {...}. Можно ли работать с памятью до запуска ОС?

 

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

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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