console-dev.de

Home of VsTortoise, VisualHAM, N3D and HEL Library

Author Archive

VsTortoise, Build 24 beta released

Posted by Peter Schraut under Programming, Tools

VsTortoise is an add-in for Microsoft Visual Studio that provides an interface to perform the most common revision control operations directly from inside the Visual Studio IDE. It uses TortoiseSVN to execute the Subversion commands.

Existing users of VsTortoise receive an update notification if  the option “Also check for beta versions” is checked. New users can download it from http://vstortoise.codeplex.com/releases/view/45202.

VsTortoise Build 24 beta changelog:

  • New: Added “Open Modified File…” to Solution Explorer context menus. VsTortoise.SolutionExplorerSelectedItemsOpenModifiedFile command.
  • New: Added “Repository Browser” to Solution Explorer context menus. VsTortoise.SolutionExplorerSelectedItemsRepoBrowser command.
  • New: Added default shortcuts to various VsTortoise commands (ActiveDocumentDiff, ActiveDocumentDiffWithPreviousVersion, ActiveDocumentBlame, ActiveDocumentFindNextModification, ActiveDocumentFindPreviousModification, OpenModifiedFile). In case the particular shortcut is used by another Command already, VsTortoise will not steal the others shortcut and will not provide a default shortcut. Existing users of VsTortoise would need to uninstall / install the new version for this feature to take effect.
  • New: Added VsTortoise.SolutionExplorerSelectedItemsUpdateToRevision command to Solution Explorer context menus. Issue: update to revision
  • New: Added options to enable/disable various VsTortoise menu integrations. Options located under Mainmenu -> Tools -> Options -> VsTortoise -> Integration. Issue: additional code editor context menu
  • New: Added VsTortoise to text/code editor context menu. Issue: additional code editor context menu
  • New: Assigned accelerator keys to VsTortoise menu items. You can open the VsTortoise main menu bar without using the mouse now. Just press Alt key, then use the underlined key in the VsTortoise main menu item to open it. The preferred accelerator key is Alt+S, but this can change when it is used by another menu item already.
  • New: OpenModifiedDocumentDialog: added support for “Ctrl+Enter” within filter textbox and listview to execute “Compare with base”
  • New: OpenModifiedDocumentDialog: added support for PageUp/PageDown keys in filter textbox to navigate the listview, so far only up/down were supported
  • Rem: OpenModifiedDocumentDialog: removed “select all” shortcut from listview, never used this
  • Fix: Fixed Issue: Labels not properly sized in the options page
  • Fix: Fixed Webupdate “Interval in days”. An interval of 1 day should check for updates every day (when day of year changed). In earlier versions, at least 24hrs had to elapse, not taking the day of year in to account.
  • Fix: OpenModifiedDocumentDialog: List uses Windows Explorer visual style information (Windows XP or later)

VsTortoise, Build 23 stable released

Posted by Peter Schraut under Programming, Tools

I just uploaded VsTortoise Build 23. After more than a month of testing Build 22 Beta and fixing just one simple problem, I’m pretty confident this version is stable.

Existing users of VsTortoise should receive an update notification via the webupdate feature. New users can download it from http://vstortoise.codeplex.com/releases/view/43201.

Here is the changelog:

  • Fix: Executing “Blame” through the Solution Explorer on a file opens TortoiseMerge rather than TortoiseBlame.

VsTortoise, Build 22 beta available

Posted by Peter Schraut under Programming, Tools

Today I uploaded VsTortoise build 22 beta to codeplex. The most significant changes are support for Visual Studio 2010RC and the Solution Explorer integration.

Here is the complete changelog:

  • New: Visual Studio 2010 RC support (VsTortoise for Visual Studio 2010 RC screenshots)
  • New: VsTortoise integrates in to Solution Explorer context menus. The following commands can be found when you right click an item in the Solution Explorer now: Update, Commit, Add, Blame, Diff, Diff with previous version, Show Log, Resolve, Revert, Properties.
  • New: OpenModifiedFileDialog: Assigned icons to context menu items
  • Fix: Icons for VsTortoise Commands using a non en-US version of Visual Studio
  • Fix: Hide VsTortoise.ActiveDocumentDiff Command when active document is unmodified instead of disabling it. This mimics the TortoiseSVN explorer context menu behavior.
  • Fix: Don’t disable VsTortoise.ActiveDocumentCommit Command when active document is unmodified. This mimics the TortoiseSVN explorer context menu behavior.
  • Fix: VsTortoise.ActiveDocumentBlame Command is not required to save the active document when executed
  • Fix: VsTortoise.ActiveDocumentProperties Command is not required to save the active document when executed
  • Fix: OpenModifiedFileDialog: Rearranged order of items to more match the TortoiseSVN commits context menu
  • Fix: OpenModifiedFileDialog: No context menu when no item is selected
  • Fix: OpenModifiedFileDialog: Slightly optimized scanning for modifications.
  • Fix: Typo in Webupdate diagnostics output fixed (thanks Björn)
  • Fix: The installer now also detects when Visual Studio is running on Windows Vista / Windows 7. (contributed by Malte)

