world leader in high performance signal processing
Trace: » snippets

Code Snippets for Visual DSP

Here's some VBScript code snippets that can be used in Visual DSP when debugging Linux.

Note: These are not supported nor endorsed by the Linux team. If you have questions, ask seek help from the Visual DSP team at Analog.com, not from the Blackfin Linux Team. These snippets are provided for your amusement only.

Setup

Everything else relies on these intro bits.

Common

Execution

cmd /c cscript your-script.vbs

Include File

Option Explicit
Const ForReading = 1
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
Private Sub IncludeFileAbs(ByVal FileName)
	Dim f: Set f = fso.OpenTextFile(FileName, ForReading)
	ExecuteGlobal f.ReadAll()
	f.Close()
End Sub
IncludeFileAbs "common.vbs"

Common Functions/Objects

Option Explicit

Dim debugit: debugit = 0
Dim filetxt: Set filetxt = fso.CreateTextFile("logs\trace-" & (Now() * Rnd()) & ".txt", True)

Sub Output(msg): filetxt.Write(msg): WScript.StdOut.Write(msg): End Sub
Sub Echo(msg): Output msg & vbCrLf: End Sub
Sub DOutput(msg): If (debugit) Then Output(msg): End If: End Sub
Sub DEcho(msg): If (debugit) Then Echo(msg): End If: End Sub

Dim idde: Set idde = CreateObject("VisualDSP.ADspApplication")
Dim proc: Set proc = idde.ActiveSession.ActiveProcessor

' From VisualDSP.adspDebugState
Const stateHalted = 3
'From VisualDSP.adspBreakpointType
Const breakpointPublic = 0
Public Function FmtAddr(ByRef addr)
	FmtAddr = CStr(Hex(addr))
	FmtAddr = "0x" & LCase(Left("00000000", 8 - Len(FmtAddr)) & FmtAddr)
End Function
Dim ihalted
Public Sub MaybeHalt()
	ihalted = (proc.State <> stateHalted)
	If (ihalted) Then proc.Halt True
End Sub
Public Sub MaybeRun()
	If (ihalted) Then proc.Run False
End Sub

Workarounds

Some things appear to be broken in Visual DSP, so we work around limitations here.

32bit Reads

Private Function GB(ByRef addr): GB = proc.EvaluateToLong("$dm32(0x" & Hex(addr) & ")"): End Function
Public Function Get4Byte(ByRef addr)
	Dim a: a = GB(addr+0)
	Dim b: b = GB(addr+1)
	Dim c: c = GB(addr+2)
	Dim d: d = GB(addr+3)
	If (d > &H80) Then
		Get4Byte = ((&HFF-a) + (&HFF-b)*&H100 + (&HFF-c)*&H10000 + (&HFF-d)*&H1000000) + 1
	Else
		Get4Byte = a + b*&H100 + c*&H10000 + d*&H1000000
	End If
End Function

Setting Registers

Dim register_list: Set register_list = proc.RegisterList
Dim tmp: Set tmp = register_list("WPDA0").Value
tmp.Value = &H1234
register_list("WPDA0").Value = tmp

Abstract Features

Quick functions to avoid walking object paths.

Public Function GetSymAddr(ByRef sym)
	GetSymAddr = CLng(proc.MemoryTypeList.FindSymbol(sym)(0).Address)
End Function
Public Sub SetBreakpointAtAddr(ByRef addr)
	proc.BreakpointList.SetBreakpointAtAddress breakpointPublic, addr, 0, "", False, True, 0
End Sub
Public Sub ClearAllBreakpoints()
	Dim i
	For i = 1 To proc.BreakpointList.Count
		DEcho "Deleted breakpoint at " & FmtAddr(proc.BreakpointList(0).Address)
		proc.BreakpointList.RemoveBreakpointByIndex 0
	Next
End Sub

Reading Kernel Maps

Equivalent of `cat /proc/maps`. Requires kernel symbols be loaded. Note some offsets for structs may need tweaking.

'TODO: make sure symbols are loaded

Private Function rb_all_left(node)
	Dim n
	rb_all_left = node
	Do while (1)
		DOutput FmtAddr(rb_all_left) & ".->"
		n = Get4Byte(rb_all_left + 3 * 4) 'n->rb_left
		If (n = 0) Then Exit Do
		rb_all_left = n
	Loop
End Function

