From rusmv1!iraun1.ira.uka.de!sol.ctr.columbia.edu!zaphod.mps.ohio-state.edu!wupost!uunet!mcsun!ukc!slxsys!ibmpcug!demon!news Mon Sep 9 14:35:29 MET 1991 Article: 1525 of comp.sys.acorn Path: rusmv1!iraun1.ira.uka.de!sol.ctr.columbia.edu!zaphod.mps.ohio-state.edu!wupost!uunet!mcsun!ukc!slxsys!ibmpcug!demon!news From: pmoore@cix.compulink.co.uk (Paul Moore) Newsgroups: comp.sys.acorn Subject: Re: "C" __FUNC__ Message-ID: <1991Sep02.234251.24067@demon.co.uk> Date: 2 Sep 91 23:42:51 GMT Sender: news@demon.co.uk (C-News Owner) Reply-To: Paul Moore Organization: Gated to News by demon.co.uk Lines: 178 In his message, Gavin.Flower@comp.vuw.ac.nz (Gavin Flower) writes: > I would find it very useful, for diagnostic purposes, > to have a macro __FUNC__ that would expand out > to the name of function that contained it. I can't do it as a macro, but here's a little assembler routine which returns the name of the function which called it. It relies on the Arm procedure call standard, plus the backtrace data stored by C (the stuff which the -ff flag omits!). I don't know whether the format of the backtrace data is "standard", but I've seen it documented in a couple of places - anyone at Acorn care to comment? See the chunk of data between labels 'name' and 'caller' in the stuff below, for an example... I know I shouldn't really include code here, but it's only teeny, so I hope it's OK... I include ObjAsm source code, and for those without ObjAsm, the uuencoded object file, O.Caller. Just link the latter with your program, and user as shown in the comments at the top of the source. Enjoy! Gustav --------------------------------------------------------------------------- Paul Moore | "If you could have any amount of money... pmoore@cix.compulink.co.uk | any amount of money at all... gustav@tharr.UUCP | how much would you want?" ----------------------------| "All of it." Opinions? Not me, surely? | Cerebus, Church & State. --------------------------------------------------------------------------- ----------------------- ObjAsm source code ----------------------- ; Trace back through the function call thread, and return the name of the ; function "n" calls up. ; ; e.g. if you have (in C) ; ; extern char *caller(int); ; ; void foo (void) ; { ; bar(); ; } ; ; void bar (void) ; { ; char *f0 = caller(0); ; char *f1 = caller(1); ; } ; ; f0 should contain "bar", and f1 should contain "foo". ; ; I tend to use caller(1) to debug things like memory problems, where I ; use things like ; ; void *xmalloc(size_t size) ; { ; void *res = malloc(size); ; if (res == NULL) ; { ; fprintf(stderr, "Error calling malloc(%d) from %s\n", ; size, caller(1)); ; exit(1); ; } ; return res; ; } ; ; [This use of caller(1) is OK - see below - as it is unlikely that anything ; will call xmalloc() and not use the result somehow...] ; ; ; NOTE: This routine RELIES on the Risc-OS Arm procedure call standard ; binding (APCS-R). In particular, it will NOT work on code ; compiled under Ansi C v2.00 or earlier! ; ; NOTE: The parameter "n" passed to caller() is NOT always what you might ; think! Basically, because Acorn C is very good at optimising tail- ; recursive function calls into simple branches. For example, in the ; example above, foo() would be optimised to a simple "B bar" ; instruction. So f1 will, in fact, be the name of foo()'s caller (or ; possibly even further up the calling tree!), rather than "foo". ; In general, caller(0) is always safe, and caller(1) is OK in most ; cases. caller(n) for n = 2 and above are more risky, basically ; getting worse as n increases. ; ; NOTE: If you "back up" into functions compiled without function names ; embedded in the code (eg, C library functions), caller(n) will ; CRASH. Sorry - but there is no way that I can see to recognise ; this situation... ; ; (Basically, the usual disclaimers all apply, in spades!). ; a1 RN 0 a2 RN 1 a3 RN 2 a4 RN 3 v1 RN 4 v2 RN 5 v3 RN 6 v4 RN 7 v5 RN 8 v6 RN 9 sl RN 10 fp RN 11 ip RN 12 sp RN 13 lr RN 14 pc RN 15 AREA |Caller|, CODE, READONLY EXPORT caller 01 DCB "caller",0 ALIGN DCD &FF000000 :OR: ( {PC} - %B01 ) caller MOVS a2, fp ; If frame pointer is 0 then ADREQ a1, null ; there is no backtrace structure MOVEQS pc, lr 02 TEQ a1, #0 ; Go back a1 frames... BEQ %F03 LDR a2, [a2, #-12] ; Get caller's frame pointer TEQ a2, #0 ; If 0, no backtrace structure ADREQ a1, null MOVEQS pc, lr SUB a1, a1, #1 B %B02 03 LDR a2, [a2] ; Get caller's PC (return data save) BIC a2, a2, #&FC000003 ; Convert to an address SUB a2, a2, #12 ; Go to return data save instruction 04 LDR a3, [a2, #-4]! ; Get previous word MOV a4, a3, LSR #24 ; Test top 8 bits TEQ a4, #&FF ; If they're FF, we've found the name BNE %B04 BIC a3, a3, #&FF000000 ; Get the name offset SUB a1, a2, a3 ; Point to the caller's name MOVS pc, lr ; Return null DCB "",0 ALIGN END ---------- Uuencoded version of the compiled result... ---------- begin 644 O.Caller MQ<;+PP<````%````3T)*7TA%041\````+````$]"2E]!4D5!J````&0```!/ M0DI?4UE-5`P!```@````3T)*7U-44E0L`0``$````$]"2E])1$9./`$``"@` M``!5;G5S960@(```````````56YU($(#'E(CR@X?\`,^/[__\:_R3"XP(`0>`.\+#A```````` M```!```````````````(`````P````P`````````0V%L;&5R``!C86QL97(` I`$%232!!3T8@36%C