Download the new version from http://vstortoise.codeplex.com/releases/view/41394

VsTortoise went open source under Apache License 2.0 (Apache) and the source code (C#) and installer scripts are available at codeplex.com now.

I also wrote a document how to build and debug the add-in.

If anyone is interested in the project, feel free to contribute. Patches for the resources problem are welcome. :) I don’t have a german Visual Studio version which I could use to debug and identify the problem, it’s more like poking around in the dark for me.

VsTortoise, need feedback

Posted by Peter Schraut under Programming, Tools

VsTortoise has been downloaded about 100 times so far. One user reported VsTortoise displays a “Resource not found” error when he starts his german Visual Studio 2005 standard edition. I don’t understand what causes the problem yet, since I specified the en-US satellite DLL that comes with VsTortoise as ultimate resource fallback. I wonder whether this is a common problem or occurs on his computer only.

Does VsTortoise works with your (non-english) Visual Studio version?

Please leave a comment with your Visual Studio version, Visual Studio language and if it works for you. (javascript must be enabled to post comments)

Introduction

Over the past months I’ve worked on a TortoiseSVN add-in for Microsoft Visual Studio 2005/2008 called VsTortoise. I work with Visual Studio and TortoiseSVN every day. Since TortoiseSVN integrates in the Windows Explorer, I’ve to switch from Visual Studio to Windows Explorer alot.

This is when VsTortoise add-in comes into play. You can use VsTortoise to invoke TortoiseSVN commands directly from the IDE, you don’t have to switch between Visual Studio and Windows Explorer for a lot of tasks anymore. Beside TortoiseSVN specific commands, VsTortoise provides features to help you to find uncommited modifications in your source code faster.

I’ve created a new page for VsTortoise where you can download it.

Screenshots

VsTortoise ActiveDocument Popup Menu

VsTortoise ActiveProject Popup Menu

VsTortoise Tools Popup Menu

VsTortoise OpenModifiedFile Dialog

VsTortoise commands in Visual Studio Keyboard options

VsTortoise - Options integration in to the standard Microsoft Visual Studio Options dialog

VsTortoise - Webupdate notification

The processors built-in the Nintendo DS, an ARM946E-S and ARM7TDMI, support hardware exceptions such as “Data Abort – accessing invalid memory addresses” and “Undefined Instruction”. The Nintendo DS homebrew library libnds includes exception handling and comes with a default handler that displays the exception type, at which address in the instruction sequence the exception occured and a dump of all registers.

In this article I show what you can do with this information and why it is useful during development.

libnds exceptionTest example

If you installed the libnds examples, you should find the exeptionTest example in devkitPro\examples\nds\debugging\exceptionTest.

Copy&Paste from exceptionTest.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <nds.h>
 
/*---------------------------------------------------------------------------------
 
 The default exception handler displays the exception type - data abort or undefined
 instruction you can relate the exception to your code using
 
    arm-eabi-addr2line -e <elf file> <address>
 
 assuming you built with debug info this will display a source file and a line number
 The address of the instruction is shown as pc, beside the address which faulted
 the rest of the screen is a dump of the registers.
 
---------------------------------------------------------------------------------*/
 
//---------------------------------------------------------------------------------
int main(void)
{
  // install the default exception handler
  defaultExceptionHandler();
 
  // generate an exception
  *(unsigned int*)0xFA = 0x64;
 
  // never comes here
  while(1)
    swiWaitForVBlank();
 
  return 0;
}

When you build and run the example project, it displays:

Guru Meditation Error! - Data Abort

This is actually a fake screenshot. I rebuilt the Guru Meditation Error with printf, because the example didn’t behave in any emulator as on actual hardware. I tested no$gba 2.6a, iDeaS 1.0.3.3 beta and DeSmeME 0.9.5, none of them displayed the error report as on the real Nintendo DS hardware.

guru meditation origin

Guru Meditation is the name of the error that occurred on early versions of the Commodore Amiga computer when they crashed. It is analogous to the “Blue Screen Of Death” in Microsoft Windows operating systems.

The Guru Meditation error message was a black screen with the following animation at the top:

Commodore Amiga - Guru Meditation error message

decode nds guru meditation data abort message