Private Function rb_first(root)
	'struct rb_node {
	'	0	struct rb_node *rb_parent;
	'	4	int color; /* red = 0, black = 1 */
	'	8	struct rb_node *rb_right;
	'	12	struct rb_node *rb_left;
	'}
	'struct rb_root { struct rb_node *rb_node; }
	DOutput " ["
	rb_first = rb_all_left(Get4Byte(root + 0)) 'rb_root->rb_node
	DEcho "done]"
End Function

Private Function rb_next(node)
	DOutput " [" & FmtAddr(node) & "->"

	' If we have right hand child, go down and then all the way left
	rb_next = Get4Byte(node + 2 * 4) 'node->rb_right
	If (rb_next <> 0) Then
		rb_next = rb_all_left(rb_next)
		DEcho "done!]"
		Exit Function
	End If
	
	' No right hand children, so go up the tree
	rb_next = node 'node->rb_parent
	If (rb_next = 0) Then
		DEcho "DonE]"
		Exit Function
	End If
	Dim parent, parent_right
	Do While (1)
		DOutput FmtAddr(rb_next) & "->"
		parent = Get4Byte(rb_next + 0)
		If (parent = 0) Then Exit Do
		parent_right = Get4Byte(parent + 2 * 4) 'parent->rb_right
		If (parent_right <> rb_next) Then Exit Do
		rb_next = parent
	Loop
	rb_next = Get4Byte(rb_next + 0)
	DEcho FmtAddr(rb_next) & "]"
End Function

Private Function FlagOrDash(b, f): If (b) Then FlagOrDash = f: Else: FlagOrDash = "-": End If: End Function
Private Function FmtFlags(flags)
	'Note: vbscript "And" is bitwise, not logical
	flags = CLng(flags)
	FmtFlags = ""
	FmtFlags = FmtFlags & FlagOrDash(flags And &H1, "r")
	FmtFlags = FmtFlags & FlagOrDash(flags And &H2, "w")
	FmtFlags = FmtFlags & FlagOrDash(flags And &H4, "x")
	If (flags And &H80) Then
		If (flags And &H8) Then
			FmtFlags = FmtFlags & "S"
		Else
			FmtFlags = FmtFlags & "s"
		End If
	Else
		FmtFlags = FmtFlags & "p"
	End If
End Function

Public Sub DumpVMAList()
	Dim FileArr
	FileArr = Array( _
		"18",  "busybox", _
		"38",  "init", _
		"197", "ld-uClibc.so", _
		"200", "libcrypt.so", _
		"202", "libdl.so", _
		"204", "libm.so", _
		"308", "libpthread.so", _
		"316", "libc.so" _
	)
	Dim vma, rb
	rb = GetSymAddr("_nommu_vma_tree")
	Echo "vma tree is based at " & FmtAddr(rb)
	rb = rb_first(rb)
	Do While (rb <> 0)
		Dim vm_start, vm_end, vm_flags
		vma = rb - 6 * 4 ' vm_rb is 7 member in vm_area_struct; previous 6 should each be 4 bytes in size ...
		vm_start = Get4Byte(vma + 1 * 4)
		vm_end   = Get4Byte(vma + 2 * 4)
		vm_flags = Get4Byte(vma + 5 * 4)
		
		Dim vm_file, f_dentry, d_inode, i_ino, i_sb, s_dev, major, minor
		vm_file  = Get4Byte(vma + 76)
		If (vm_file <> 0) Then
			f_dentry = Get4Byte(vm_file + 8)
			d_inode  = Get4Byte(f_dentry + 8)
			i_ino    = Get4Byte(d_inode + 32)
			i_sb     = Get4Byte(d_inode + 144)
			s_dev    = Get4Byte(i_sb + 8)
			major    = s_dev / (2 ^ 20)
			minor    = s_dev Mod (2 ^ 20)
		Else
			i_ino    = 0
			major    = 0
			minor    = 0
		End If
		Output "vma"
		DOutput " " & FmtAddr(vma) & " [" & FmtAddr(rb) & "]"
		Output ": " & FmtAddr(vm_start) & "-" & FmtAddr(vm_end) & " " & FmtFlags(vm_flags)
		major = Hex(major)
		If (Len(major) <> 2) Then major = "0" & major
		minor = Hex(minor)
		If (Len(minor) <> 2) Then minor = "0" & minor
		Output " " & major & ":" & minor & " " & i_ino
		Dim i: i = LBound(FileArr)
		Do While (i <= UBound(FileArr))
			If (FileArr(i) = CStr(i_ino)) Then
				Output " " & FileArr(i+1)
				Exit Do
			End If
			i = i + 2
		Loop
		Echo ""
		rb = rb_next(rb)
	Loop
