This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Setting a watchpoint dynamically

Hello,

I am trying to spot a bug in which some memory corruption is happening. I don't know in advance at which address the corruption will happen, as it happens in some circular buffer. Maybe something is wrongly reading an address referring to this buffer and then writing there. I don't know.

Anyhow, I have found some way to store in some global variable mem_corruption_address the  data memory address at which the corruption before it happens, just in case it would happen, and what I want to do it call some watch_corruption_enable function at some point before code section in which the corruption may happen and watch_corruption_disable after this code section.

So here is the code which I have written : 

extern uint32_t const volatile* volatile mem_corruption_address;
uint32_t const volatile* volatile mem_corruption_address = NULL;

nrf_atomic_flag_t watch_corruption;

static void watch_corruption_set_watchpoint(void)
{
    if (watch_corruption) {
        DWT->COMP0     = (uintptr_t)mem_corruption_address;
        DWT->MASK0     = 0; // range = 1 × 32bit word.
        DWT->FUNCTION0 = (6<<0) // generate watch point debug event on WO match
            | (0<<7) // FUNCTION0.CYCMATCH == 0 for address comparison
            | (0<<8) // FUNCTIONn.DATAVMATCH == 0 for address comparison
            | (1<<5) // FUNCTIONn.EMITRANGE == 1 for trace packet generation
            ;
    }
}

static void watch_corruption_reset_watchpoint(void)
{
    DWT->FUNCTION0 = 0;
}

void watch_corruption_enable(void)
{
    nrf_atomic_flag_set(&watch_corruption);
}

void watch_corruption_disable(void)
{
    if (nrf_atomic_flag_clear_fetch(&watch_corruption)) {
        watch_corruption_reset_watchpoint();
    }
}

The watch_corruption_set_watchpoint is called after each valid writing to the potentially corrupted memory, so that a valid write won't trigger the watchpoint, and I am also sure that no subsequent valid writing can occur before watch_corruption_disable is called.

Now, I am debugging with the Segger JTracePro with Ozone IDE (so I compiled my FW with ENABLE_TRACE #define'd). My hope was after the watchpoint is triggered I can browse back the instruction trace to find what made the corruption.

So, my problem is the following : instead of what I expected/hoped to get, I get Ozone more or less frozen at some point of time, I cannot stop the debugging session. I need to for the Ozone application stop from system. So, it seems that what I am doing it messing up how Ozone interfaces with the JTracePro.

I would like to know how to do it properly. 

  • OK, here is the new test_dwt function.

    void test_dwt(void)
    {
        CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk | CoreDebug_DEMCR_MON_EN_Msk;
    
        NRF_LOG_INFO("CoreDebug->DEMCR : 0x%x", CoreDebug->DEMCR);
    
        NRF_LOG_WARNING("DWT->CTRL = 0x%08x",DWT->CTRL);
    
        uint32_t dummy;
    
        DWT->COMP0 = &dummy;
        DWT->MASK0 = 0;
        DWT->FUNCTION0 = (6<<0) // FUNCTION0.FUNCTION = 6, generate watchpoint debug event for WO access
            | (0<<7) // FUNCTION0.CYCMATCH == 0 for address comparison
            | (0<<8) // FUNCTIONn.DATAVMATCH == 0 for address comparison
            | (0<<5) // FUNCTIONn.EMITRANGE == 0, no meaning when FUNCTION = 6
            ;
    
        *(uint32_t volatile*)&dummy = 0;
    
        NRF_LOG_WARNING("DWT->FUNCTION0 = 0x%08x",DWT->FUNCTION0);
        DWT->FUNCTION0 = 0;
    
        NRF_LOG_FLUSH();
        for(;;) {}
    }
    

    And here is what I get now, it looks better : 

    <info> app: CoreDebug->DEMCR : 0x1010000
    <warning> app: DWT->CTRL = 0x40000001
    <warning> app: DWT->FUNCTION0 = 0x01000006
    


  • Dear ,

    Happy new year 2021, all the best to you !

    One more question : can you remember which logger backend you used with the code which you provided. Was it through the UART, right ?

    I adapted this code to my platform and breakpoint condition, and I tried that with the RTTlogger, and it seems that if I start to play with the DebugMonitor_IRQn and overload the DebugMon_Handler, then when the breakpoint condition occurs, the trace stops, so the final trace made by DebugMon_Handler does not go to the output, the log gets kind of frozen, and nothing more seems to happen.

    I tried to connect the debugger in this situation, and the only thing I can see is that I am stuck in the FreeRTOS calling vPortSuppressTicksAndSleep for the Idle task.

  • Hi and happy new year! 

    Yes, I only tested with the UART backend so I didn't have to keep the debugger connected. Have you tried testing with and without the NRF_LOG_FINAL_FLUSH()  line in your DebugMon_Handler?

  • Actually, it gets to the same result (everything looking like frozen) even with an empty DebugMon_Handler

    I also tried this : I carried out the procedure to get up to the breakpoint with the RTTlogger running. When the breakpoint occurs then everything gets kind of frozen, so I exit the RTTlogger and connect to the app with gdb through the Segger gdb server. When doing this I can see this backtrace (I just replaced the full path by …) : 

    (gdb) bt
    #0 vPortSuppressTicksAndSleep (xExpectedIdleTime=5120)
    at …/nrf-sdk/external/freertos/portable/CMSIS/nrf52/port_cmsis_systick.c:254
    #1 0x00002616 in prvIdleTask (pvParameters=0x0 <__isr_vector>)
    at …/nrf-sdk/external/freertos/source/tasks.c:3321
    #2 0x00000548 in pxPortInitialiseStack (pxTopOfStack=0x1400 <xQueueSemaphoreTake+204>, pxCode=0xa5a5a5a5,
    pvParameters=0xa5a5a5a5)
    at …/nrf-sdk/external/freertos/portable/CMSIS/nrf52/port_cmsis.c:147
    Backtrace stopped: previous frame identical to this frame (corrupt stack?)

    I also did the experiment with having DebugMon_Handler doing some border effect on some static flag like this : 

    uint32_t titi;
    void DebugMon_Handler(void)
    {
    titi = 1;
    }


    and what happens is that when inspecting titi with the debugger, it is equal to 0, the startup value, so it seems that either DebugMon_Handler is not entered when the interrupt occurs, or the execution goes again to the startup and gets frozen at some point. 

  • Yes, I agree, it looks like maybe the DebugMonitor_IRQn is not being raised, and that the program just stays in the "wait for event" loop. Are you sure you are meeting the conditions to trigger this event? Does the ISR get invoked as expected if you call NVIC_SetPendingIRQ(DebugMonitor_IRQn )?

Related