One of the Nintendo DS variants of the Guru Meditation error message is the data abort, which includes the following information:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
     Guru Meditation Error!    
          data abort!          
 
  pc: 02000386 addr: 000000FA  
 
  r0: 020005BD   r8 : 00000000 
  r1: 00000000   r9 : 00000000 
  r2: 00000064   r10: 00000000 
  r3: 000000FA   r11: 00000000 
  r4: 0B000000   r12: 02000000 
  r5: 00000000   sp : 0B003CF8 
  r6: 00000000   lr : 020005B5 
  r7: 00000000   pc : 0200038E 
 
  0B003CF8: 0B000000 02000180  
  0B003D00: 00000000 00000000  
  0B003D08: 00000000 00000000  
  0B003D10: 00000000 00000000  
  0B003D18: 00000000 00000000  
  0B003D20: 00000000 00000000  
  0B003D28: 00000000 00000000  
  0B003D30: 00000000 00000000  
  0B003D38: 00000000 00000000  
  0B003D40: 00000000 00000000

The first line is always “Guru Meditation Error!”, followed by the type of the exception:

1
2
     Guru Meditation Error!    
          data abort!

The libnds default exception handler displays “Data Abort” and “Undefined Instructions” exceptions.

Below the exception type is pc and addr displayed, which I guess is not very obvious for many novice programmers:

1
  pc: 02000386 addr: 000000FA

pc is short for Program Counter. The Program Counter register indicates where the processor is in its instruction sequence. In this case, it’s the address of the instruction that caused the exception.

addr represents the exception address. In this case, writing to the memory address 0xFA:

1
2
  // generate an data abort exception
  *(unsigned int*)0xFA = 0x64;

This again is followed by a register dump:

1
2
3
4
5
6
7
8
  r0: 020005BD   r8 : 00000000 
  r1: 00000000   r9 : 00000000 
  r2: 00000064   r10: 00000000 
  r3: 000000FA   r11: 00000000 
  r4: 0B000000   r12: 02000000 
  r5: 00000000   sp : 0B003CF8 
  r6: 00000000   lr : 020005B5 
  r7: 00000000   pc : 0200038E
  • r0r12 are the so called General Purpose Registers.
  • sp (r13) is the Stack Pointer Register, which indicates the current top of stack memory.
  • lr (r14) represents the Link Register, which holds the return address when the current function completes (it’s the address of the caller).
  • pc (r15) is short for Program Counter. The Program Counter register indicates where the processor is in its instruction sequence.

The last information in the error report is a memory dump of the top 80 bytes of stack memory:

1
2
3
4
5
6
7
8
9
10
  0B003CF8: 0B000000 02000180  
  0B003D00: 00000000 00000000  
  0B003D08: 00000000 00000000  
  0B003D10: 00000000 00000000  
  0B003D18: 00000000 00000000  
  0B003D20: 00000000 00000000  
  0B003D28: 00000000 00000000  
  0B003D30: 00000000 00000000  
  0B003D38: 00000000 00000000  
  0B003D40: 00000000 00000000
  • First column specifies the stack address (0B003CF8)
  • Second column shows the 32bit value from the stack address (0B000000)
  • Third column shows the 32bit value from the stack address + 4 bytes (02000180)

how to make use of all this

This information is informative only when the output file was built with debug information. You can include debug information by adding the -g option (produce debugging information) to CFLAGS in the project makefile:

Copy&Paste from exceptionTest makefile:

1
2
3
4
5
CFLAGS	:= -g -Wall -O2\
		-march=armv5te -mtune=arm946e-s \
 		-fomit-frame-pointer\
		-ffast-math \
		$(ARCH)

devkitARM includes an application named “arm-eabi-addr2line.exe”, located in devkitPro\devkitARM\bin, that can be used to convert addresses into line number/file name pairs. It supports the following command line options:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Usage: arm-eabi-addr2line.exe [option(s)] [addr(s)]
 Convert addresses into line number/file name pairs.
 If no addresses are specified on the command line, they will be read from stdin
 
 The options are:
  @<file>                Read options from <file>
  -b --target=<bfdname>  Set the binary file format
  -e --exe=<executable>  Set the input file name (default is a.out)
  -i --inlines           Unwind inlined functions
  -j --section=<name>    Read section-relative offsets instead of addresses
  -s --basenames         Strip directory names
  -f --functions         Show function names
  -C --demangle[=style]  Demangle function names
  -h --help              Display this information
  -v --version           Display the program's version

We are interested in the address where the Data Abort exception occured, named pc here:

1
2
3
4
     Guru Meditation Error!    
          data abort!          
 
  pc: 02000386 addr: 000000FA

I used the following commandline for conversion:

1
arm-eabi-addr2line.exe -f -C -e  -s -i exceptionTest.elf 02000386

The output is:

1
2
main
exceptionTest.c:23

When you scroll at the beginning of this article, where I present the source code of the exceptionTest example, you see line 23 is inside main(), which is stored in exceptionTest.c and contains the code that writes to the address 0xFA.

conclusion

Make sure to install an exception handler in your projects. It’s very helpful to actually see that an error occured rather than a freeze. libnds makes it enormously easy, it’s just one line of code!

introduction

