Record running task name on hard fault

Hi,

I’ve written a hard fault handler for a cortex M7 with the help of my friend Deepseek and would like to record the name of the freeRTOS task that was running at the time the hard fault occurred.

I was thinking:

TaskStatus_t xTaskDetails; 

xTaskDetails = vTaskGetTaskInfo( xTaskGetCurrentTaskHandle()  ) 

I would do this at the end of the task handler so cpu/fpu registers are recorded before executing this code which will probably change the cpu register values.

This code is performed in the hard fault handler interrupt routine and as the proposed commands aren’t ISR friendly I thought I had better ask.

I can give it a try, but I was wondering if there were any gotchas that I need to be aware of.

If the method I propose is a dud is there another way?

I know doing a stack analysis will often point to the naughty task, however this is magnum tedious and error prone in my case.

Kind regards
Rob

This is certainly not what the API was designed for as you rightly pointed out too. Looking at the function definition, I see that it has some critical section which would enable and disable interrupts and may cause some problems. I cannot confirm because we have not tried this.

You cannot trust the state of the system once a hard fault happens. For example, what if the reason of the hard fault was a corrupted pxCurrentTCB. In this case you will get some garbage task name or even worse try to read some non-readable memory. The best approach usually is to dump all the state (which may include pxCurrentTCB) and then do the analysis.

You can not use non-FromISR functions inside your fault handler. I will tend to just grab the current task handle from the global variable. Yes, that isn’t advised for normal task code, but this isn’t normal task code. You can even get the details of the TCB either by copying the structure definitions, or by putting your fault handling code inside the file freertos_task_c_additions.h and defining the define to enable that.

Another point is that, at least currently, pcTaskGetName(0) will get a pointer to the name of the current task, and while it doesn’t have FromISR, is currently safe to use to determine the task that was interrupted.

Note, the fault handler might have been triggered by an ISR, so you do need some stack tracing to find the guilty party. I find that for fault handlers, showing the registers, and especially the PC of the fault, is the first step to the answer, and then I look at the task that was running if I am in library code.

1 Like

Hi,

Thanks for that.

I will give it a try and create some hard faults to see what happens.

Rob

Hi,

I tried it out:

TaskStatus_t 	xTaskDetails;
TaskStatus_t 	taskStatus;


/* HardFault handler in C, with stack frame location and LR value extracted
   from the assembly wrapper as input parameters */
void HardFault_Handler_C(unsigned int *hardfault_args)
{
	_BFAR     = SCB->BFAR;
	_MMAR 	  = SCB->MMFAR;
	_CFSR 	  = SCB->CFSR;

  stacked_r0  = ((unsigned int) hardfault_args[0]);
  stacked_r1  = ((unsigned int) hardfault_args[1]);
  stacked_r2  = ((unsigned int) hardfault_args[2]);
  stacked_r3  = ((unsigned int) hardfault_args[3]);
  stacked_r12 = ((unsigned int) hardfault_args[4]);
  stacked_lr  = ((unsigned int) hardfault_args[5]);
  stacked_pc  = ((unsigned int) hardfault_args[6]);
  stacked_psr = ((unsigned int) hardfault_args[7]);

	#if(PRINT_HARD_FAULTS == 1)
		printf("\n\r==== [HardFault_Handler] ====\n\r");
	  printOutFault();
	#endif

#if(USING_FREE_RTOS == 1)
	vTaskGetTaskInfo( xTaskGetCurrentTaskHandle(), &taskStatus, pdTRUE, eInvalid);
	printTaskStatus();
#endif

#if(LOOP_AT_COMPLETION == 1)
	  while(1) __asm("BKPT #0\n") ; // Break into the debugger
#else
#if(EROR_LED_FLASH_ON_HARD_FAULT == 1)
		for(uint32_t i = ERROR_LED_LOOP; i > 0 ; i--)
		{
			HAL_GPIO_WritePin(ErrorLED_Red_GPIO_Port, ErrorLED_Red_Pin, GPIO_PIN_SET);
		}

		HAL_GPIO_WritePin(ErrorLED_Red_GPIO_Port, ErrorLED_Red_Pin, GPIO_PIN_RESET);
#endif
		NVIC_SystemReset();
#endif
}

And it worked fine. I had a look at the code for the vTaskGetTaskInfo and in my version of freeRTOS Kernel V10.3.1 and there were some taskCritical functions in functions called by vTaskGetTaskInfo , (eg. eTaskGetState), however this did not appear to be a problem. The vTaskGetTaskInfo gave the correct answer for hard faults caused within the task itself or by a called function. I tried a few different typed of fault. Of course it won’t work if the task control blocks are corrupted, but it does seem to be of some utility.

I will do some more testing to see what breaks it.

The out put looked like:

==== [MemManage_Handler] ====
Hard Fault at Time: - UTC 2025-04-10  05:32:12
- Stack frame:
 R0  = 0x00000000
 R1  = 0xa5a5a5a5
 R2  = 0xdeadbeef
 R3  = 0x00000000
 R12 = 0xa5a5a5a5
 LR  = 0x00000d2d
 PC  = 0x90043476
 PSR = 0x41000000

- FSR/FAR:
 CFSR = 0x00000082
 HFSR = 0x00000000
 DFSR = 0x00000009
 AFSR = 0x00000000
 Mem Mgt Fault Addr = 0x00000000
 Mem Mgt Fault - DACCVIOL Data access violation\s

======= [End] =======

Task Name: ghControlTask
Task State: 0
Task Priority: 55
Task Stack High Water Mark: 238
Task Handle Addr: 200098d8

Thank you for sharing your findings!

No problem, thaank’s for your advice. You are a very helpful person.

1 Like

I call xTaskGetCurrentTaskHandle to get the task handle, check that it isn’t null, then call pcTaskGetName using that handle. It works for me. Yes I know I shouldn’t really call a function without _FROM_ISR in the name from an ISR but from examining the code I can see that it is safe at least for single-core systems.