#144956 - Lazy1 - Fri Nov 09, 2007 8:24 pm
In my random testings I had wolf3d freeze on me, unfortunately I have no idea where it could be happening but I have an idea.
Is it possible to set up an interrupt so if I press L+R+Start it will write the address of the interrupted instruction to the console?
That way I could just use arm-eabi-addr2line to locate the offending code.
How would I go about doing that?
#144959 - DekuTree64 - Fri Nov 09, 2007 8:48 pm
I can think of a good way, and an evil way.
The good way is to write your interrupt handler in assembly, count how many things get pushed onto the stack by the BIOS handler and the libnds dispatcher, so you can grab the final return address off the stack directly.
Here is the evil way:
Code: |
void KeyInterruptHandler()
{
u32 evilArray[1];
for (int i = 0; i < 16; i++)
{
iprintf("%x\n", evilArray[i]);
}
} |
The idea being that the array goes onto the stack, and then you read off the end of it into whatever else has recently been pushed onto the stack. Then look through the values for ones that look like code addresses. Although you may need to go more than 16 values into the stack to get to the return address, I'm not sure.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#144963 - Lazy1 - Fri Nov 09, 2007 9:33 pm
Interesting...
The evil way does work but as you said it takes a bit of fishing to find the right address.
#144990 - Lazy1 - Sat Nov 10, 2007 3:29 am
That's odd, I found a few valid code addresses in there.
How do I do the less-evil one?
#145000 - DekuTree64 - Sat Nov 10, 2007 9:31 am
Hmm, looks like the libnds dispatcher switches to the user stack, so the evil method doesn't even work... but on the other hand it makes the good way a bit easier. I'm pretty sure the BIOS handler just pushes 6 values onto the stack (r0-r3, r12, lr), so we want to the last one there. Here's a quick attempt at a function to do it:
Code: |
.arm
.align 2
.global KeyInterruptHandler
KeyInterruptHandler:
mrs r1, cpsr @ Grab the current status register
bic r0, r1, #0x1f @ Clear mode field
orr r0, r0, #0x12 @ Set to IRQ mode
msr cpsr, r0 @ Write to status register
ldr r0, [sp, #5 * 4] @ Load the final return address from the IRQ stack
msr cpsr, r1 @ Switch back to the original mode
b PrintReturnAddress @ Call a function to print the value |
Then PrintReturnAddress can just be a regular C function that takes one u32 argument (i.e. the value in r0). That also uses a fun little trick of just jumping to another function, leaving it to return to your return address. Nothing too special, just saves a couple of instructions... but make sure the function is also compiled as ARM code.
_________________
___________
The best optimization is to do nothing at all.
Therefore a fully optimized program doesn't exist.
-Deku
#145010 - Lazy1 - Sat Nov 10, 2007 12:17 pm
Thanks for your help on this though it does not seem to give a valid address, I have seen:
0x00000000
0x00000001
0x000015EF
0x40000000
0xA3D71910
...
...
ect...
#145080 - Lazy1 - Sun Nov 11, 2007 7:36 am
Just a side note:
If someone can get this working I will give you one of my Orange Box gifts (HL2 or HL2:EP1).
#145086 - simonjhall - Sun Nov 11, 2007 11:54 am
Have you tried the libnds exception handler? Try running that, and seeing if your screen suddenly turns bright red with a load of registers on it!
There's a chance your (crashing) code may be doing something illegal, and running that couldn't hurt...
_________________
Big thanks to everyone who donated for Quake2
#145101 - HyperHacker - Sun Nov 11, 2007 5:20 pm
Yeah, during development (unless you've written your own exception handler of course) it's generally a good idea to stick a call to defaultExceptionHandler() at the beginning of main() so you get error screens instead of just weird behaviour. If you aren't using the console, though, you may want to remove it for the release build as it will bring that in which adds a fair bit of size to your binary.
If you're up to it, building your own handler is even better, since you can display more useful information specific to your program, try to dump it to a file, etc. I'd post mine, but I'm a bit short on time here, so PM me if you want it. It dumps the stack (though I think that's broken <_<), registers, IPC, and a few globals to help tell just what was going on.
_________________
I'm a PSP hacker now, but I still <3 DS.
#145140 - Lazy1 - Mon Nov 12, 2007 12:11 am
I am using the default exception handler but the problem is not a crash.
The problem may lie in the timing code but I'm not sure, if the game is stuck in a loop waiting for the time to catch up then this would help me alot.
It still could be getting locked up elsewhere, which is why this code would help me alot.
#145148 - HyperHacker - Mon Nov 12, 2007 12:30 am
Well the CPU won't just lock up solid (barring some hardware defect), so if it's not triggering an exception, it has to be stuck in a loop somewhere. What you can do is throughout your main loop, set a global variable to __LINE__, and then during VBlank, print the variable on-screen. You could also put it in IPC so ARM7 can, for example, play tones of that frequency times whatever, or you could use a trick I like to use in PC apps:
#define WRITELINE fprintf(logfile, "%s:%u\n", __FILE__, __LINE__)
Then just drop WRITELINE; in various places in your code, open the file before calling it, and you'll get a log of what "checkpoints" were passed in what order. (On PC I write it to the console, but same idea.) Only caveat is on the DS, fflush() seems broken (for me at least), so you have to actually close the file to get it to flush to disk. It's hard to close a file when your app is hung, but you could do it in VBlank if a certain button is pressed. (That, and the logfile could fill up a small card quickly, and you definitely don't want to be writing to flash that much in your release version!)
Also, don't forget you have two CPUs. A trick I use to detect and report endless loops is during each iteration of the main loop, and periodically in long loops, send the other CPU a FIFO message. Each CPU keeps a counter, starting at say 120, decrements it in VBlank, and refills it when it gets this message. If it hits zero you can assume the other CPU has locked up and report the problem. Just don't forget to send the message during things that take more than 120 frames to do. If debugging on an emulator that doesn't handle FIFO properly you can just set the counter to some huge value, so that it won't hit zero for a few hours. (I've found this to be necessary on No$GBA, but I'm using an older version.)
_________________
I'm a PSP hacker now, but I still <3 DS.
#145410 - Lazy1 - Thu Nov 15, 2007 12:42 am
The offer for HL2 or HL2:EP1 still stands if someone can help me out...
I cannot find out where it gets stuck and since it happens so infrequently this makes it all that much more annoying.
Ideally I want to add in a key combo into a test release, that way if it does get stuck the user can press the combo and give me an address so I can finally fix that bug once and for all.