0
votes

I am still learning about debugging C using python within gdb (arm-none-eabi-gdb, in my case). I am trying to use this facility to get thread information of a real-time OS running on ARM Cortex-M. Reading some OS structures, I can access the thread control blocks of the OS. I know the PC and the SP of each thread. How can I use gdb's Python to dump the threads' backtrace. Is there a generic API that can traverse the stack when given PC and SP?

I have read https://sourceware.org/gdb/current/onlinedocs/gdb/Unwinding-Frames-in-Python.html#Unwinding-Frames-in-Python and I feel there might be a way to achieve that but I need some help.

Also, if possible, can I make gdb aware of the different threads of the OS? This link: https://sourceware.org/gdb/current/onlinedocs/gdb/Threads-In-Python.html#Threads-In-Python touches on threads but relies on OS info. Can these be overload with what I know about the different OS threads from their respective control blocks?

Thanks!

2

2 Answers

0
votes

After some more reading and trying to make use of old debugger knowledge that I have accumulated over the years, I managed to get this working. It lacks optimization but for now, I'm very please. This can be considered a poor-man's debugger making use of GDB's Python support to track the active threads in the system. It's generic, I assume, but the implementation targeted RTX (Keil's OS). It worked on Cortex-M0. It may need some tweaking to fit other operating systems or different cores.

The main idea:

  1. Use OS structures, to identify where the thread control block reside.
  2. From the thread control block identify where is the different thread stacks are.
  3. Read from the stack all the vital registers; SP, LR, and PC
  4. Save the same registers for the current, running thread.
  5. Loop over the different thread, change the vital registers to the ones matching the thread, then print the backtrace.
  6. Enjoy a poor-man's OS-aware debugger.

The script can be found here:

https://gitlab.com/hesham/gdb-rtx-thread-backtrce/blob/master/rtx-threads-bt.py

It was a very good exercise to explore the power of GDB's Python extension!

0
votes

For FreeRTOS I use the following gdb script:

define printtasklist
  # $arg0 is a pointer to an uxList of tasks.
  set $plist = (List_t*)$arg0
  printf ": %d tasks\n", $plist->uxNumberOfItems
  set $iter = $plist->xListEnd.pxNext
  while ($iter != &$plist->xListEnd)
    set $vtask = ($iter->pvOwner)
    set $task = (tskTCB*)$vtask
    print $task
    print $task->pcTaskName
    set $iter = $iter->pxNext
  end
end

define printtasks
  printf "%d tasks:\n", uxCurrentNumberOfTasks
  printf "pending ready"
  printtasklist &xPendingReadyList
  printf "suspended"
  printtasklist &xSuspendedTaskList
  printf "delayedW"
  printtasklist pxDelayedTaskList
  printf "delayedO"
  printtasklist pxOverflowDelayedTaskList
  set $prio = 0
  set $maxprio = sizeof(pxReadyTasksLists) / sizeof(pxReadyTasksLists[0])
  while ($prio < $maxprio)
    printf " ready at prio"
    printf "%d", $prio
    printtasklist &pxReadyTasksLists[$prio]
    set $prio = $prio + 1  
  end
end

define savestate
  set $svpc = $pc
  set $svsp = $sp
  set $svlr = $lr
end

define restorestate
  set $pc = $svpc
  set $sp = $svsp
  set $lr = $svlr
end

define cm3bttask
       # arg0: task handle (pointer to tskTCB)
  savestate
  set $ptsk = (tskTCB*)$arg0
  set $tskstk = (uint32_t*)$ptsk->pxTopOfStack
  set $lr = $tskstk[13]
  set $pc = $tskstk[14]
  set $sp= $tskstk + 16
  bt
end

The idea is that I use printtasks to get the list of task control blocks, then use cm3bttask $26 if the task I'm interested in has the control block pointer in the GDB history printed as line $26. I can see that on the output of printtasks.