windows-nt/Source/XPSP1/NT/base/ntos/rtl/amd64/stkwalk.c
2020-09-26 16:20:57 +08:00

349 lines
8.6 KiB
C

/*++
Copyright (c) 2000 Microsoft Corporation
Module Name:
stkwalk.c
Abstract:
This module implements the routine to get the callers and the callers
caller address.
Author:
David N. Cutler (davec) 26-Jun-2000
Revision History:
--*/
#include "ntrtlp.h"
USHORT
RtlCaptureStackBackTrace (
IN ULONG FramesToSkip,
IN ULONG FramesToCapture,
OUT PVOID *BackTrace,
OUT PULONG BackTraceHash
)
/*++
Routine Description:
This routine caputes a stack back trace by walking up the stack and
recording the information for each frame.
Arguments:
FramesToSkip - Supplies the number of frames to skip over at the start
of the back trace.
FramesToCapture - Supplies the number of frames to be captured.
BackTrace - Supplies a pointer to the back trace buffer.
BackTraceHash - Supples a pointer to the computed hash value.
Return Value:
The number of captured frames is returned as the function value.
--*/
{
ULONG FramesFound;
ULONG HashValue;
ULONG Index;
PVOID Trace[2 * MAX_STACK_DEPTH];
//
// If the number of frames to capture plus the number of frames to skip
// (one additional frame is skipped for the call to walk the chain), then
// return zero.
//
FramesToSkip += 1;
if ((FramesToCapture + FramesToSkip) >= (2 * MAX_STACK_DEPTH)) {
return 0;
}
//
// Capture the stack back trace.
//
FramesFound = RtlWalkFrameChain(&Trace[0],
FramesToCapture + FramesToSkip,
0);
//
// If the number of frames found is less than the number of frames to
// skip, then return zero.
//
if (FramesFound <= FramesToSkip) {
return 0;
}
//
// Compute the hash value and transfer the captured trace to the back
// trace buffer.
//
HashValue = 0;
for (Index = 0; Index < FramesToCapture; Index += 1) {
if (FramesToSkip + Index >= FramesFound) {
break;
}
BackTrace[Index] = Trace[FramesToSkip + Index];
HashValue += PtrToUlong(BackTrace[Index]);
}
*BackTraceHash = HashValue;
return (USHORT)Index;
}
#undef RtlGetCallersAddress
VOID
RtlGetCallersAddress (
OUT PVOID *CallersPc,
OUT PVOID *CallersCallersPc
)
/*++
Routine Description:
This routine returns the address of the call to the routine that called
this routine, and the address of the call to the routine that called
the routine that called this routine. For example, if A called B called
C which called this routine, the return addresses in B and A would be
returned.
Arguments:
CallersPc - Supplies a pointer to a variable that receives the address
of the caller of the caller of this routine (B).
CallersCallersPc - Supplies a pointer to a variable that receives the
address of the caller of the caller of the caller of this routine
(A).
Return Value:
None.
Note:
If either of the calling stack frames exceeds the limits of the stack,
they are set to NULL.
--*/
{
CONTEXT ContextRecord;
ULONG64 EstablisherFrame;
PRUNTIME_FUNCTION FunctionEntry;
PVOID HandlerData;
ULONG64 HighLimit;
ULONG64 ImageBase;
ULONG64 LowLimit;
//
// Assume the function table entries for the various routines cannot be
// found or there are not three procedure activation records on the stack.
//
*CallersPc = NULL;
*CallersCallersPc = NULL;
//
// Get current stack limits, capture the current context, virtually
// unwind to the caller of this routine, and lookup function table entry.
//
RtlpGetStackLimits(&LowLimit, &HighLimit);
RtlCaptureContext(&ContextRecord);
FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
&ImageBase,
NULL);
//
// Attempt to unwind to the caller of this routine (C).
//
if (FunctionEntry != NULL) {
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
ImageBase,
ContextRecord.Rip,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
//
// Attempt to unwind to the caller of the caller of this routine (B).
//
FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
&ImageBase,
NULL);
if ((FunctionEntry != NULL) &&
(ContextRecord.Rsp < HighLimit)) {
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
ImageBase,
ContextRecord.Rip,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
*CallersPc = (PVOID)ContextRecord.Rip;
//
// Attempt to unwind to the caller of the caller of the caller
// of the caller of this routine (A).
//
FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
&ImageBase,
NULL);
if ((FunctionEntry != NULL) &&
(ContextRecord.Rsp < HighLimit)) {
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
ImageBase,
ContextRecord.Rip,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
*CallersCallersPc = (PVOID)ContextRecord.Rip;
}
}
}
return;
}
ULONG
RtlWalkFrameChain (
OUT PVOID *Callers,
IN ULONG Count,
IN ULONG Flags
)
/*++
Routine Description:
This function attempts to walk the call chain and capture a vector with
a specified number of return addresses. It is possible that the function
cannot capture the requested number of callers, in which case, the number
of captured return address will be returned.
Arguments:
Callers - Supplies a pointer to an array that is to received the return
address values.
Count - Supplies the number of frames to be walked.
Flags - Supplies the flags value (unused).
Return value:
The number of captured return addresses.
--*/
{
CONTEXT ContextRecord;
ULONG64 EstablisherFrame;
PRUNTIME_FUNCTION FunctionEntry;
PVOID HandlerData;
ULONG64 HighLimit;
ULONG64 ImageBase;
ULONG Index;
ULONG64 LowLimit;
//
// Get current stack limits and capture the current context.
//
RtlpGetStackLimits(&LowLimit, &HighLimit);
RtlCaptureContext (&ContextRecord);
//
// Capture the requested number of return addresses if possible.
//
Index = 0;
try {
while ((Index < Count) && (ContextRecord.Rip != 0)) {
//
// Lookup the function table entry using the point at which control
// left the function.
//
FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
&ImageBase,
NULL);
//
// If there is a function table entry for the routine and the stack is
// within limits, then virtually unwind to the caller of the routine
// to obtain the return address. Otherwise, discontinue the stack walk.
//
if ((FunctionEntry != NULL) &&
(ContextRecord.Rsp < HighLimit)) {
RtlVirtualUnwind(UNW_FLAG_NHANDLER,
ImageBase,
ContextRecord.Rip,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
Callers[Index] = (PVOID)ContextRecord.Rip;
Index += 1;
} else {
break;
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
#if DBG
DbgPrint ("Unexpected exception in RtlWalkFrameChain ...\n");
DbgBreakPoint ();
#endif
Index = 0;
}
return Index;
}