In this article I show how to set up Microsoft Visual Studio 2008 to use the devkitARM compiler to create applications for the Nintendo DS. After you have read this article, you are able to use Visual Studio 2008 to build, rebuild, clean and execute your Nintendo DS application.

Visual Studio 2008 Express can be downloaded for free at microsoft.com, you want to have the C++ edition: Download Visual C++ 2008 Express Edition.

prerequisites

I assume you already have devkitARM and Visual Studio 2008 installed and both function correctly. I further assume you have a project that you want to migrate from e.g. VisualHAM, Programmers Notepad, etc… to Visual Studio, so I use the hello world example from libnds as existing project reference, which is located in devkitPro\examples\nds\hello_world.

create new visual studio project

We need to create an empty makefile project. You can find this in Visual Studio 2008 under Main menu -> New -> Project:

Visual Studio 2008 - Showing the path to create a new project

Select the project template and target location:

Visual Studio 2008 - Create new project dialog

Rather than selecting the hello_world directory, we specify its parent, because Visual Studio creates a sub directory in the specified location with the specified name. This configuration will save the project file directly in the existing hello_world directory.

Click OK to continue.

configure new project

A new dialog pops up, the New Project Wizard:

Visual Studio 2008 - Create new project wizard

Just click Next here to move on to the next page.

The following page looks more like fun! Settings specified in this New Project Wizard can be changed at any time in the Project Settings as well:

Visual Studio 2008 - Create new project wizard

Please note that none of these settings have influence on the build process: Output, Preprocessor definitions, Include search path, Forced include files, CMR assembly search path and Forced using .NET assemblies.

We set Preprocessor definitions so Visual Studio knows which ARM9/ARM7 #if’s to syntax highlight differently. This is not passed to devkitARM.

We set Include search path so Visual Studio knows where to look for the libnds headers to provide code completion and intellisense, it will not add it to the search path used by devkitARM.

Here is an example what code completion and intellisense look like, first code completion:

Visual Studio 2008 - Code completion (with Visual AssistX add-in activated)

Intellisense:
Visual Studio 2008 - Showing intellisense (with Visual AssistX add-in activated)

The remaining commands in the New Project Wizard are what you typically use when working via the command prompt or shell. Now click Finish to complete the Wizard.

The New Project Wizard closes and Visual Studio shows an empty workspace now:

Visual Studio 2008 - Showing an empty project

add files to project

It’s essential to know that Visual Studio will just invoke the commands specified in the New Project Wizard to build the project.

Compiling and linking your Nintendo DS application is entirely handled by devkitARM, NOT Visual Studio!

Visual Studio is only the graphical front-end that triggers these devkitARM build processes and serves as code editor.

The devkitPro makefile system has a feature to add directories that are supposed to contain source code to the so called SOURCES variable. All source files in those directories, added to this variables, are being compiled and considered during the link process.

Copied from devkitPro\examples\nds\hello_world\Makefile:

1
2
3
4
5
6
7
8
9
10
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET		:=	$(shell basename $(CURDIR))
BUILD		:=	build
SOURCES		:=	gfx source data  
INCLUDES	:=	include build

This means that source files don’t get compiled when you add them to the Visual Studio project, but when you store them in the source directory of your file system!

add files to project, really

Now that you know your files don’t get compiled when you only add them to the Visual Studio Project, let’s really add them. You can do this via drag&drop operations from the Windows Explorer or via the Visual Studio Project context menu:

Visual Studio 2008 - Menu path to add an existing item to a project file

A new “File Open” dialog appears, which you have to navigate to your source files, select them and click Add. I recommend you add the makefile too.

Once done, your Visual Studio Project should look similar to this:

Visual Studio 2008 - Editing a .cpp file

compile project

The magic moment has arrived, to compile the project you can use the Build sub-menu from the mainbar or hit CTRL+SHIFT+B:

Visual Studio 2008 - Showing the Build sub-menu in the mainbar

When everything is configured properly, the project should build just fine and the output looks like:

1
2
3
4
5
6
7
8
9
10
11
12
1>------ Build started: Project: hello_world, Configuration: Debug Win32 ------
1>Performing Makefile project actions
1>main.cpp
1>arm-eabi-g++ -MMD -MP -MF /c/devkitPro/examples/nds/hello_world/build/main.d -g -Wall -O2 -march=armv5te -mtune=arm946e-s -fomit-frame-pointer -ffast-math -mthumb -mthumb-interwork -I/c/devkitPro/examples/nds/hello_world/include -I/c/devkitPro/examples/nds/hello_world/build -I/c/devkitPro/libnds/include -I/c/devkitPro/libnds/include -I/c/devkitPro/examples/nds/hello_world/build -DARM9 -fno-rtti -fno-exceptions -c /c/devkitPro/examples/nds/hello_world/source/main.cpp -o main.o
1>linking hello_world.elf
1>built ... hello_world.arm9
1>Nintendo DS rom tool 1.41 - May  1 2009
1>by Rafael Vuijk, Dave Murphy, Alexei Karpenko
1>built ... hello_world.nds
1>Build log was saved at "file://c:\devkitPro\examples\nds\hello_world\Debug\BuildLog.htm"
1>hello_world - 0 error(s), 0 warning(s)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========

