• Hello MLAers! We've re-enabled auto-approval for accounts. If you are still waiting on account approval, please check this thread for more information.

Possible to get the saved instruction address at interrupt time, in a Time Manager task?

bribri

6502
I'm trying to see if I can hack together a sampling profiler, and to do that I tried using a Time Manager task that would execute every X milliseconds. The idea was that at interrupt time I would figure out the address of the instruction that execution will return to when the interrupt is finished, since according to Inside Macintosh its pushed on to the stack when an interrupt occurs.

The trouble is, I'm not sure how to do it! The stack doesn't seem to have any sort of predictable layout when a Time Manager task is firing at interrupt time. I figure this is because the OS is doing a bunch of work behind the scenes to allow a normal C or Pascal function to be called, and still restore everything back to how it was before the interrupt fired.

Is this actually possible? (Maybe it requires a much lower level interrupt handler written in assembly?)
 
(Maybe it requires a much lower level interrupt handler written in assembly?)

This would be my bet. You'd be well into "undocumented horror" territory doing this with the Time Manager, and relying on its precise stack layout feels fragile. Doing it with a "real" interrupt handler will be more painful but much more consistent across OS versions, phases of the moon &c. I'd expect, and you'd be only using totally documented and understood behaviouor.
 
This would be my bet. You'd be well into "undocumented horror" territory doing this with the Time Manager, and relying on its precise stack layout feels fragile. Doing it with a "real" interrupt handler will be more painful but much more consistent across OS versions, phases of the moon &c. I'd expect, and you'd be only using totally documented and understood behaviouor.
If I wanted to give this a shot, any idea where to look for how to do it? I see some instructions in Inside Macintosh volume II pointing to the manual for the Synertek SY6522 Versatile Interface Adapter, although I don't know if that applies to Macs beyond the 128K and 512K. I'd prefer not to use a raw vertical retrace interrupt, especially since if I'm profiling something like a game, I'm already using that interrupt to figure out when to render to the screen.

Better would be if I could still use Time Manager somehow, especially the extended Time Manager, since something that fires every millisecond would be ideal. But that sounds like I'd be replacing an entry in one of the interrupt vector tables with my own... possibly not a good idea? Time Manager is doing it somehow though that I could in theory reproduce myself.

(I found a few people asking about this in the comp.sys.mac.programmer archives but sadly no one gave them an answer!)
 
This would be my bet. You'd be well into "undocumented horror" territory doing this with the Time Manager, and relying on its precise stack layout feels fragile. Doing it with a "real" interrupt handler will be more painful but much more consistent across OS versions, phases of the moon &c. I'd expect, and you'd be only using totally documented and understood behaviouor.

I ran across an interesting header "Perf.h".


This seems to be something Apple created that checks application performance, as well as ROM usage, etc. Something @zigzagjoe and @ymk might be interested in, given they both have modern creations that accelerate Macintosh performance.

I'm try to figure out where the actual calls reside. Possibly a library (.o) file? The Perf.h file is published on the Tool Chest CDs, so it isn't internal or a secret. Maybe something covered by develop magazine?
 
I'm trying to see if I can compile it with Retro68! If it works then it would save me from trying to figure out how to write my own sampling routine.

PerformLib.o gets converted to a gcc static lib along with everything else when building Retro68, but if I link to it, I get some errors I don't know how to fix:

Code:
/Users/username/retro68/retro68-build/toolchain/m68k-apple-macos/lib/libPerformLib.a(PerformLib.o): in function `L261.__NUMTOOLBOXTRAPS':
(.text.L261.__NUMTOOLBOXTRAPS+0x1e): relocation truncated to fit: R_68K_PC16 against symbol `NGETTRAPADDRESS' defined in .text.NGETTRAPADDRESS section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.L261.__NUMTOOLBOXTRAPS+0x10): relocation truncated to fit: R_68K_PC16 against symbol `NGETTRAPADDRESS' defined in .text.NGETTRAPADDRESS section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
/Users/username/retro68/retro68-build/toolchain/m68k-apple-macos/lib/libPerformLib.a(PerformLib.o): in function `L265.TRAPAVAILABLE':
(.text.L265.TRAPAVAILABLE+0x50): relocation truncated to fit: R_68K_PC16 against symbol `NGETTRAPADDRESS' defined in .text.NGETTRAPADDRESS section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.L265.TRAPAVAILABLE+0x42): relocation truncated to fit: R_68K_PC16 against symbol `NGETTRAPADDRESS' defined in .text.NGETTRAPADDRESS section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
/Users/username/retro68/retro68-build/toolchain/m68k-apple-macos/lib/libPerformLib.a(PerformLib.o): in function `PERFDUMP':
(.text.PERFDUMP+0x6bc): relocation truncated to fit: R_68K_PC16 against symbol `FLUSHVOL' defined in .text.FLUSHVOL section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x6a6): relocation truncated to fit: R_68K_PC16 against symbol `FLUSHVOL' defined in .text.FLUSHVOL section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x68a): relocation truncated to fit: R_68K_PC16 against symbol `FSCLOSE' defined in .text.FSCLOSE section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x674): relocation truncated to fit: R_68K_PC16 against symbol `SETEOF' defined in .text.SETEOF section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x660): relocation truncated to fit: R_68K_PC16 against symbol `GETFPOS' defined in .text.GETFPOS section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x540): relocation truncated to fit: R_68K_PC16 against symbol `NUMTOSTRING' defined in .text.NUMTOSTRING section in /Users/username/retro68/retro68-build/toolchain/lib/gcc/m68k-apple-macos/12.2.0/../../../../m68k-apple-macos/lib/libInterface.a(Interface.o)
(.text.PERFDUMP+0x4f8): additional relocation overflows omitted from the output
collect2: error: ld returned 1 exit status
 
I'm trying to see if I can compile it with Retro68! If it works then it would save me from trying to figure out how to write my own sampling routine.

PerformLib.o gets converted to a gcc static lib along with everything else when building Retro68, but if I link to it, I get some errors I don't know how to fix:

PerformLib.o appears to be included as part of MPW. The version from the CD is from MPW 3.2.x. Attach is also a version from MPW 3.3. It may be in a newer library format that works with Retro68. If not, it appears that MPW 3.5 and later are available for download.

1744672900817.png

The attached files are in BinHex format.
 

Attachments

There's a TestPerf.c and TestPerf.p file that shows how to use Perf.h or Perf.p. There's a MPW makefile to make the TestPerf executable in both cases. These are in the elliotnunn/supermario source. Check the MPW Examples folder?
 
PerformLib.o appears to be included as part of MPW. The version from the CD is from MPW 3.2.x. Attach is also a version from MPW 3.3. It may be in a newer library format that works with Retro68. If not, it appears that MPW 3.5 and later are available for download.
Yup, I downloaded MPW 3.5 and copied in its Interfaces&Libraries folder when building Retro68, as recommended by its build steps. It automatically converted PerformLib into a gcc compatible static lib that I can link against. I found TestPerf.c as well (thanks to scanning through MPW 3.0 Reference Volume 1 -- not sure how I found that one!) which I've used to try and get profiling working in my own app.

But then building gets me those linker errors. :cry: I think I understand what they're about, but I don't know how to fix them.
 
Supposedly with Retro68 and gcc, code segments don't matter and there's no 16k limit. But I might try segmenting anyway.
 
what flags are you building with? it looks like you may be building with -mpcrel which means all address references are pc-relative to be relocatable without an relocation pass required. This is what you want for drivers and inits in declroms, which end up in random places in memory, however it does place a limit on the max jump distance without a trampoline or other method to increase range.

You don't need to use mpcrel for code that you can relocate properly. I don't quite know how Retro68 handles it, but it should have the capability to do a static relocation for you. Or use segments.
 
I don't believe I'm building with -mpcrel, and don't see it in the build output. (And I tried building with -fPIC as an experiment, but it caused a legit gcc error, so I'm assuming that retro68 doesn't supports it.)
 
I don't know the structure of your project, but if you can figure out the (assembly) code referred to by the offsets into .text.PERFDUMP (such as 0x6bc) that might be enough to understand out how GCC is setting up to try to call those functions and work at figuring out why that is happening. For some reason it's using a PC-relative reference.

You may be able to simply build your source with -S -fverbose-asm (and no -o) to have GCC simply generate the assembly without assembling it. Then, look at the call to the functions named in the error output.

Unfortunately most of my retro68 work is done with baremetal code or drivers (which are near enough to baremetal) rather than applications for Mac OS proper.
 
Alas, using segments didn't help with linking to PerformLib.

In case it's a clue, I can link to it so long as I don't call either InitPerf or PerfDump. Linking those two functions is what triggers the error.
 
I don't know the structure of your project, but if you can figure out the (assembly) code referred to by the offsets into .text.PERFDUMP (such as 0x6bc) that might be enough to understand out how GCC is setting up to try to call those functions and work at figuring out why that is happening. For some reason it's using a PC-relative reference.

You may be able to simply build your source with -S -fverbose-asm (and no -o) to have GCC simply generate the assembly without assembling it. Then, look at the call to the functions named in the error output.

Unfortunately most of my retro68 work is done with baremetal code or drivers (which are near enough to baremetal) rather than applications for Mac OS proper.
Interesting idea. I gave it a try, but I don't think it's revealing, because the problem seems to stem not from linking my code to the functions in libPerformLib, but rather linking libPerformLib to libInterface. I wonder if this is an issue when converting MPW static libraries to gcc?
 
Oh, I missed that. Given these libraries are intended to work in a segmented environment, perhaps that's why they seem to be using the relative displacement. I don't know much about conventions for mac applications/libraries, I'm afraid, inside macintosh might have something to say on the topic.
 
Okay, I think I see the issue, though I'm not sure if there's much I can do about it. If I run Retro68's ConvertObj tool to convert MPW's PerformLib.o into a gcc assembly file, I can see that the various symbols in it are listed like:
.short GETFPOS-.
That only gives it 2 bytes, meaning it can't link it!

The other converted MPW libraries don't seem to link to other toolbox functions like PerformLib does, so they don't have this issue.

As an experiment, I changed every instance of .short followed by a toolbox function to .long, and sure enough, my app linked and built! Naturally running it results in a system crash because I just messed up the assembly code in PerformLib. But that at least reveals why it won't link.
 
Back
Top