End Sub

MaybeHalt
DumpVMAList
MaybeRun
WScript.StdIn.Read 1
WScript.Quit 0

Automating Breakpoints

When a breakpoint is hit, dump some info and resume.

Dim ExecBase: ExecBase = &H1A48000
Dim MallocAddr: MallocAddr = ExecBase + &H6574
Dim FreeAddr: FreeAddr = ExecBase + &H66D8

MaybeHalt
ClearAllBreakpoints
Echo "Breakpoints at " & FmtAddr(GetSymAddr("_trap_c")) & " " & FmtAddr(MallocAddr) & " " & FmtAddr(FreeAddr)
SetBreakpointAtAddr GetSymAddr("_trap_c")
SetBreakPointAtAddr MallocAddr
SetBreakPointAtAddr FreeAddr
MaybeRun

WScript.ConnectObject proc, "proc_"

Sub ValidateHeap(HeapStart, HeapEnd)
	Dim current, tempsize, header_size
	HeapStart = CLng(HeapStart)
	HeapEnd = CLng(HeapEnd)
	header_size = 8
	current = HeapStart
	Do While (current < HeapEnd)
		'tempsize = CLng(proc.EvaluateToLong("$dm32(0x" & Hex(current) & ")"))
		tempsize = Get4Byte(current)
		if (tempsize < 0) Then tempsize = tempsize * -1
		Output "[" & FmtAddr(current) & " " & tempsize & "]"
		current = current + tempsize + header_size
		If (tempsize = 0 or tempsize mod 4 > 0) Then
			Output " Tempsize sucks!?"
			Exit Do
		End If
	Loop
	Echo ""
End Sub

Sub proc_OnHalted(ReasonCode, HaltReason)
	Dim register_list: Set register_list = proc.RegisterList
	Dim kernel_end: kernel_end = GetSymAddr("__end")
	Dim trap_c: trap_c = GetSymAddr("_trap_c")
	Dim PC: PC = CLng(register_list("PC").Value)
	
	'If we halted ourselves in the debugger, dont auto run ... address here is arbitrary but tends to be after "idle" in kernel idle func
	If (PC = &H1D14) Then Exit Sub
	
	If (PC = MallocAddr) Then
		' Halt on call to user's malloc and verify the heap is sane
		Dim heap_start: heap_start = CLng(register_list("R1").Value)
		Dim heap_size: heap_size = CLng(register_list("R2").Value)
		Dim tmp: Set tmp = register_list("WPDA0").Value
		
		tmp.Value = heap_start
		register_list("WPDA0").Value = tmp
		tmp.Value = heap_start + heap_size
		register_list("WPDA1").Value = tmp
		tmp.Value = &H1DCD
		register_list("WPDACTL").Value = tmp
		
		Echo "Watching heap from " & FmtAddr(heap_start) & " to " & FmtAddr(heap_start + heap_size) & " (size=" & heap_size & " bytes)"
		
		proc.Run False
		Exit Sub
	End If
	
	Output Now() & " "
	
	If (PC < kernel_end) Then
		Output "Kernel: " & FmtAddr(PC)
	Else
		Output "User: " & FmtAddr(PC)
	End If
	Output " "
	
	' Output disassembly of this instruction
	Dim words_converted, memory_type_list, memory_type
	Set memory_type_list = proc.MemoryTypeList
	Set memory_type = memory_type_list("BLACKFIN Memory")
	Output "{ " & memory_type.GetFormattedMemory(PC, 1, "Assembly", words_converted)(0) & " } "
	
	'Echo "Heap " & Hex(register_list("WPDA0").Value) & " -> " & Hex(register_list("WPDA1").Value)
	ValidateHeap register_list("WPDA0").Value, register_list("WPDA1").Value
	
	Dim i
	For i = 0 to 5
		Output "P" & i & ": " & FmtAddr(register_list("P"&i).Value) & " "
	Next
	For i = 0 to 0
		Output "R" & i & ": " & FmtAddr(register_list("R"&i).Value) & " "
	Next
	Echo ""
	
	'if we halted in kernel, dont want to continue automatically
	If (PC <> trap_c) Then proc.Run False
End Sub

' hang out in a loop
Do While (True)
	WScript.Sleep 250
Loop