compile project, the Vista barrier

I use Microsoft Vista and have to run Visual Studio 2008 as administrator, otherwise it outputs this error:

1
2
3
4
5
6
7
8
9
1>------ Build started: Project: hello_world, Configuration: Debug Win32 ------
1>Performing Makefile project actions
1>      0 [main] us 0 init_cheap: VirtualAlloc pointer is null, Win32 error 487
1>AllocationBase 0x0, BaseAddress 0x71110000, RegionSize 0x2C0000, State 0x10000
1>c:\devkitPro\msys\bin\make.exe: *** Couldn't reserve space for cygwin's heap, Win32 error 0
1>Project : error PRJ0019: A tool returned an error code from "Performing Makefile project actions"
1>Build log was saved at "file://c:\devkitPro\examples\nds\hello_world\Debug\BuildLog.htm"
1>hello_world - 1 error(s), 0 warning(s)
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

If you encounter the same problem, see if it fixes the problem when you start Visual Studio 2008 with administrator privileges. You can do this when you right-click the Visual Studio 2008 startmenu entry and select “Run as administrator”.

add a rebuild target

We specified “make rebuild” in the New Project Wizard, but this target does not exist by default, so we have to create it first. Basically it should perform:

1
2
make clean
make

Open the makefile and add this below clean:

1
rebuild: clean $(BUILD)

Visual Studio 2008 - Adding a 'rebuild' target to makefile

Select Build -> Rebuild Solution and see the output if it’s performing correctly.

add run target

So far we were able to compile, clean and rebuild the Nintendo DS project out of Visual Studio, but how to run it? We use a software emulator! I use no$gba to run my Game Boy Advance and Nintendo DS applications, so I want Visual Studio to start no$gba as well!

We do this like we did with rebuild, we add a new target to makefile which is responsible to start and pass the filename to the Nintendo DS emulator, in my case no$gba.

Put this below our earlier added rebuild target:

1
2
run: $(BUILD)
	$(DEVKITPRO)/utilities/nogba/nogba.exe $(TARGET).nds

The tab at the beginning at the 2nd line is important and must not be removed, you also want to modify the path to where the emulator of your choice is located.

Visual Studio 2008 - Adding a target to run the output in an emulator

Now open the Visual Studio Project Properties:

Visual Studio 2008 - Project context menu

Select Debugging in the following dialog and fill out the Command and Command Argument text boxes:

Visual Studio 2008 - Project debugging properties

Confirm the dialog with OK and select Debug -> Start Debugging, the project should build and start in the specified emulator!

Visual Studio compatible build messages

All build messages are redirected to the Visual Studio output “Build” pane. Warnings and errors include the filename and line number, but those are in the GCC format and when you double click them, Visual Studio is smart enough to open this file, but not to go to the line.

1
1>c:/devkitPro/examples/nds/hello_world/source/main.cpp:17: error: expected constructor, destructor, or type conversion before 'void'

Double clicking this line opens main.cpp, but keeps the cursor at line 1.

Fortunalety, Jasper ‘cearn’ Vijn once had a regular expression on his website that transforms GCC messages in to Visual Studio format and I was clever enough to make a backup before the information went offline:

1
's/\(\w\+\):\([0-9]\+\):/\1(\2):/'

We can redirect the build output to sed.exe (stream editor), which uses this regular expresssion to transform and output the text so Visual Studio can handle it.

We do this by extending the commands earlier specified in the New Project Wizard.

Visual Studio 2008 - Project context menu

In the following dialog select NMake and modify the settings as shown below:

Visual Studio 2008 - Makefile Project properties

Build Command line:

1
make 2>&1 | sed -e 's/\(\w\+\):\([0-9]\+\):/\1(\2):/'

Rebuild All Command line:

1
make rebuild 2>&1 | sed -e 's/\(\w\+\):\([0-9]\+\):/\1(\2):/'

If you have never seen this 2>&1 before, it’s called command redirection operator and a feature of Microsoft Windows. >& writes the output from one handle to the input of another handle. Next comes |, the pipe operator. It takes the output of one command and directs it into the input of another command.

Confirm the Project Properties Dialog with OK, add an invalid line of code to one source file and build. The error message should look like this now and is clickable:

1
1>c:/devkitPro/examples/nds/hello_world/source/main.cpp(17): error: expected constructor, destructor, or type conversion before 'void'

we are done

Happy compiling!

callstack trace on the nintendo ds

Posted by Peter Schraut under GBA, NDS, Programming

introduction

