68k Any sampling profilers for Mac 68k?

Relating to a 68k application

bribri

New Tinkerer
Jun 28, 2024
22
9
3
I'm been exploring profiling the code I'm writing, and I'm wondering what options are available. I've gotten CodeWarrior Pro 4's profile working, and while it certainly provides some useful information, it only provides timing on a per-function basis. I'm wondering if anyone made a sampling profile that can get down to the granularity of individual lines of code, like most modern profilers?

Also, this is mainly for my game project that I've got compiling with both Retro68 and CodeWarrior, so I'm curious about whether it's possible to profile the Retro68 build. Unfortunately using gcc's profiling features / gprof doesn't seem to work, since I don't think they support 68k.

I suppose if I wanted to get crazy, I could roll my own. In theory I could create a timer that fires every X microseconds, take note of what code was being executed before the interrupt, and then somehow figure out which lines of code it was using debugging data generated by gcc. But that's a wheel I'd rather not reinvent!
 

cy384

New Tinkerer
Nov 18, 2021
20
18
3
USA
www.cy384.com
I'd be really surprised if you find anything like that, but if you do, please share. I've been considering writing something that hooks up to QEMU's gdb interface; I think that would open up a lot more options compared to attempting something native.
 

bribri

New Tinkerer
Jun 28, 2024
22
9
3
I said I didn't want to reinvent that wheel... but I gave it a shot! I had the sense of what was the difficult part reversed, though. Correlating an instruction address with a line of code is fairly easy -- that's what building with debugging information is for.

But I haven't figured out how to catch what was last being executed at interrupt time. I thought it'd be straight forward, because according to Inside Macintosh, when an interrupt occurs, the Program Counter gets pushed on to the stack. And so I used Time Manager to write a function that would fire regularly and inspect the stack to see what was being called.

The trouble is, at least for Time Manager and VBL interrupts, the OS is doing a bunch of stuff behind the scenes to make it possible for a normal C or Pascal function to be called at interrupt time, and then restoring the previous state after that function finishes. So the stack is in a totally unpredictable state when my timer function is called, and there's no clear way to figure out where execution is eventually going to return to. Or at least, none that I've found so far.

Is it actually possible to figure out what the Program Counter was at interrupt time in a Time Manager task?
 

David Cook

Tinkerer
Jul 20, 2023
60
54
18
Here's a possible approach.

1. Run a test program that shows an alert with it's address range when it starts up. Then have it do some heavy looping with occasional calls to WaitNextEvent/GetNextEvent.
2. Add a debug breakpoint in your Time Manager program.
3. Each time it breaks, dump the stack in Macsbug until you find an address in the range of your test program. I assume a pattern will emerge of where the return address is located.
 

bribri

New Tinkerer
Jun 28, 2024
22
9
3
Here's a possible approach.

1. Run a test program that shows an alert with it's address range when it starts up. Then have it do some heavy looping with occasional calls to WaitNextEvent/GetNextEvent.
2. Add a debug breakpoint in your Time Manager program.
3. Each time it breaks, dump the stack in Macsbug until you find an address in the range of your test program. I assume a pattern will emerge of where the return address is located.
I did something that I think is basically equivalent to that:

Each time my Time Manager task fired, I had it get the address of the stack, and then scan through it looking for anything that could be an address of my program (looking for specific address ranges). I then had it print all of those -- possible because I'm running in an emulator and the printing happened on the host system -- and look for a pattern.

Unfortunately what I found is that the return address was not in a consistent position, and sometimes it wasn't there at all.

That said, your idea might be the better one, because naturally my Time Manager task is probably changing the stack by virtue of running compiled code -- the old "changing the result by measuring it" issue.

I'm still learning to use MacsBug though. How do I set a breakpoint when my task fires?
 

David Cook

Tinkerer
Jul 20, 2023
60
54
18
BTW, it looks like Apple's Time Manager source code is available. It might help you understand what the stack (and registers) should like like when your routine is called. The exact Time Manager code will depend on your ROM / System. But, this might help.

 
  • Like
Reactions: bribri

bribri

New Tinkerer
Jun 28, 2024
22
9
3
Checking with the debugger, I'm still seeing inconsistent layout of the stack at interrupt time. I'll take a look at the Time Manager source code and see if anything can be gleamed from that.
 

cy384

New Tinkerer
Nov 18, 2021
20
18
3
USA
www.cy384.com
Nice work! When I have a few hours I'll see if I can get it running with my project (ssheven). I've been wanting to see what the ratio of CPU usage for encryption vs UI operations is.
 

bribri

New Tinkerer
Jun 28, 2024
22
9
3
Nice work! When I have a few hours I'll see if I can get it running with my project (ssheven). I've been wanting to see what the ratio of CPU usage for encryption vs UI operations is.

Let me know how it goes! I added a bit more instructions to the repo's read me.
 

bribri

New Tinkerer
Jun 28, 2024
22
9
3
Do feel free to use the profiler, though I now realize that there's some things in it that need to be fixed. Currently it discards samples where it can't do a stack crawl, which means it might discard legitimate samples from functions that make use of register A6.

Edit: the fix for this is now described in the repo's read me
 
Last edited:

lauland

Tinkerer
Dec 12, 2023
39
26
18
This is some impressive and very cool work you've done!

I've been doing a lot of m68k Retro68 lately, and have moved my primary building and testing of my SDL2 port for MacOS 7/8/9 to it from CodeWarrior. Being able to use Makefiles makes things SO much simpler, cleaner, and easier to see what code is actually being included in the build, to say the least.

I'm eager to try your profiler on it, just to see what is going on under the hood.

Even if your profiler is still being worked on, it is very far along, and it will be quite interesting to try it on such a large project.
 
Last edited:

bribri

New Tinkerer
Jun 28, 2024
22
9
3
Thanks! I think at this point the profiler is basically done. The one change I'm thinking about making is improving how the samples it takes are stored in memory. Right now they're all just stuffed into a buffer one after the other, until the buffer runs out of room, and then it can't take any samples any longer. I want to replace that with some kind of hashmap so that it can keep a count of unique samples and use the memory more efficiently, but that requires getting a C hashmap implementation that works without mallocing anything. For the time being I just give it a whole megabyte to use when profiling.