mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-26 09:26:25 +08:00
move Debug to platforms/nuttx
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
#
|
||||
# Assorted ARMv7M macros
|
||||
#
|
||||
|
||||
echo Loading ARMv7M GDB macros. Use 'help armv7m' for more information.\n
|
||||
|
||||
define armv7m
|
||||
echo Use 'help armv7m' for more information.\n
|
||||
end
|
||||
|
||||
document armv7m
|
||||
. Various macros for working with the ARMv7M family of processors.
|
||||
.
|
||||
. vecstate
|
||||
. Print information about the current exception handling state.
|
||||
.
|
||||
. Use 'help <macro>' for more specific help.
|
||||
end
|
||||
|
||||
|
||||
define vecstate
|
||||
set $icsr = *(unsigned *)0xe000ed04
|
||||
set $vect = $icsr & 0x1ff
|
||||
set $pend = ($icsr & 0x1ff000) >> 12
|
||||
set $shcsr = *(unsigned *)0xe000ed24
|
||||
set $cfsr = *(unsigned *)0xe000ed28
|
||||
set $mmfsr = $cfsr & 0xff
|
||||
set $bfsr = ($cfsr >> 8) & 0xff
|
||||
set $ufsr = ($cfsr >> 16) & 0xffff
|
||||
set $hfsr = *(unsigned *)0xe000ed2c
|
||||
set $bfar = *(unsigned *)0xe000ed38
|
||||
set $mmfar = *(unsigned *)0xe000ed34
|
||||
|
||||
if $vect < 15
|
||||
|
||||
if $hfsr != 0
|
||||
printf "HardFault:"
|
||||
if $hfsr & (1<<1)
|
||||
printf " due to vector table read fault\n"
|
||||
end
|
||||
if $hfsr & (1<<30)
|
||||
printf " forced due to escalated or disabled configurable fault (see below)\n"
|
||||
end
|
||||
if $hfsr & (1<<31)
|
||||
printf " due to an unexpected debug event\n"
|
||||
end
|
||||
end
|
||||
if $mmfsr != 0
|
||||
printf "MemManage:"
|
||||
if $mmfsr & (1<<5)
|
||||
printf " during lazy FP state save"
|
||||
end
|
||||
if $mmfsr & (1<<4)
|
||||
printf " during exception entry"
|
||||
end
|
||||
if $mmfsr & (1<<3)
|
||||
printf " during exception return"
|
||||
end
|
||||
if $mmfsr & (1<<1)
|
||||
printf " during data access"
|
||||
end
|
||||
if $mmfsr & (1<<0)
|
||||
printf " during instruction prefetch"
|
||||
end
|
||||
if $mmfsr & (1<<7)
|
||||
printf " accessing 0x%08x", $mmfar
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
if $bfsr != 0
|
||||
printf "BusFault:"
|
||||
if $bfsr & (1<<2)
|
||||
printf " (imprecise)"
|
||||
end
|
||||
if $bfsr & (1<<1)
|
||||
printf " (precise)"
|
||||
end
|
||||
if $bfsr & (1<<5)
|
||||
printf " during lazy FP state save"
|
||||
end
|
||||
if $bfsr & (1<<4)
|
||||
printf " during exception entry"
|
||||
end
|
||||
if $bfsr & (1<<3)
|
||||
printf " during exception return"
|
||||
end
|
||||
if $bfsr & (1<<0)
|
||||
printf " during instruction prefetch"
|
||||
end
|
||||
if $bfsr & (1<<7)
|
||||
printf " accessing 0x%08x", $bfar
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
if $ufsr != 0
|
||||
printf "UsageFault"
|
||||
if $ufsr & (1<<9)
|
||||
printf " due to divide-by-zero"
|
||||
end
|
||||
if $ufsr & (1<<8)
|
||||
printf " due to unaligned memory access"
|
||||
end
|
||||
if $ufsr & (1<<3)
|
||||
printf " due to access to disabled/absent coprocessor"
|
||||
end
|
||||
if $ufsr & (1<<2)
|
||||
printf " due to a bad EXC_RETURN value"
|
||||
end
|
||||
if $ufsr & (1<<1)
|
||||
printf " due to bad T or IT bits in EPSR"
|
||||
end
|
||||
if $ufsr & (1<<0)
|
||||
printf " due to executing an undefined instruction"
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
else
|
||||
if $vect >= 15
|
||||
printf "Handling vector %u\n", $vect
|
||||
end
|
||||
end
|
||||
if ((unsigned)$lr & 0xf0000000) == 0xf0000000
|
||||
if ($lr & 1)
|
||||
printf "exception frame is on MSP\n"
|
||||
#set $frame_ptr = (unsigned *)$msp
|
||||
set $frame_ptr = (unsigned *)$sp
|
||||
else
|
||||
printf "exception frame is on PSP, backtrace may not be possible\n"
|
||||
#set $frame_ptr = (unsigned *)$psp
|
||||
set $frame_ptr = (unsigned *)$sp
|
||||
end
|
||||
if $lr & 0x10
|
||||
set $fault_sp = $frame_ptr + (8 * 4)
|
||||
else
|
||||
set $fault_sp = $frame_ptr + (26 * 4)
|
||||
end
|
||||
|
||||
|
||||
printf " r0: %08x r1: %08x r2: %08x r3: %08x\n", $frame_ptr[0], $frame_ptr[1], $frame_ptr[2], $frame_ptr[3]
|
||||
printf " r4: %08x r5: %08x r6: %08x r7: %08x\n", $r4, $r5, $r6, $r7
|
||||
printf " r8: %08x r9: %08x r10: %08x r11: %08x\n", $r8, $r9, $r10, $r11
|
||||
printf " r12: %08x\n", $frame_ptr[4]
|
||||
printf " sp: %08x lr: %08x pc: %08x PSR: %08x\n", $fault_sp, $frame_ptr[5], $frame_ptr[6], $frame_ptr[7]
|
||||
|
||||
# Swap to the context of the faulting code and try to print a backtrace
|
||||
set $saved_sp = $sp
|
||||
set $sp = $fault_sp
|
||||
set $saved_lr = $lr
|
||||
set $lr = $frame_ptr[5]
|
||||
set $saved_pc = $pc
|
||||
set $pc = $frame_ptr[6]
|
||||
bt
|
||||
set $sp = $saved_sp
|
||||
set $lr = $saved_lr
|
||||
set $pc = $saved_pc
|
||||
else
|
||||
printf "(not currently in exception handler)\n"
|
||||
end
|
||||
end
|
||||
|
||||
document vecstate
|
||||
. vecstate
|
||||
. Print information about the current exception handling state.
|
||||
end
|
||||
@@ -0,0 +1,282 @@
|
||||
#
|
||||
# Generic GDB macros for working with NuttX
|
||||
#
|
||||
|
||||
echo Loading NuttX GDB macros. Use 'help nuttx' for more information.\n
|
||||
|
||||
define nuttx
|
||||
echo Use 'help nuttx' for more information.\n
|
||||
end
|
||||
|
||||
document nuttx
|
||||
. Various macros for working with NuttX.
|
||||
.
|
||||
. showheap
|
||||
. Prints the contents of the malloc heap(s).
|
||||
. showtasks
|
||||
. Prints a list of all tasks.
|
||||
. showtask <address>
|
||||
. Prints information about the task at <address>
|
||||
.
|
||||
. Use 'help <macro>' for more specific help.
|
||||
end
|
||||
|
||||
################################################################################
|
||||
# Heap display
|
||||
################################################################################
|
||||
|
||||
define _showheap
|
||||
set $index = $arg0
|
||||
set $used = 0
|
||||
set $free = 0
|
||||
if (sizeof(struct mm_allocnode_s) == 4)
|
||||
set $MM_ALLOC_BIT = 0x8000
|
||||
else
|
||||
set $MM_ALLOC_BIT = 0x80000000
|
||||
end
|
||||
printf "HEAP %d %p - %p\n", $index, g_mmheap.mm_heapstart[$index], g_mmheap.mm_heapend[$index]
|
||||
printf "ptr size\n"
|
||||
set $node = (char *)g_mmheap.mm_heapstart[$index] + sizeof(struct mm_allocnode_s)
|
||||
while $node < g_mmheap.mm_heapend[$index]
|
||||
printf " %p", $node
|
||||
set $nodestruct = (struct mm_allocnode_s *)$node
|
||||
printf " %u", $nodestruct->size
|
||||
if !($nodestruct->preceding & $MM_ALLOC_BIT)
|
||||
printf " FREE"
|
||||
set $free = $free + $nodestruct->size
|
||||
else
|
||||
set $used = $used + $nodestruct->size
|
||||
end
|
||||
if ($nodestruct->size > g_mmheap.mm_heapsize) || (($node + $nodestruct->size) > g_mmheap.mm_heapend[$index])
|
||||
printf " (BAD SIZE)"
|
||||
end
|
||||
printf "\n"
|
||||
set $node = $node + $nodestruct->size
|
||||
end
|
||||
printf " ----------\n"
|
||||
printf " Used: %u\n", $used
|
||||
printf " Free: %u\n\n", $free
|
||||
end
|
||||
|
||||
define showheap
|
||||
set $nheaps = sizeof(g_mmheap.mm_heapstart) / sizeof(g_mmheap.mm_heapstart[0])
|
||||
printf "Printing %d heaps\n", $nheaps
|
||||
set $heapindex = (int)0
|
||||
while $heapindex < $nheaps
|
||||
_showheap $heapindex
|
||||
set $heapindex = $heapindex + 1
|
||||
end
|
||||
end
|
||||
|
||||
document showheap
|
||||
. showheap
|
||||
. Prints the contents of the malloc heap(s).
|
||||
end
|
||||
|
||||
################################################################################
|
||||
# Task file listing
|
||||
################################################################################
|
||||
|
||||
define showfiles
|
||||
set $task = (struct tcb_s *)$arg0
|
||||
set $nfiles = sizeof((*(struct filelist*)0).fl_files) / sizeof(struct file)
|
||||
printf "%d files\n", $nfiles
|
||||
set $index = 0
|
||||
while $index < $nfiles
|
||||
set $file = &($task->filelist->fl_files[$index])
|
||||
printf "%d: inode %p f_priv %p\n", $index, $file->f_inode, $file->f_priv
|
||||
if $file->f_inode != 0
|
||||
printf " i_name %s i_private %p\n", &$file->f_inode->i_name[0], $file->f_inode->i_private
|
||||
end
|
||||
set $index = $index + 1
|
||||
end
|
||||
end
|
||||
|
||||
document showfiles
|
||||
. showfiles <TCB pointer>
|
||||
. Prints the files opened by a task.
|
||||
end
|
||||
|
||||
################################################################################
|
||||
# Task display
|
||||
################################################################################
|
||||
|
||||
define _showtask_oneline
|
||||
set $task = (struct tcb_s *)$arg0
|
||||
printf " %p %.2d %.3d %s\n", $task, $task->pid, $task->sched_priority, $task->name
|
||||
end
|
||||
|
||||
define _showtasklist
|
||||
set $queue = (dq_queue_t *)$arg0
|
||||
set $cursor = (dq_entry_t *)$queue->head
|
||||
|
||||
if $cursor != 0
|
||||
printf " TCB PID PRI\n"
|
||||
else
|
||||
printf " <none>\n"
|
||||
end
|
||||
|
||||
while $cursor != 0
|
||||
|
||||
_showtask_oneline $cursor
|
||||
|
||||
if $cursor == $queue->tail
|
||||
set $cursor = 0
|
||||
else
|
||||
set $next = $cursor->flink
|
||||
|
||||
if $next->blink != $cursor
|
||||
printf "task linkage corrupt\n"
|
||||
set $cursor = 0
|
||||
else
|
||||
set $cursor = $next
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Print task registers for a NuttX v7em target with FPU enabled.
|
||||
#
|
||||
define _showtaskregs_v7em
|
||||
set $task = (struct tcb_s *)$arg0
|
||||
set $regs = (uint32_t *)&($task->xcp.regs[0])
|
||||
|
||||
printf " r0: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", $regs[27], $regs[28], $regs[29], $regs[30], $regs[2], $regs[3], $regs[4], $regs[5]
|
||||
printf " r8: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", $regs[6], $regs[7], $regs[8], $regs[9], $regs[31], $regs[0], $regs[32], $regs[33]
|
||||
printf " XPSR 0x%08x EXC_RETURN 0x%08x PRIMASK 0x%08x\n", $regs[34], $regs[10], $regs[1]
|
||||
end
|
||||
|
||||
#
|
||||
# Print current registers for a NuttX v7em target with FPU enabled.
|
||||
#
|
||||
define _showcurrentregs_v7em
|
||||
printf " r0: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", $r0, $r1, $r2, $r3, $r4, $r5, $r6, $r7
|
||||
printf " r8: 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", $r8, $r9, $r10, $r11, $r12, $r13, $r14, $r15
|
||||
printf " XPSR 0x%08x\n", $xpsr
|
||||
end
|
||||
|
||||
#
|
||||
# Print details of a semaphore
|
||||
#
|
||||
define _showsemaphore
|
||||
printf "count %d ", $arg0->semcount
|
||||
if $arg0->holder.htcb != 0
|
||||
set $_task = (struct tcb_s *)$arg0->holder.htcb
|
||||
printf "held by %s", $_task->name
|
||||
end
|
||||
printf "\n"
|
||||
end
|
||||
|
||||
#
|
||||
# Print information about a task's stack usage
|
||||
#
|
||||
define showtaskstack
|
||||
set $task = (struct tcb_s *)$arg0
|
||||
|
||||
if $task == &g_idletcb
|
||||
printf "can't measure idle stack\n"
|
||||
else
|
||||
set $stack_free = 0
|
||||
while ($stack_free < $task->adj_stack_size) && ((uint32_t *)($task->stack_alloc_ptr))[$stack_free] == 0xffffffff
|
||||
set $stack_free = $stack_free + 1
|
||||
end
|
||||
set $stack_free = $stack_free * 4
|
||||
printf" stack 0x%08x-0x%08x (%d) %d free\n", $task->stack_alloc_ptr, $task->adj_stack_ptr, $task->adj_stack_size, $stack_free
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Print details of a task
|
||||
#
|
||||
define showtask
|
||||
set $task = (struct tcb_s *)$arg0
|
||||
|
||||
printf "%p %.2d ", $task, $task->pid
|
||||
_showtaskstate $task
|
||||
printf " %s\n", $task->name
|
||||
showtaskstack $task
|
||||
|
||||
if $task->task_state == TSTATE_WAIT_SEM
|
||||
printf " waiting on %p ", $task->waitsem
|
||||
_showsemaphore $task->waitsem
|
||||
end
|
||||
|
||||
if $task->task_state != TSTATE_TASK_RUNNING
|
||||
_showtaskregs_v7em $task
|
||||
else
|
||||
_showtaskregs_v7em $task
|
||||
end
|
||||
|
||||
# XXX print registers here
|
||||
end
|
||||
|
||||
document showtask
|
||||
. showtask <TCB pointer>
|
||||
. Print details of a task.
|
||||
end
|
||||
|
||||
define _showtaskstate
|
||||
if $arg0->task_state == TSTATE_TASK_INVALID
|
||||
printf "INVALID"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_TASK_PENDING
|
||||
printf "PENDING"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_TASK_READYTORUN
|
||||
printf "READYTORUN"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_TASK_RUNNING
|
||||
printf "RUNNING"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_TASK_INACTIVE
|
||||
printf "INACTIVE"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_WAIT_SEM
|
||||
printf "WAIT_SEM"
|
||||
end
|
||||
if $arg0->task_state == TSTATE_WAIT_SIG
|
||||
printf "WAIT_SIG"
|
||||
end
|
||||
if $arg0->task_state > TSTATE_WAIT_SIG
|
||||
printf "%d", $arg0->task_state
|
||||
end
|
||||
end
|
||||
|
||||
define showtasks
|
||||
printf "PENDING\n"
|
||||
_showtasklist &g_pendingtasks
|
||||
printf "RUNNABLE\n"
|
||||
_showtasklist &g_readytorun
|
||||
printf "WAITING for Semaphore\n"
|
||||
_showtasklist &g_waitingforsemaphore
|
||||
printf "WAITING for Signal\n"
|
||||
_showtasklist &g_waitingforsignal
|
||||
printf "INACTIVE\n"
|
||||
_showtasklist &g_inactivetasks
|
||||
end
|
||||
|
||||
document showtasks
|
||||
. showtasks
|
||||
. Print a list of all tasks in the system, separated into their respective queues.
|
||||
end
|
||||
|
||||
define my_mem
|
||||
|
||||
set $start = $arg0
|
||||
set $end = $arg1
|
||||
set $cursor = $start
|
||||
|
||||
if $start < $end
|
||||
while $cursor != $end
|
||||
set *$cursor = 0x0000
|
||||
set $cursor = $cursor + 4
|
||||
printf "0x%x of 0x%x\n",$cursor,$end
|
||||
end
|
||||
else
|
||||
while $cursor != $end
|
||||
set *$cursor = 0x0000
|
||||
set $cursor = $cursor - 4
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
#
|
||||
# Setup macros for the BlackMagic debug probe and NuttX.
|
||||
#
|
||||
|
||||
mon swdp_scan
|
||||
attach 1
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,55 @@
|
||||
#
|
||||
# Various PX4-specific macros
|
||||
#
|
||||
source platforms/nuttx/Debug/NuttX
|
||||
source platforms/nuttx/Debug/ARMv7M
|
||||
|
||||
echo Loading PX4 GDB macros. Use 'help px4' for more information.\n
|
||||
|
||||
define px4
|
||||
echo Use 'help px4' for more information.\n
|
||||
end
|
||||
|
||||
document px4
|
||||
. Various macros for working with the PX4 firmware.
|
||||
.
|
||||
. perf
|
||||
. Prints the state of all performance counters.
|
||||
.
|
||||
. Use 'help <macro>' for more specific help.
|
||||
end
|
||||
|
||||
|
||||
define _perf_print
|
||||
set $hdr = (struct perf_ctr_header *)$arg0
|
||||
#printf "%p\n", $hdr
|
||||
printf "%s: ", $hdr->name
|
||||
# PC_COUNT
|
||||
if $hdr->type == 0
|
||||
set $count = (struct perf_ctr_count *)$hdr
|
||||
printf "%llu events\n", $count->event_count
|
||||
end
|
||||
# PC_ELPASED
|
||||
if $hdr->type == 1
|
||||
set $elapsed = (struct perf_ctr_elapsed *)$hdr
|
||||
printf "%llu events, %lluus elapsed, min %lluus, max %lluus\n", $elapsed->event_count, $elapsed->time_total, $elapsed->time_least, $elapsed->time_most
|
||||
end
|
||||
# PC_INTERVAL
|
||||
if $hdr->type == 2
|
||||
set $interval = (struct perf_ctr_interval *)$hdr
|
||||
printf "%llu events, %llu avg, min %lluus max %lluus\n", $interval->event_count, ($interval->time_last - $interval->time_first) / $interval->event_count, $interval->time_least, $interval->time_most
|
||||
end
|
||||
end
|
||||
|
||||
define perf
|
||||
set $ctr = (sq_entry_t *)(perf_counters.head)
|
||||
while $ctr != 0
|
||||
_perf_print $ctr
|
||||
set $ctr = $ctr->flink
|
||||
end
|
||||
end
|
||||
|
||||
document perf
|
||||
. perf
|
||||
. Prints performance counters.
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
# copy the file to .gdbinit in your Firmware tree, and adjust the path
|
||||
# below to match your system
|
||||
# For example:
|
||||
# target extended /dev/serial/by-id/usb-Black_Sphere_Technologies_Black_Magic_Probe_DDE5A1C4-if00
|
||||
# target extended /dev/ttyACM4
|
||||
|
||||
|
||||
monitor swdp_scan
|
||||
attach 1
|
||||
monitor vector_catch disable hard
|
||||
set mem inaccessible-by-default off
|
||||
set print pretty
|
||||
source platforms/nuttx/Debug/PX4
|
||||
@@ -0,0 +1,11 @@
|
||||
define f4_memdump
|
||||
shell mkdir -p /tmp/dump
|
||||
printf "Dumping CCSRAM to /tmp/dump/ccsram\n"
|
||||
dump memory /tmp/dump/ccsram 0x10000000 0x10010000
|
||||
printf "Dumping SRAM to /tmp/dump/sram\n"
|
||||
dump memory /tmp/dump/sram 0x20000000 0x20020000
|
||||
end
|
||||
|
||||
document f4_memdump
|
||||
Dumps the STM32F4 memory to files in /tmp/dump.
|
||||
end
|
||||
@@ -0,0 +1,22 @@
|
||||
# program a bootable device load on a mavstation
|
||||
# To run type openocd -f mavprogram.cfg
|
||||
|
||||
source [find interface/olimex-arm-usb-ocd-h.cfg]
|
||||
source [find px4fmu-board.cfg]
|
||||
|
||||
init
|
||||
halt
|
||||
|
||||
# Find the flash inside this CPU
|
||||
flash probe 0
|
||||
|
||||
# erase it (128 pages) then program and exit
|
||||
|
||||
#flash erase_sector 0 0 127
|
||||
# stm32f1x mass_erase 0
|
||||
|
||||
# It seems that Pat's image has a start address offset of 0x1000 but the vectors need to be at zero, so fixbin.sh moves things around
|
||||
#flash write_bank 0 fixed.bin 0
|
||||
#flash write_image firmware.elf
|
||||
#shutdown
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
target remote :3333
|
||||
|
||||
# Don't let GDB get confused while stepping
|
||||
define hook-step
|
||||
mon cortex_m maskisr on
|
||||
end
|
||||
define hookpost-step
|
||||
mon cortex_m maskisr off
|
||||
end
|
||||
|
||||
mon init
|
||||
mon stm32_init
|
||||
# mon reset halt
|
||||
mon poll
|
||||
mon cortex_m maskisr auto
|
||||
set mem inaccessible-by-default off
|
||||
set print pretty
|
||||
source platforms/nuttx/Debug/PX4
|
||||
|
||||
echo PX4 resumed, press ctrl-c to interrupt\n
|
||||
continue
|
||||
Executable
+270
@@ -0,0 +1,270 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Author: Pavel Kirienko <pavel.kirienko@zubax.com>
|
||||
#
|
||||
# Poor man's sampling profiler for NuttX.
|
||||
#
|
||||
# Usage: Install flamegraph.pl in your PATH, configure your .gdbinit, run the script with proper arguments and go
|
||||
# have a coffee. When you're back, you'll see the flamegraph. Note that frequent calls to GDB significantly
|
||||
# interfere with normal operation of the target, which means that you can't profile real-time tasks with it.
|
||||
# For best results, ensure that the PC is not overloaded, the USB host controller to which the debugger is
|
||||
# connected is not congested. You should also allow the current user to set negative nice values.
|
||||
#
|
||||
# The FlameGraph script can be downloaded from https://github.com/brendangregg/FlameGraph. Thanks Mr. Gregg.
|
||||
#
|
||||
# Requirements: ARM GDB with Python support. You can get one by downloading the sources from
|
||||
# https://launchpad.net/gcc-arm-embedded and building them with correct flags.
|
||||
# Note that Python support is not required if no per-task sampling is needed.
|
||||
#
|
||||
|
||||
set -e
|
||||
root=$(dirname $0)/..
|
||||
|
||||
function die()
|
||||
{
|
||||
echo "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function usage()
|
||||
{
|
||||
echo "Invalid usage. Supported options:"
|
||||
cat $0 | sed -n 's/^\s*--\([^)\*]*\).*/\1/p' # Don't try this at home.
|
||||
exit 1
|
||||
}
|
||||
|
||||
which flamegraph.pl > /dev/null || die "Install flamegraph.pl first"
|
||||
|
||||
#
|
||||
# Parsing the arguments. Read this section for usage info.
|
||||
#
|
||||
nsamples=0
|
||||
sleeptime=0.1 # Doctors recommend 7-8 hours a day
|
||||
taskname=
|
||||
elf=
|
||||
append=0
|
||||
fgfontsize=10
|
||||
fgwidth=1900
|
||||
|
||||
for i in "$@"
|
||||
do
|
||||
case $i in
|
||||
--nsamples=*)
|
||||
nsamples="${i#*=}"
|
||||
;;
|
||||
--sleeptime=*)
|
||||
sleeptime="${i#*=}"
|
||||
;;
|
||||
--taskname=*)
|
||||
taskname="${i#*=}"
|
||||
;;
|
||||
--elf=*)
|
||||
elf="${i#*=}"
|
||||
;;
|
||||
--append)
|
||||
append=1
|
||||
;;
|
||||
--fgfontsize=*)
|
||||
fgfontsize="${i#*=}"
|
||||
;;
|
||||
--fgwidth=*)
|
||||
fgwidth="${i#*=}"
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ -z "$elf" ]] && die "Please specify the ELF file location, e.g.: build/px4fmu-v4_default/src/firmware/nuttx/firmware_nuttx"
|
||||
|
||||
#
|
||||
# Temporary files
|
||||
#
|
||||
stacksfile=/tmp/pmpn-stacks.log
|
||||
foldfile=/tmp/pmpn-folded.txt
|
||||
graphfile=/tmp/pmpn-flamegraph.svg
|
||||
gdberrfile=/tmp/pmpn-gdberr.log
|
||||
|
||||
#
|
||||
# Sampling if requested. Note that if $append is true, the stack file will not be rewritten.
|
||||
#
|
||||
cd $root
|
||||
|
||||
if [[ $nsamples > 0 ]]
|
||||
then
|
||||
[[ $append = 0 ]] && (rm -f $stacksfile; echo "Old stacks removed")
|
||||
|
||||
echo "Sampling the task '$taskname'..."
|
||||
|
||||
for x in $(seq 1 $nsamples)
|
||||
do
|
||||
if [[ "$taskname" = "" ]]
|
||||
then
|
||||
arm-none-eabi-gdb $elf --batch -ex "set print asm-demangle on" -ex bt \
|
||||
2> $gdberrfile \
|
||||
| sed -n 's/\(#.*\)/\1/p' \
|
||||
>> $stacksfile
|
||||
else
|
||||
arm-none-eabi-gdb $elf --batch -ex "set print asm-demangle on" \
|
||||
-ex "source $root/platforms/nuttx/Debug/Nuttx.py" \
|
||||
-ex "show mybt $taskname" \
|
||||
2> $gdberrfile \
|
||||
| sed -n 's/0\.0:\(#.*\)/\1/p' \
|
||||
>> $stacksfile
|
||||
fi
|
||||
echo -e '\n\n' >> $stacksfile
|
||||
echo -ne "\r$x/$nsamples"
|
||||
sleep $sleeptime
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Stacks saved to $stacksfile"
|
||||
else
|
||||
echo "Sampling skipped - set 'nsamples' to re-sample."
|
||||
fi
|
||||
|
||||
#
|
||||
# Folding the stacks.
|
||||
#
|
||||
[ -f $stacksfile ] || die "Where are the stack samples?"
|
||||
|
||||
cat << 'EOF' > /tmp/pmpn-folder.py
|
||||
#
|
||||
# This stack folder correctly handles C++ types.
|
||||
#
|
||||
from __future__ import print_function, division
|
||||
import fileinput, collections, os, sys
|
||||
|
||||
def enforce(x, msg='Invalid input'):
|
||||
if not x:
|
||||
raise Exception(msg)
|
||||
|
||||
def split_first_part_with_parens(line):
|
||||
LBRACES = {'(':'()', '<':'<>', '[':'[]', '{':'{}'}
|
||||
RBRACES = {')':'()', '>':'<>', ']':'[]', '}':'{}'}
|
||||
QUOTES = set(['"', "'"])
|
||||
quotes = collections.defaultdict(bool)
|
||||
braces = collections.defaultdict(int)
|
||||
out = ''
|
||||
for ch in line:
|
||||
out += ch
|
||||
# escape character cancels further processing
|
||||
if ch == '\\':
|
||||
continue
|
||||
# special cases
|
||||
if out.endswith('operator>') or out.endswith('operator>>') or out.endswith('operator->'): # gotta love c++
|
||||
braces['<>'] += 1
|
||||
if out.endswith('operator<') or out.endswith('operator<<'):
|
||||
braces['<>'] -= 1
|
||||
# switching quotes
|
||||
if ch in QUOTES:
|
||||
quotes[ch] = not quotes[ch]
|
||||
# counting parens only when outside quotes
|
||||
if sum(quotes.values()) == 0:
|
||||
if ch in LBRACES.keys():
|
||||
braces[LBRACES[ch]] += 1
|
||||
if ch in RBRACES.keys():
|
||||
braces[RBRACES[ch]] -= 1
|
||||
# sanity check
|
||||
for v in braces.values():
|
||||
enforce(v >= 0, 'Unaligned braces: ' + str(dict(braces)))
|
||||
# termination condition
|
||||
if ch == ' ' and sum(braces.values()) == 0:
|
||||
break
|
||||
out = out.strip()
|
||||
return out, line[len(out):]
|
||||
|
||||
def parse(line):
|
||||
def take_path(line, output):
|
||||
line = line.strip()
|
||||
if line.startswith('at '):
|
||||
line = line[3:].strip()
|
||||
if line:
|
||||
output['file_full_path'] = line.rsplit(':', 1)[0].strip()
|
||||
output['file_base_name'] = os.path.basename(output['file_full_path'])
|
||||
output['line'] = int(line.rsplit(':', 1)[1])
|
||||
return output
|
||||
|
||||
def take_args(line, output):
|
||||
line = line.lstrip()
|
||||
if line[0] == '(':
|
||||
output['args'], line = split_first_part_with_parens(line)
|
||||
return take_path(line.lstrip(), output)
|
||||
|
||||
def take_function(line, output):
|
||||
output['function'], line = split_first_part_with_parens(line.lstrip())
|
||||
return take_args(line.lstrip(), output)
|
||||
|
||||
def take_mem_loc(line, output):
|
||||
line = line.lstrip()
|
||||
if line.startswith('0x'):
|
||||
end = line.find(' ')
|
||||
num = line[:end]
|
||||
output['memloc'] = int(num, 16)
|
||||
line = line[end:].lstrip()
|
||||
end = line.find(' ')
|
||||
enforce(line[:end] == 'in')
|
||||
line = line[end:].lstrip()
|
||||
return take_function(line, output)
|
||||
|
||||
def take_frame_num(line, output):
|
||||
line = line.lstrip()
|
||||
enforce(line[0] == '#')
|
||||
end = line.find(' ')
|
||||
num = line[1:end]
|
||||
output['frame_num'] = int(num)
|
||||
return take_mem_loc(line[end:], output)
|
||||
|
||||
return take_frame_num(line, {})
|
||||
|
||||
stacks = collections.defaultdict(int)
|
||||
current = ''
|
||||
|
||||
stack_tops = collections.defaultdict(int)
|
||||
num_stack_frames = 0
|
||||
|
||||
for idx,line in enumerate(fileinput.input()):
|
||||
try:
|
||||
line = line.strip()
|
||||
if line:
|
||||
inf = parse(line)
|
||||
fun = inf['function']
|
||||
current = (fun + ';' + current) if current else fun
|
||||
|
||||
if inf['frame_num'] == 0:
|
||||
num_stack_frames += 1
|
||||
stack_tops[fun] += 1
|
||||
elif current:
|
||||
stacks[current] += 1
|
||||
current = ''
|
||||
except Exception, ex:
|
||||
print('ERROR (line %d):' % (idx + 1), ex, file=sys.stderr)
|
||||
|
||||
for s, f in sorted(stacks.items(), key=lambda (s, f): s):
|
||||
print(s, f)
|
||||
|
||||
print('Total stack frames:', num_stack_frames, file=sys.stderr)
|
||||
print('Top consumers (distribution of the stack tops):', file=sys.stderr)
|
||||
for name,num in sorted(stack_tops.items(), key=lambda (name, num): num, reverse=True)[:300]:
|
||||
print('% 7.3f%% ' % (100 * num / num_stack_frames), name, file=sys.stderr)
|
||||
EOF
|
||||
|
||||
cat $stacksfile | python /tmp/pmpn-folder.py > $foldfile
|
||||
|
||||
echo "Folded stacks saved to $foldfile"
|
||||
|
||||
#
|
||||
# Graphing.
|
||||
#
|
||||
cat $foldfile | flamegraph.pl --fontsize=$fgfontsize --width=$fgwidth > $graphfile
|
||||
echo "FlameGraph saved to $graphfile"
|
||||
|
||||
# On KDE, xdg-open prefers Gwenview by default, which doesn't handle interactive SVGs, so we need a browser.
|
||||
# The current implementation is hackish and stupid. Somebody, please do something about it.
|
||||
opener=xdg-open
|
||||
which firefox > /dev/null && opener=firefox
|
||||
which google-chrome > /dev/null && opener=google-chrome
|
||||
|
||||
$opener $graphfile
|
||||
@@ -0,0 +1,37 @@
|
||||
# The latest defaults in OpenOCD 0.7.0 are actually prettymuch correct for the px4fmu
|
||||
|
||||
# increase working area to 32KB for faster flash programming
|
||||
set WORKAREASIZE 0x8000
|
||||
|
||||
source [find target/stm32f4x.cfg]
|
||||
|
||||
# needed for px4
|
||||
reset_config trst_only
|
||||
|
||||
proc stm32_reset {} {
|
||||
reset halt
|
||||
# FIXME - needed to init periphs on reset
|
||||
# 0x40023800 RCC base
|
||||
# 0x24 RCC_APB2 0x75933
|
||||
# RCC_APB2 0
|
||||
}
|
||||
|
||||
# perform init that is required on each connection to the target
|
||||
proc stm32_init {} {
|
||||
|
||||
# force jtag to not shutdown during sleep
|
||||
#uint32_t cr = getreg32(STM32_DBGMCU_CR);
|
||||
#cr |= DBGMCU_CR_STANDBY | DBGMCU_CR_STOP | DBGMCU_CR_SLEEP;
|
||||
#putreg32(cr, STM32_DBGMCU_CR);
|
||||
mww 0xe0042004 00000007
|
||||
}
|
||||
|
||||
# if srst is not fitted use SYSRESETREQ to
|
||||
# perform a soft reset
|
||||
cortex_m reset_config sysresetreq
|
||||
|
||||
# Let GDB directly program elf binaries
|
||||
gdb_memory_map enable
|
||||
|
||||
# doesn't work yet
|
||||
gdb_flash_program disable
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
openocd -f interface/olimex-arm-usb-ocd-h.cfg -f $DIR/px4fmu-board.cfg
|
||||
Reference in New Issue
Block a user