I was experimenting how I could retrieve a stack trace on the Nintendo DS, using the devkitARM compiler, recently. A callstack is a list of active subroutines of a computer program. This can be used to identify from which code-path a particular subroutine was called, which is enormously helpful when detecting logical errors at run-time.

I tend to flood my code with “ASSERT’s”. ASSERT is used to identify logic errors during program development by implementing the expression argument to evaluate to FALSE only when the program is operating incorrectly and then calling a function to report the error:

1
2
3
4
5
6
7
8
9
void Vector2Add(Vector2 *dest, const Vector2 *v0, const Vector2 *v1)
{
  ASSERT(dest != NULL);
  ASSERT(v0 != NULL);
  ASSERT(v1 != NULL);
 
  dest->x = v0->x + v1->x;
  dest->y = v0->y + v1->y;
}

When any of the incoming arguments point to NULL, ASSERT will call a subroutine to report the error. Typically the report includes the filename, line number and functionname. This information is helpful, but lacks of information from where the function was called.

I use Vector2Add all over the place, how should I know what part in my program code does not work, when I only know at some point a NULL argument is passed to Vector2Add?

Right, I need to know from where Vector2Add was called, I need a callstack trace!

my earlier instrumentation approach

Around 2005-2006 I implemented a callstack trace feature in HEL Library. HEL Library is a middleware solution to ease development of Game Boy Advance titles.

The callstack trace was displayed together with an assertion-report, that looked like:
HEL Library assertion report

HEL Library callstack trace

Back then I used function instrumentation (-finstrument-functions) to build a list of subroutine calls at run-time. It was basically a callback that was called for every function that is about to be entered (__cyg_profile_func_enter) and left (__cyg_profile_func_exit).

This was a huge overhead and slowed down program-execution dramatically, but the callstack trace was so precious, that the performance penalty didn’t matter for the debug library.

fail does not mean to stop

When I started the Nintendo DS version, one goal was to use an approach which does not have any impact on run-time performance! I have tried several libraries, unfortunalety I didn’t find anything that worked for me.

This includes libunwind, backtrace, several GCC builtin-keywords to get return addresses of different calldepths, just to name a few.

I gave all up. It was either a never ending story to integrate the library in to the project or headers / functions were missing in libraries that come with devkitARM.

hacking to success, then fail

Rather than giving up, I decided to accept it as challenge and come up with my own callstack trace code. It was a long and rocky journey, but at the end I had something that worked suprisingly well.

What I do is basically pretty simple, I interpret instructions of interest to simulate the beaviour of the Program Counter (PC) and Stack Pointer (SP). The Program Counter indicates where the program is in its intruction sequence. Stack Pointer indicates the current top of stack memory. The stack this is where local variables are located.

Armed with my own Program Counter and Stack Pointer variables, I can traverse program code and react when Program Counter is assigned a new value, which basically means to jump to a different address and continue to operate there.

Example:

1
2
3
4
5
6
7
8
9
10
void FirstFunc()
{
  SecondFunc();
}
 
void SecondFunc()
{
  // Program Counter is located here  <--
  ThirdFunc();
}

Let’s presume the Program Counter is located in SecondFunc, which was previously called by FirstFunc.

The thumb assembler version of this looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
FirstFunc:
  push {lr}       ; Push Link Register (LR) on stack
  sub sp, sp, #4  ; Update Stack Pointer by 4bytes because LR was pushed
  bl  SecondFunc  ; Call to SecondFunc()
  add sp, sp, #4  ; Update Stack Pointer to point to pushed LR again
  pop {pc}        ; Pop LR from stack and store in Program Counter, this will return to caller
 
SecondFunc:
  push {lr}       ; Push Link Register (LR) on stack
  sub sp, sp, #4  ; Update Stack Pointer by 4bytes because LR was pushed
  bl  ThirdFunc   ; Call to ThirdFunc()
  add sp, sp, #4  ; Update Stack Pointer to point to pushed LR again
  pop {pc}        ; Pop LR from stack and store in Program Counter, this will return to caller

I need to get my hands on the Link Register (LR) to return to SecondFunc’s caller (*1). Since LR was being pushed on the stack, I know where to look! What is left is to traverse the program code in reversed order and interpret the “sub” and “push” instructions.

Moving the Program Counter in reversed order, which actually would be a backtrace imo, didn’t work out for some reasons and I wasn’t able to solve those problems. It did, however, worked in some cases, unfortunalety too unreliable.

when in doubt, try it out

I stayed away from this problem for week and then had the idea why not trying to simulate the Program Counter the same way it would walk along when it continues program execution normally. It means rather then traversing program code backwards, I tried what happens, when I forward advance PC. Now I have to interpret “add” and “pop” instructions and this works suprisingly well!

From here on it took a few hours to come up with a version that is robust enough to work in all places that I tested with my code base, which consists of both, C and C++ source code.

