$(ADA_TARGET_PROPERTIES) \
$(OUTPUT_OPTION)
-# Force no sibling call optimization on s-traceb.o so the number of stack
-# frames to be skipped when computing a call chain is not modified by
-# optimization. We don't want inlining, either.
+# Force no sibling call optimization on s-traceb.o and g-traceb.o so the
+# number of stack frames to be skipped when computing a call chain is not
+# modified by optimization. We don't want inlining, either.
s-traceb.o: s-traceb.adb s-traceb.ads
$(ADAC) -c $(ALL_ADAFLAGS) $(FORCE_DEBUG_ADAFLAGS) \
$(NO_INLINE_ADAFLAGS) $(NO_SIBLING_ADAFLAGS) $(ADA_INCLUDES) $< \
$(OUTPUT_OPTION)
+g-traceb.o: g-traceb.adb g-traceb.ads
+ $(ADAC) -c $(ALL_ADAFLAGS) $(FORCE_DEBUG_ADAFLAGS) \
+ $(NO_INLINE_ADAFLAGS) $(NO_SIBLING_ADAFLAGS) $(ADA_INCLUDES) $< \
+ $(OUTPUT_OPTION)
+
# Compile s-tasdeb.o without optimization and with debug info so that it is
# always possible to set conditional breakpoints on tasks.
-- Run-time non-symbolic traceback support
+-- As for System.Traceback, inlining and sibling call optimizations
+-- must be prevented within this unit.
+
with System.Traceback;
package body GNAT.Traceback is
Len : out Natural)
is
begin
- System.Traceback.Call_Chain (Traceback, Traceback'Length, Len);
+ -- Request skipping this frame + that of our callee
+ System.Traceback.Call_Chain
+ (Traceback, Traceback'Length, Len, Skip_Frames => 2);
end Call_Chain;
function Call_Chain
procedure Call_Chain (Traceback : out Tracebacks_Array; Len : out Natural);
-- Store up to Traceback'Length tracebacks corresponding to the current
- -- call chain. The first entry stored corresponds to the deepest level
- -- of subprogram calls. Len shows the number of traceback entries stored.
- -- It will be equal to Traceback'Length unless the entire traceback is
- -- shorter, in which case positions in Traceback past the Len position
- -- are undefined on return.
+ -- call chain. The first entry is for the caller of this subprogram and
+ -- Len conveys the number of entries stored. Len will be Traceback'Length
+ -- unless the entire traceback is shorter. Positions in Traceback beyond
+ -- Len are undefined on return.
function Call_Chain
(Max_Len : Positive;
for Switches ("C") use Libgnat_Common.C_Flags;
for Switches ("Ada") use Libgnat_Common.Ada_Flags;
- for Switches ("s-traceb.adb") use
+ for Switches ("?-traceb.adb") use
Libgnat_Common.Ada_Flags & Libgnat_Common.Force_Debug &
Libgnat_Common.No_Inline & Libgnat_Common.No_Sibling;
- -- Force no sibling call optimization on s-traceb.o so the number of
- -- stack frames to be skipped when computing a call chain is not
- -- modified by optimization. We don.t want inlining, either.
+ -- Prevent inlining and sibling call optimization on s-traceb.o and
+ -- g-traceb.o so the number of stack frames to be skipped when computing
+ -- a call chain is not modified by optimization.
for Switches ("a-except.adb") use
Libgnat_Common.Ada_Flags & ("-O1") &
-- This is the default version of this package
--- Note: this unit must be compiled using -fno-optimize-sibling-calls.
--- See comment below in body of Call_Chain for details on the reason.
+-- Note: To preserve the correctness of "Skip_Frames" processing,
+-- subprogram calls here must materialize as subprogram calls (and
+-- frames) at the machine level, so inlining and sibling call
+-- optimizations must be prevented for this unit.
package body System.Traceback is
o FRAME_OFFSET, the offset, from a given frame address or frame pointer
value, at which this layout will be found,
- o FRAME_LEVEL, controls how many frames up we get at to start with,
- from the initial frame pointer we compute by way of the GCC builtin,
+ o FRAME_LEVEL, control how many stack frames up we start from, in
+ builtin_frame_address terms.
0 is most often the appropriate value. 1 may be necessary on targets
where return addresses are saved by a function in it's caller's frame
| |
+----------------+
- o BASE_SKIP,
+ o BASE_SKIP represents the initial shift incurred by the initial
+ FRAME_LEVEL + the fact that what we find on the stack are return
+ addresses. One way to see it is: starting from
- Since we inherently deal with return addresses, there is an implicit shift
- by at least one for the initial point we are able to observe in the chain.
+ ptr = (struct layout *)__builtin_frame_address(FRAME_LEVEL);
- On some targets (e.g. sparc-solaris), the first return address we can
- easily get without special code is even our caller's return address, so
- there is a initial shift of two.
+ in __gnat_backtrace, where does ptr->return_address land?
- BASE_SKIP represents this initial shift, which is the minimal "skip_frames"
- value we support. We could add special code for the skip_frames < BASE_SKIP
- cases. This is not done currently because there is virtually no situation
- in which this would be useful.
+ If this is within __gnat_backtrace, BASE_SKIP should be 0. If this
+ is within the caller of __gnat_backtrace, BASE_SKIP should be 1. etc.
+
+ BASE_SKIP is the minimal "skip_frames" value we support.
Finally, to account for some ABI specificities, a target may (but does
not have to) define:
#define STOP_FRAME(CURRENT, TOP_STACK) \
((CURRENT)->next == 0 || ((long)(CURRENT)->next % __alignof__(void*)) != 0)
+/* builtin_frame_address(1) gets us the frame pointer of our caller,
+ where we store our own return address. */
#define BASE_SKIP 1
/*-------------------------- SPARC Solaris or RTEMS --------------------*/
|| (void *) ((CURRENT)->next) < (TOP_STACK) \
|| EXTRA_STOP_CONDITION(CURRENT))
+/* builtin_frame_address(1) gets us the *frame* pointer of our caller, where
+ we'll find the return address from that caller to its own caller. */
#define BASE_SKIP (1+FRAME_LEVEL)
/* On i386 architecture we check that at the call point we really have a call