This chapter discusses the library routines that support procedure call tracing. These routines are used to:
Refer to a given procedure invocation
Provide the context of a procedure invocation
Navigate (walk) the procedure call chain
The chapter also describes the data structures and procedures
that these routines require.
7.1 Referencing a Procedure Invocation
When a reference to a specific procedure invocation is made at run time, the virtual frame pointer or the real frame pointer for that invocation can be used. The virtual frame pointer of a procedure invocation is the contents of the stack pointer at the entry point of the procedure. The real frame pointer of a procedure is the contents of the stack pointer after the size of the fixed part of the stack frame has been subtracted from the virtual frame pointer.
Note that the virtual frame pointer of an invocation is not the value used by the procedure itself for addressing. The contents of the SP register are modified in the procedure prologue and the resulting real frame pointer value is then sometimes copied into FP (as in the case of a variable size stack frame). The real frame pointer is always used for addressing local storage throughout the remainder of the procedure.
The real frame pointer is not, by itself, sufficient to unambiguously identify all possible procedure invocations. For example, a null frame procedure has the same real frame pointer as its caller because the null frame procedure allocates no stack storage. This ambiguity is of no consequence for the purposes of this calling standard because the real frame pointer value is always used in combination with a program counter value that identifies an instruction within a particular procedure.
The static link used in calling nested procedures in languages such as Pascal and Ada is usually the virtual frame pointer or the real frame pointer value. The choice is implementation-dependent and can vary from language to language and release to release.
The full context of a specific procedure invocation is provided through
the use of the
sigcontext
data structure.
The
sigcontext
structure is defined
in the file
/usr/include/signal.h
.
7.2 Providing a Procedure Invocation Context
A thread can obtain its own context by calling a system library function defined as follows:
exc_capture_context (ContextRecord)
ContextRecord |
Address of a
sigcontext
structure
into which the procedure context of the caller is written |
A thread can obtain the invocation context of the procedure preceding another procedure context by calling a system library routine defined as follows:
exc_virtual_unwind (FunctionEntry, ContextRecord)
FunctionEntry |
Address of the function table entry for the
function.
If zero, the function table entry is looked up using the PC from
ContextRecord . |
ContextRecord |
Address of a
sigcontext
structure.
The given structure is updated to represent the context of the previous (calling)
frame. |
InPrologueOrEpilogue |
If 1, indicates that the resulting program
counter value in the given
ContextRecord
is within the prologue
or the epilogue code of the function.
If zero, indicates that the program
counter is in the body of the function. |
The
exc_virtual_unwind()
procedure takes a
sigcontext
structure together with its associated procedure descriptor and updates the
context to reflect the state of the caller at the point where it made the
call.
7.3 Walking the Call Chain
During program execution, it is sometimes necessary to navigate (walk) the call chain. For example, frame-based exception handling requires call chain navigation. Call-chain navigation is possible only in the reverse direction; for example, latest-to-earliest procedure, or top to bottom procedure.
There are two steps for performing call chain navigation:
Build a
sigcontext
structure
when given a program state that contains
a register set.
For the current routine, an initial
sigcontext
structure
can be obtained by calling
exc_capture_context()
.
Repeatedly call
exc_virtual_unwind()
until the end of the chain is
reached.
Compilers are allowed to optimize high-level language procedure calls so that they do not appear in the call chain. For example, inline procedures never appear in the call chain.
No assumptions should be made about the relative positions of any memory used for procedure frame information. There is no guarantee that successive stack frames will always appear at higher addresses.