Obviously it does not mean it always works in every sitiuation with all source code in the world. For example, optimization level -O0 generates so many unconditional branches and places return code all over the place, that the Stacktrace function fails. It supports thumb code only and has problens with hand written/optimized assembler routines (when LR is not push’ed and PC not pop’ed).

I added a couple of more instruction Stacktrace looks out for:

1
2
3
4
5
pop {...pc} ; adjust PC to the pop'ed value
add sp, nn  ; adjust SP by nn
sub sp, nn  ; adjust SP by nn
b   ofs     ; set PC to the addr PC+ofs
add sp, rn  ;  adjust SP by the value rn points to

The unconditional branch instruction “b” is from hell, because it could lead to an infinite loop in Stacktrace, such as while(1) {}. For this reason I added two special cases:

  • Limit how many instructions are allowed to touch
  • Branch only to the target address when it’s different from PC

When any of the conditions evaluates to true, Stacktrace aborts and outputs the callstack only to this position. It will, however, report that an error occured.

Support for the unconditional branch was important, because the compiler generates code like this sometimes:

1
2
3
4
5
6
7
8
9
10
11
12
ExampleFunc:
  push   {lr}
  ...
  tst    r0, #2
  bne    .L92 ; if r0 is not 2 then jump to L92
  ...
.L90:
  pop    {pc} ; returns to caller
 
.L92:
  bl     AnotherFunction
  b      .L90 ; jump to L90 (above)

When we return from AnotherFunction we must jump to L90 to pop the return address from the stack and return to the caller. This is the reason why I added support for unconditional branches. I haven’t noticed other branches that are of importance for Stacktrace yet.

At this point of time I was able to return to each function-caller and have the return addresses, e.g:

Stacktrace output with return addresses only

resolving function names by hand

In order to map the memory address to an actual function, we have to look up the address in the so called Map file. Map files are typically plain text files that indicate the relative offsets of functions for a given version of a compiled binary. This file is generated by the linker when you pass -Map to it, here is a part of it:

1
2
3
4
5
6
7
.text   0x02000310    0xdf28
.text   0x02000380    0x1ac main.o
        0x02000394      PrintStacktrace
        0x0200046c      FirstFunc
        0x0200040c      ThirdFunc
        0x020004c8      main
        0x02000450      SecondFunc

Line 1 specifies where the .text section (program code) starts and its size. Line 2 specifies the start address of the object file main.o followed by all functions it contains. While writing this article, I have learned static functions are not listed in the map file.

In order to find the function name of the memory address 0×02000458 (from screenshot above), we have to check in which range it is located. But it would be too simple when the .map file has everything sorted by addresses, so we have to do this instead.

Here is a sorted list by addresses:

1
2
3
4
5
6
.text   0x02000380    0x1ac main.o
        0x02000394      PrintStacktrace
        0x0200040c      ThirdFunc
        0x02000450      SecondFunc
        0x0200046c      FirstFunc
        0x020004c8      main

The memory address 0×02000458 is between:

1
2
        0x02000450      SecondFunc
        0x0200046c      FirstFunc

So 0×02000458 belongs to SecondFunc. While this approach seems to work, it is complicated and really only works when you have the exact .map file available that was used to build the project. You can’t use a .map file of a different version of your code, this is very unlikely to work.

It would be so much better when the actual function name could be displayed instead!

what is your function name, dear memory address

Fortunalety, the compiler has an option to insert function names in plain-text infront of each function. This can be activated by adding -mpoke-function-name to the CFLAGS variable in your makefile.

But it not only inserts the name, it also inserts a bit-pattern to recognize that there is a function name as well as a relative offset to the name from this pattern.

What Stacktrace function does, it takes a memory address and then decreases this address until it detects the bit-pattern of the function name and we have it!

Stacktrace without functio names and return addresses

Well, there is of course a special case again. When -mpoke-function-name has not been specified, the function name bit-pattern is missing, thus Stacktrace would try to find it until it reaches the start of the text section. While this makes little sense, I added a limit how many 32bit words the “GetFunctionnameTag” function is allowed to touch before it returns “did not find”.

how to integrate in your own project

The stacktrace.zip archive contains the entire source code, you only need those two files:

  • stacktrace.h
  • stacktrace.c

Copy them to your source code directory. Then open the makefile file which should be located in your project directory also. You have to adjust the following variables:

1
2
ARCH   := -mthumb -mthumb-interwork
CFLAGS := -O2 -mpoke-function-name

Now do a rebuild, rather than a regular build. You can do this by performing a “make clean” first, then a “make”. Don’t forget to include stacktrace.h in that file where you want to use Stacktrace.

Optimization level -O2 worked best with Stacktrace so far. When you use more aggressive optimization levels the compiler also starts to inline code heavily, which can be quite confusing when the Stacktrace output does not reflect what you see in your high level code.

stacktrace interface and usage

The usage of Stacktrace function is fairly simple.

