#
# 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
