Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

348 lines
8.6 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. stkwalk.c
  5. Abstract:
  6. This module implements the routine to get the callers and the callers
  7. caller address.
  8. Author:
  9. David N. Cutler (davec) 26-Jun-2000
  10. Revision History:
  11. --*/
  12. #include "ntrtlp.h"
  13. USHORT
  14. RtlCaptureStackBackTrace (
  15. IN ULONG FramesToSkip,
  16. IN ULONG FramesToCapture,
  17. OUT PVOID *BackTrace,
  18. OUT PULONG BackTraceHash
  19. )
  20. /*++
  21. Routine Description:
  22. This routine caputes a stack back trace by walking up the stack and
  23. recording the information for each frame.
  24. Arguments:
  25. FramesToSkip - Supplies the number of frames to skip over at the start
  26. of the back trace.
  27. FramesToCapture - Supplies the number of frames to be captured.
  28. BackTrace - Supplies a pointer to the back trace buffer.
  29. BackTraceHash - Supples a pointer to the computed hash value.
  30. Return Value:
  31. The number of captured frames is returned as the function value.
  32. --*/
  33. {
  34. ULONG FramesFound;
  35. ULONG HashValue;
  36. ULONG Index;
  37. PVOID Trace[2 * MAX_STACK_DEPTH];
  38. //
  39. // If the number of frames to capture plus the number of frames to skip
  40. // (one additional frame is skipped for the call to walk the chain), then
  41. // return zero.
  42. //
  43. FramesToSkip += 1;
  44. if ((FramesToCapture + FramesToSkip) >= (2 * MAX_STACK_DEPTH)) {
  45. return 0;
  46. }
  47. //
  48. // Capture the stack back trace.
  49. //
  50. FramesFound = RtlWalkFrameChain(&Trace[0],
  51. FramesToCapture + FramesToSkip,
  52. 0);
  53. //
  54. // If the number of frames found is less than the number of frames to
  55. // skip, then return zero.
  56. //
  57. if (FramesFound <= FramesToSkip) {
  58. return 0;
  59. }
  60. //
  61. // Compute the hash value and transfer the captured trace to the back
  62. // trace buffer.
  63. //
  64. HashValue = 0;
  65. for (Index = 0; Index < FramesToCapture; Index += 1) {
  66. if (FramesToSkip + Index >= FramesFound) {
  67. break;
  68. }
  69. BackTrace[Index] = Trace[FramesToSkip + Index];
  70. HashValue += PtrToUlong(BackTrace[Index]);
  71. }
  72. *BackTraceHash = HashValue;
  73. return (USHORT)Index;
  74. }
  75. #undef RtlGetCallersAddress
  76. VOID
  77. RtlGetCallersAddress (
  78. OUT PVOID *CallersPc,
  79. OUT PVOID *CallersCallersPc
  80. )
  81. /*++
  82. Routine Description:
  83. This routine returns the address of the call to the routine that called
  84. this routine, and the address of the call to the routine that called
  85. the routine that called this routine. For example, if A called B called
  86. C which called this routine, the return addresses in B and A would be
  87. returned.
  88. Arguments:
  89. CallersPc - Supplies a pointer to a variable that receives the address
  90. of the caller of the caller of this routine (B).
  91. CallersCallersPc - Supplies a pointer to a variable that receives the
  92. address of the caller of the caller of the caller of this routine
  93. (A).
  94. Return Value:
  95. None.
  96. Note:
  97. If either of the calling stack frames exceeds the limits of the stack,
  98. they are set to NULL.
  99. --*/
  100. {
  101. CONTEXT ContextRecord;
  102. ULONG64 EstablisherFrame;
  103. PRUNTIME_FUNCTION FunctionEntry;
  104. PVOID HandlerData;
  105. ULONG64 HighLimit;
  106. ULONG64 ImageBase;
  107. ULONG64 LowLimit;
  108. //
  109. // Assume the function table entries for the various routines cannot be
  110. // found or there are not three procedure activation records on the stack.
  111. //
  112. *CallersPc = NULL;
  113. *CallersCallersPc = NULL;
  114. //
  115. // Get current stack limits, capture the current context, virtually
  116. // unwind to the caller of this routine, and lookup function table entry.
  117. //
  118. RtlpGetStackLimits(&LowLimit, &HighLimit);
  119. RtlCaptureContext(&ContextRecord);
  120. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  121. &ImageBase,
  122. NULL);
  123. //
  124. // Attempt to unwind to the caller of this routine (C).
  125. //
  126. if (FunctionEntry != NULL) {
  127. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  128. ImageBase,
  129. ContextRecord.Rip,
  130. FunctionEntry,
  131. &ContextRecord,
  132. &HandlerData,
  133. &EstablisherFrame,
  134. NULL);
  135. //
  136. // Attempt to unwind to the caller of the caller of this routine (B).
  137. //
  138. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  139. &ImageBase,
  140. NULL);
  141. if ((FunctionEntry != NULL) &&
  142. (ContextRecord.Rsp < HighLimit)) {
  143. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  144. ImageBase,
  145. ContextRecord.Rip,
  146. FunctionEntry,
  147. &ContextRecord,
  148. &HandlerData,
  149. &EstablisherFrame,
  150. NULL);
  151. *CallersPc = (PVOID)ContextRecord.Rip;
  152. //
  153. // Attempt to unwind to the caller of the caller of the caller
  154. // of the caller of this routine (A).
  155. //
  156. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  157. &ImageBase,
  158. NULL);
  159. if ((FunctionEntry != NULL) &&
  160. (ContextRecord.Rsp < HighLimit)) {
  161. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  162. ImageBase,
  163. ContextRecord.Rip,
  164. FunctionEntry,
  165. &ContextRecord,
  166. &HandlerData,
  167. &EstablisherFrame,
  168. NULL);
  169. *CallersCallersPc = (PVOID)ContextRecord.Rip;
  170. }
  171. }
  172. }
  173. return;
  174. }
  175. ULONG
  176. RtlWalkFrameChain (
  177. OUT PVOID *Callers,
  178. IN ULONG Count,
  179. IN ULONG Flags
  180. )
  181. /*++
  182. Routine Description:
  183. This function attempts to walk the call chain and capture a vector with
  184. a specified number of return addresses. It is possible that the function
  185. cannot capture the requested number of callers, in which case, the number
  186. of captured return address will be returned.
  187. Arguments:
  188. Callers - Supplies a pointer to an array that is to received the return
  189. address values.
  190. Count - Supplies the number of frames to be walked.
  191. Flags - Supplies the flags value (unused).
  192. Return value:
  193. The number of captured return addresses.
  194. --*/
  195. {
  196. CONTEXT ContextRecord;
  197. ULONG64 EstablisherFrame;
  198. PRUNTIME_FUNCTION FunctionEntry;
  199. PVOID HandlerData;
  200. ULONG64 HighLimit;
  201. ULONG64 ImageBase;
  202. ULONG Index;
  203. ULONG64 LowLimit;
  204. //
  205. // Get current stack limits and capture the current context.
  206. //
  207. RtlpGetStackLimits(&LowLimit, &HighLimit);
  208. RtlCaptureContext (&ContextRecord);
  209. //
  210. // Capture the requested number of return addresses if possible.
  211. //
  212. Index = 0;
  213. try {
  214. while ((Index < Count) && (ContextRecord.Rip != 0)) {
  215. //
  216. // Lookup the function table entry using the point at which control
  217. // left the function.
  218. //
  219. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  220. &ImageBase,
  221. NULL);
  222. //
  223. // If there is a function table entry for the routine and the stack is
  224. // within limits, then virtually unwind to the caller of the routine
  225. // to obtain the return address. Otherwise, discontinue the stack walk.
  226. //
  227. if ((FunctionEntry != NULL) &&
  228. (ContextRecord.Rsp < HighLimit)) {
  229. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  230. ImageBase,
  231. ContextRecord.Rip,
  232. FunctionEntry,
  233. &ContextRecord,
  234. &HandlerData,
  235. &EstablisherFrame,
  236. NULL);
  237. Callers[Index] = (PVOID)ContextRecord.Rip;
  238. Index += 1;
  239. } else {
  240. break;
  241. }
  242. }
  243. } except(EXCEPTION_EXECUTE_HANDLER) {
  244. #if DBG
  245. DbgPrint ("Unexpected exception in RtlWalkFrameChain ...\n");
  246. DbgBreakPoint ();
  247. #endif
  248. Index = 0;
  249. }
  250. return Index;
  251. }