Leaked source code of windows server 2003
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.

389 lines
9.9 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 OPTIONAL
  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 - Optionally 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. if (ARGUMENT_PRESENT(BackTraceHash)) {
  73. *BackTraceHash = HashValue;
  74. }
  75. return (USHORT)Index;
  76. }
  77. #undef RtlGetCallersAddress
  78. VOID
  79. RtlGetCallersAddress (
  80. OUT PVOID *CallersPc,
  81. OUT PVOID *CallersCallersPc
  82. )
  83. /*++
  84. Routine Description:
  85. This routine returns the address of the call to the routine that called
  86. this routine, and the address of the call to the routine that called
  87. the routine that called this routine. For example, if A called B called
  88. C which called this routine, the return addresses in B and A would be
  89. returned.
  90. Arguments:
  91. CallersPc - Supplies a pointer to a variable that receives the address
  92. of the caller of the caller of this routine (B).
  93. CallersCallersPc - Supplies a pointer to a variable that receives the
  94. address of the caller of the caller of the caller of this routine
  95. (A).
  96. Return Value:
  97. None.
  98. Note:
  99. If either of the calling stack frames exceeds the limits of the stack,
  100. they are set to NULL.
  101. --*/
  102. {
  103. CONTEXT ContextRecord;
  104. ULONG64 EstablisherFrame;
  105. PRUNTIME_FUNCTION FunctionEntry;
  106. PVOID HandlerData;
  107. ULONG64 HighLimit;
  108. ULONG64 ImageBase;
  109. ULONG64 LowLimit;
  110. //
  111. // Assume the function table entries for the various routines cannot be
  112. // found or there are not three procedure activation records on the stack.
  113. //
  114. *CallersPc = NULL;
  115. *CallersCallersPc = NULL;
  116. //
  117. // Get current stack limits, capture the current context, virtually
  118. // unwind to the caller of this routine, and lookup function table entry.
  119. //
  120. RtlpGetStackLimits(&LowLimit, &HighLimit);
  121. RtlCaptureContext(&ContextRecord);
  122. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  123. &ImageBase,
  124. NULL);
  125. //
  126. // Attempt to unwind to the caller of this routine (C).
  127. //
  128. if (FunctionEntry != NULL) {
  129. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  130. ImageBase,
  131. ContextRecord.Rip,
  132. FunctionEntry,
  133. &ContextRecord,
  134. &HandlerData,
  135. &EstablisherFrame,
  136. NULL);
  137. //
  138. // Attempt to unwind to the caller of the caller of this routine (B).
  139. //
  140. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  141. &ImageBase,
  142. NULL);
  143. if ((FunctionEntry != NULL) &&
  144. (ContextRecord.Rsp < HighLimit)) {
  145. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  146. ImageBase,
  147. ContextRecord.Rip,
  148. FunctionEntry,
  149. &ContextRecord,
  150. &HandlerData,
  151. &EstablisherFrame,
  152. NULL);
  153. *CallersPc = (PVOID)ContextRecord.Rip;
  154. //
  155. // Attempt to unwind to the caller of the caller of the caller
  156. // of the caller of this routine (A).
  157. //
  158. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  159. &ImageBase,
  160. NULL);
  161. if ((FunctionEntry != NULL) &&
  162. (ContextRecord.Rsp < HighLimit)) {
  163. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  164. ImageBase,
  165. ContextRecord.Rip,
  166. FunctionEntry,
  167. &ContextRecord,
  168. &HandlerData,
  169. &EstablisherFrame,
  170. NULL);
  171. *CallersCallersPc = (PVOID)ContextRecord.Rip;
  172. }
  173. }
  174. }
  175. return;
  176. }
  177. ULONG
  178. RtlWalkFrameChain (
  179. OUT PVOID *Callers,
  180. IN ULONG Count,
  181. IN ULONG Flags
  182. )
  183. /*++
  184. Routine Description:
  185. This function attempts to walk the call chain and capture a vector with
  186. a specified number of return addresses. It is possible that the function
  187. cannot capture the requested number of callers, in which case, the number
  188. of captured return address will be returned.
  189. Arguments:
  190. Callers - Supplies a pointer to an array that is to received the return
  191. address values.
  192. Count - Supplies the number of frames to be walked.
  193. Flags - Supplies the flags value (unused).
  194. Return value:
  195. The number of captured return addresses.
  196. --*/
  197. {
  198. CONTEXT ContextRecord;
  199. ULONG64 EstablisherFrame;
  200. PRUNTIME_FUNCTION FunctionEntry;
  201. PVOID HandlerData;
  202. ULONG64 HighLimit;
  203. ULONG64 ImageBase;
  204. ULONG Index;
  205. ULONG64 LowLimit;
  206. //
  207. // Amd64 code does not support any flags.
  208. //
  209. if (Flags != 0) {
  210. return 0;
  211. }
  212. //
  213. // In kernel mode avoid running at irql levels where we cannot
  214. // take page faults. The walking code will access various sections
  215. // from driver and system images and this will cause page faults.
  216. //
  217. #ifdef NTOS_KERNEL_RUNTIME
  218. if (KeAreAllApcsDisabled () == TRUE) {
  219. return 0;
  220. }
  221. #endif
  222. //
  223. // Get current stack limits and capture the current context.
  224. //
  225. RtlpGetStackLimits(&LowLimit, &HighLimit);
  226. RtlCaptureContext (&ContextRecord);
  227. //
  228. // Capture the requested number of return addresses if possible.
  229. //
  230. Index = 0;
  231. try {
  232. while ((Index < Count) && (ContextRecord.Rip != 0)) {
  233. //
  234. // Check the next PC value to make sure it is valid in the
  235. // current process.
  236. //
  237. #if defined(NTOS_KERNEL_RUNTIME)
  238. if ((MmIsAddressValid((PVOID)ContextRecord.Rip) == FALSE) ||
  239. ((MmIsSessionAddress((PVOID)ContextRecord.Rip) != FALSE) &&
  240. (MmGetSessionId(PsGetCurrentProcess()) == 0))) {
  241. break;
  242. }
  243. #endif
  244. //
  245. // Lookup the function table entry using the point at which control
  246. // left the function.
  247. //
  248. FunctionEntry = RtlLookupFunctionEntry(ContextRecord.Rip,
  249. &ImageBase,
  250. NULL);
  251. //
  252. // If there is a function table entry for the routine and the stack is
  253. // within limits, then virtually unwind to the caller of the routine
  254. // to obtain the return address. Otherwise, discontinue the stack walk.
  255. //
  256. if ((FunctionEntry != NULL) &&
  257. (ContextRecord.Rsp < HighLimit)) {
  258. RtlVirtualUnwind(UNW_FLAG_NHANDLER,
  259. ImageBase,
  260. ContextRecord.Rip,
  261. FunctionEntry,
  262. &ContextRecord,
  263. &HandlerData,
  264. &EstablisherFrame,
  265. NULL);
  266. Callers[Index] = (PVOID)ContextRecord.Rip;
  267. Index += 1;
  268. } else {
  269. break;
  270. }
  271. }
  272. } except(EXCEPTION_EXECUTE_HANDLER) {
  273. #if DBG
  274. DbgPrint ("Unexpected exception in RtlWalkFrameChain ...\n");
  275. DbgBreakPoint ();
  276. #endif
  277. Index = 0;
  278. }
  279. return Index;
  280. }