All it expects is an array of StacktraceEntry elements and the count of it. Here is the interface from stacktrace.h:

1
2
3
4
5
6
7
typedef struct
{
  unsigned int addr;  //!< Return address
  const char *name;   //!< Name of function
} StacktraceEntry;
 
unsigned int Stacktrace(StacktraceEntry* dest, unsigned int count);

Use it like this:

1
2
3
4
5
6
7
8
9
10
11
12
void OutputStacktrace()
{
  unsigned int n;
  unsigned int count;
  StacktraceEntry entries[32]; // allocate memory for 32 stacktrace entries
 
  // get only the 10 deepest stacktrace entries
  count = Stacktrace(entries, 10);
 
  for (n=0; n<count; ++n)
    printf("0x%08x: %s", entries[n].addr, entries[n].name);
}

improving stacktrace

If you want to improve Stacktrace, I recommend to read through the comments in stacktrace.c and enable STACKTRACE_VERBOSE, by setting it to 1.

This will, as far as OutputDebugString (also in stacktrace.c) uses a debug message type your Nintendo DS software emulator understands, output what instructions Stacktrace comes across and interprets.

I’ve added an opcode structure for all instructions Stacktrace interprets as well as print-routines to dump the contents.

I have used no$gba during development, which was of great help because of its “debugger“. Usually I yell about only see the assembler code in the shareware version of no$gba, but for this project it was exactly what I needed :)

conclusion

The Stacktrace function…

  • works suprisingly well for being such a hack
  • does not need debugging information/symbols
  • it runs directly on the target device, not needed to be connected to GDB or a similar debugger
  • does not come with any run-time performance penalty
  • works best with optimization level -O2
  • is most informative when adding -mpoke-function-name to CFLAGS in your makefile
  • comes with full documented source code
  • uses very little Nintendo DS specifics, except the text section start and length (portable to other ARM devices?)
  • works with thumb code only
  • does not detect when switching from thumb to arm mode
  • depends on LR / PC being push’ed and pop’ed, hand-written assembler routine can cause trouble
  • is tested in/with my code base only

frequently asked questions (faq)

Q: Why do I see so weird function names, like _ZN13DEMOPARTQUEUE9CutToPartEj and the like?

A: Because in C++, the function names go through name mangling. Name mangling is a technique used to solve various problems caused by the need to resolve unique names for programming entities.

download

Dear visiter, 100% OFF on console-dev.de, take your chance and download stacktrace.zip now! ;)

what I didn’t tell you yet

*1) LR does not always contains the address from where it was being called, but it holds the address where to return when the function completes. When using code optimization levels above -O2 the compiler e.g. generates code that does not return to functions where it was the last instruction involved anyway.

In my previous post sine approximation with fixed point math I wrote the output precision of f32sin is getting worse when the incoming radians parameter grows and guessed it is due to using fixed point.

Well, Jasper “cearn” Vijn suggested to use a higher resolution fixed point format for 1.0f/(2*pi) and this solves the problem indeed! But it does not end here!

He also created an awesome article about the ins and outs of fixed point sine approximation and presents a couple of highly precise and optimized routines.

Head over to Jasper Vijn’s document Another fast fixed-point sine approximation now!

I’ll post a corrected version of the f32sin routine from my previous post for completeness, but I highly encourage to vists Jasper’s site and get one of his!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Gets sin of specified radians.
// Input and output format is fixed point sign.19.12
int32 f32sin(int32 radians)
{
  const int32 PI2_PRECISION_BITS = 20;
  const int32 PI2_RECIPROCAL     = (1.0f / (2*M_PI)) * (1<<PI2_PRECISION_BITS);
 
  // Divide incoming radians by 2*PI, so it is a "normalized"
  // angle between -1 and +1, where 1 equals 2*PI (360 degree).
  int32 value = (radians * PI2_RECIPROCAL) >> PI2_PRECISION_BITS;
 
  // Now that we have our normalized angle,
  // we just discard all numbers which are not in the range -1..+1.
  // Since this is fixed point, a simple AND operation can be used.
  value &= floattof32(1)-1;
 
  // Always wrap normalized angle to -0.5(-PI)..+0.5(+PI)
  if(value > floattof32(0.5f))
    value -= floattof32(1); // subtract 2*PI in normalized form
  else
    if(value < floattof32(-0.5f))
      value += floattof32(1); // add 2*PI in normalized form
 
  // Convert normalized angle (-1..1) back to radians (-2*PI..2*PI)
  value = f32mul(value, floattof32(2*M_PI));
 
  // Approximate sin value
  const int32 B = floattof32(1.2732395447351f);
  const int32 C = floattof32(-0.405284734569f);
  return f32mul(B, value) + f32mul(f32mul(C, value), f32abs(value));
}
This site uses a Hackadelic PlugIn, Hackadelic SEO Table Of Contents 1.6.0.