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.

888 lines
20 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. x86trace.c
  5. Abstract:
  6. This module contains routines to get runtime stack traces
  7. for the x86 architecture.
  8. Author:
  9. Silviu Calinoiu (silviuc) 18-Feb-2001
  10. Revision History:
  11. --*/
  12. #include <ntos.h>
  13. #include <ntrtl.h>
  14. #include "ntrtlp.h"
  15. #include <nturtl.h>
  16. #include <zwapi.h>
  17. #include <stktrace.h>
  18. #include <heap.h>
  19. #include <heappriv.h>
  20. //
  21. // Forward declarations.
  22. //
  23. BOOLEAN
  24. RtlpCaptureStackLimits (
  25. ULONG_PTR HintAddress,
  26. PULONG_PTR StartStack,
  27. PULONG_PTR EndStack);
  28. BOOLEAN
  29. RtlpStkIsPointerInDllRange (
  30. ULONG_PTR Value
  31. );
  32. BOOLEAN
  33. RtlpStkIsPointerInNtdllRange (
  34. ULONG_PTR Value
  35. );
  36. VOID
  37. RtlpCaptureContext (
  38. OUT PCONTEXT ContextRecord
  39. );
  40. BOOLEAN
  41. NtdllOkayToLockRoutine(
  42. IN PVOID Lock
  43. );
  44. //
  45. // Fuzzy stack traces
  46. //
  47. #if !defined(NTOS_KERNEL_RUNTIME)
  48. BOOLEAN RtlpFuzzyStackTracesEnabled;
  49. ULONG RtlpWalkFrameChainFuzzyCalls;
  50. #endif
  51. ULONG
  52. RtlpWalkFrameChainFuzzy (
  53. OUT PVOID *Callers,
  54. IN ULONG Count
  55. );
  56. #if !defined(RtlGetCallersAddress) && (!NTOS_KERNEL_RUNTIME)
  57. VOID
  58. RtlGetCallersAddress(
  59. OUT PVOID *CallersAddress,
  60. OUT PVOID *CallersCaller
  61. )
  62. /*++
  63. Routine Description:
  64. This routine returns the first two callers on the current stack. It should be
  65. noted that the function can miss some of the callers in the presence of FPO
  66. optimization.
  67. Arguments:
  68. CallersAddress - address to save the first caller.
  69. CallersCaller - address to save the second caller.
  70. Return Value:
  71. None. If the function does not succeed in finding the two callers
  72. it will zero the addresses where it was supposed to write them.
  73. Environment:
  74. X86, user mode and w/o having a macro with same name defined.
  75. --*/
  76. {
  77. PVOID BackTrace[ 2 ];
  78. ULONG Hash;
  79. USHORT Count;
  80. Count = RtlCaptureStackBackTrace(
  81. 2,
  82. 2,
  83. BackTrace,
  84. &Hash
  85. );
  86. if (ARGUMENT_PRESENT( CallersAddress )) {
  87. if (Count >= 1) {
  88. *CallersAddress = BackTrace[ 0 ];
  89. }
  90. else {
  91. *CallersAddress = NULL;
  92. }
  93. }
  94. if (ARGUMENT_PRESENT( CallersCaller )) {
  95. if (Count >= 2) {
  96. *CallersCaller = BackTrace[ 1 ];
  97. }
  98. else {
  99. *CallersCaller = NULL;
  100. }
  101. }
  102. return;
  103. }
  104. #endif // !defined(RtlGetCallersAddress) && (!NTOS_KERNEL_RUNTIME)
  105. /////////////////////////////////////////////////////////////////////
  106. //////////////////////////////////////////// RtlCaptureStackBackTrace
  107. /////////////////////////////////////////////////////////////////////
  108. USHORT
  109. RtlCaptureStackBackTrace(
  110. IN ULONG FramesToSkip,
  111. IN ULONG FramesToCapture,
  112. OUT PVOID *BackTrace,
  113. OUT PULONG BackTraceHash
  114. )
  115. /*++
  116. Routine Description:
  117. This routine walks up the stack frames, capturing the return address from
  118. each frame requested.
  119. Arguments:
  120. FramesToSkip - frames detected but not included in the stack trace
  121. FramesToCapture - frames to be captured in the stack trace buffer.
  122. One of the frames will be for RtlCaptureStackBackTrace.
  123. BackTrace - stack trace buffer
  124. BackTraceHash - very simple hash value that can be used to organize
  125. hash tables. It is just an arithmetic sum of the pointers in the
  126. stack trace buffer. If NULL then no hash value is computed.
  127. Return Value:
  128. Number of return addresses returned in the stack trace buffer.
  129. --*/
  130. {
  131. PVOID Trace [2 * MAX_STACK_DEPTH];
  132. ULONG FramesFound;
  133. ULONG HashValue;
  134. ULONG Index;
  135. //
  136. // One more frame to skip for the "capture" function (RtlWalkFrameChain).
  137. //
  138. FramesToSkip += 1;
  139. //
  140. // Sanity checks.
  141. //
  142. if (FramesToCapture + FramesToSkip >= 2 * MAX_STACK_DEPTH) {
  143. return 0;
  144. }
  145. #if defined(NTOS_KERNEL_RUNTIME)
  146. FramesFound = RtlWalkFrameChain (Trace,
  147. FramesToCapture + FramesToSkip,
  148. 0);
  149. #else
  150. if (RtlpFuzzyStackTracesEnabled) {
  151. FramesFound = RtlpWalkFrameChainFuzzy (Trace,
  152. FramesToCapture + FramesToSkip);
  153. }
  154. else {
  155. FramesFound = RtlWalkFrameChain (Trace,
  156. FramesToCapture + FramesToSkip,
  157. 0);
  158. }
  159. #endif
  160. if (FramesFound <= FramesToSkip) {
  161. return 0;
  162. }
  163. #if defined(NTOS_KERNEL_RUNTIME)
  164. Index = 0;
  165. #else
  166. //
  167. // Mark fuzzy stack traces with a FF...FF value.
  168. //
  169. if (RtlpFuzzyStackTracesEnabled) {
  170. BackTrace[0] = (PVOID)((ULONG_PTR)-1);
  171. Index = 1;
  172. }
  173. else {
  174. Index = 0;
  175. }
  176. #endif
  177. for (HashValue = 0; Index < FramesToCapture; Index++) {
  178. if (FramesToSkip + Index >= FramesFound) {
  179. break;
  180. }
  181. BackTrace[Index] = Trace[FramesToSkip + Index];
  182. HashValue += PtrToUlong(BackTrace[Index]);
  183. }
  184. if (BackTraceHash != NULL) {
  185. *BackTraceHash = HashValue;
  186. }
  187. return (USHORT)Index;
  188. }
  189. /////////////////////////////////////////////////////////////////////
  190. /////////////////////////////////////////////////// RtlWalkFrameChain
  191. /////////////////////////////////////////////////////////////////////
  192. #define SIZE_1_KB ((ULONG_PTR) 0x400)
  193. #define SIZE_1_GB ((ULONG_PTR) 0x40000000)
  194. #define PAGE_START(address) (((ULONG_PTR)address) & ~((ULONG_PTR)PAGE_SIZE - 1))
  195. #if FPO
  196. #pragma optimize( "y", off ) // disable FPO
  197. #endif
  198. ULONG
  199. RtlWalkFrameChain (
  200. OUT PVOID *Callers,
  201. IN ULONG Count,
  202. IN ULONG Flags
  203. )
  204. /*++
  205. Routine Description:
  206. This function tries to walk the EBP chain and fill out a vector of
  207. return addresses. It is possible that the function cannot fill the
  208. requested number of callers. In this case the function will just return
  209. with a smaller stack trace. In kernel mode the function should not take
  210. any exceptions (page faults) because it can be called at all sorts of
  211. irql levels.
  212. The `Flags' parameter is used for future extensions. A zero value will be
  213. compatible with new stack walking algorithms.
  214. A value of 1 for `Flags' means we are running in K-mode and we want to get
  215. the user mode stack trace.
  216. Return value:
  217. The number of identified return addresses on the stack. This can be less
  218. then the Count requested.
  219. --*/
  220. {
  221. ULONG_PTR Fp, NewFp, ReturnAddress;
  222. ULONG Index;
  223. ULONG_PTR StackEnd, StackStart;
  224. BOOLEAN Result;
  225. BOOLEAN InvalidFpValue;
  226. //
  227. // Get the current EBP pointer which is supposed to
  228. // be the start of the EBP chain.
  229. //
  230. _asm mov Fp, EBP;
  231. StackStart = Fp;
  232. InvalidFpValue = FALSE;
  233. if (Flags == 0) {
  234. if (! RtlpCaptureStackLimits (Fp, &StackStart, &StackEnd)) {
  235. return 0;
  236. }
  237. }
  238. try {
  239. #if defined(NTOS_KERNEL_RUNTIME)
  240. //
  241. // If we need to get the user mode stack trace from kernel mode
  242. // figure out the proper limits.
  243. //
  244. if (Flags == 1) {
  245. PKTHREAD Thread = KeGetCurrentThread ();
  246. PTEB Teb;
  247. PKTRAP_FRAME TrapFrame;
  248. TrapFrame = Thread->TrapFrame;
  249. Teb = Thread->Teb;
  250. //
  251. // If this is a system thread, it has no Teb and no kernel mode
  252. // stack, so check for it so we don't dereference NULL.
  253. //
  254. // If there is no trap frame then we are probably in an APC.
  255. // User mode stacks for APC's are not important.
  256. //
  257. if (Teb == NULL || TrapFrame == NULL || KeIsAttachedProcess()) {
  258. return 0;
  259. }
  260. StackStart = (ULONG_PTR)(Teb->NtTib.StackLimit);
  261. StackEnd = (ULONG_PTR)(Teb->NtTib.StackBase);
  262. Fp = (ULONG_PTR)(TrapFrame->Ebp);
  263. if (StackEnd <= StackStart) {
  264. return 0;
  265. }
  266. ProbeForRead (StackStart, StackEnd - StackStart, sizeof (UCHAR));
  267. }
  268. #endif
  269. for (Index = 0; Index < Count; Index += 1) {
  270. if (Fp >= StackEnd || StackEnd - Fp < sizeof(ULONG_PTR) * 2) {
  271. break;
  272. }
  273. NewFp = *((PULONG_PTR)(Fp + 0));
  274. ReturnAddress = *((PULONG_PTR)(Fp + sizeof(ULONG_PTR)));
  275. //
  276. // Figure out if the new frame pointer is ok. This validation
  277. // should avoid all exceptions in kernel mode because we always
  278. // read within the current thread's stack and the stack is
  279. // guaranteed to be in memory (no page faults). It is also guaranteed
  280. // that we do not take random exceptions in user mode because we always
  281. // keep the frame pointer within stack limits.
  282. //
  283. if (! (Fp < NewFp && NewFp < StackEnd)) {
  284. InvalidFpValue = TRUE;
  285. }
  286. //
  287. // Figure out if the return address is ok. If return address
  288. // is a stack address or <64k then something is wrong. There is
  289. // no reason to return garbage to the caller therefore we stop.
  290. //
  291. if (StackStart < ReturnAddress && ReturnAddress < StackEnd) {
  292. break;
  293. }
  294. #if defined(NTOS_KERNEL_RUNTIME)
  295. if (Flags == 0 && ReturnAddress < 0x80000000) {
  296. #else
  297. // if (ReturnAddress < 0x1000000 || ReturnAddress >= 0x80000000) {
  298. if (! RtlpStkIsPointerInDllRange(ReturnAddress)) {
  299. #endif
  300. break;
  301. }
  302. //
  303. // Store new fp and return address and move on.
  304. // If the new FP value is bogus but the return address
  305. // looks ok then we still save the address.
  306. //
  307. if (InvalidFpValue) {
  308. Callers[Index] = (PVOID)ReturnAddress;
  309. Index += 1;
  310. break;
  311. }
  312. else {
  313. Fp = NewFp;
  314. Callers[Index] = (PVOID)ReturnAddress;
  315. }
  316. }
  317. }
  318. except (EXCEPTION_EXECUTE_HANDLER) {
  319. Index = 0;
  320. }
  321. //
  322. // Return the number of return addresses identified on the stack.
  323. //
  324. return Index;
  325. }
  326. #if FPO
  327. #pragma optimize( "y", off ) // disable FPO
  328. #endif
  329. #if !defined(NTOS_KERNEL_RUNTIME)
  330. ULONG
  331. RtlpWalkFrameChainFuzzy (
  332. OUT PVOID *Callers,
  333. IN ULONG Count
  334. )
  335. /*++
  336. Routine Description:
  337. This function tries to walk the EBP chain and fill out a vector of
  338. return addresses. The function works only on x86. If the EBP chain ends
  339. it will try to pick up the start of the next one. Therefore this will not
  340. give an accurate stack trace but rather something that a desperate developer
  341. might find useful in chasing a leak.
  342. Return value:
  343. The number of identified return addresses on the stack.
  344. --*/
  345. {
  346. ULONG_PTR Fp, NewFp, ReturnAddress, NextPtr;
  347. ULONG Index;
  348. ULONG_PTR StackEnd, StackStart;
  349. BOOLEAN Result;
  350. ULONG_PTR Esp, LastEbp;
  351. //
  352. // Get the current EBP pointer which is supposed to
  353. // be the start of the EBP chain.
  354. //
  355. _asm mov Fp, EBP;
  356. if (! RtlpCaptureStackLimits (Fp, &StackStart, &StackEnd)) {
  357. return 0;
  358. }
  359. try {
  360. for (Index = 0; Index < Count; Index += 1) {
  361. NextPtr = Fp + sizeof(ULONG_PTR);
  362. if (NextPtr >= StackEnd) {
  363. break;
  364. }
  365. NewFp = *((PULONG_PTR)Fp);
  366. if (! (Fp < NewFp && NewFp < StackEnd)) {
  367. NewFp = NextPtr;
  368. }
  369. ReturnAddress = *((PULONG_PTR)NextPtr);
  370. #if defined(NTOS_KERNEL_RUNTIME)
  371. //
  372. // If the return address is a stack address it may point to where on the stack
  373. // the real return address is (FPO) so as long as we are within stack limits lets loop
  374. // hoping to find a pointer to a real address.
  375. //
  376. if (StackStart < ReturnAddress && ReturnAddress < StackEnd) {
  377. Fp = NewFp;
  378. Index -= 1;
  379. continue;
  380. }
  381. if (ReturnAddress < 0x80000000) {
  382. #else
  383. if (! RtlpStkIsPointerInDllRange(ReturnAddress)) {
  384. #endif
  385. Fp = NewFp;
  386. Index -= 1;
  387. continue;
  388. }
  389. //
  390. // Store new fp and return address and move on.
  391. // If the new FP value is bogus but the return address
  392. // looks ok then we still save the address.
  393. //
  394. Fp = NewFp;
  395. Callers[Index] = (PVOID)ReturnAddress;
  396. }
  397. }
  398. except (EXCEPTION_EXECUTE_HANDLER) {
  399. #if DBG
  400. DbgPrint ("Unexpected exception in RtlpWalkFrameChainFuzzy ...\n");
  401. DbgBreakPoint ();
  402. #endif
  403. }
  404. //
  405. // Return the number of return addresses identified on the stack.
  406. //
  407. return Index;
  408. }
  409. #endif // #if !defined(NTOS_KERNEL_RUNTIME)
  410. /////////////////////////////////////////////////////////////////////
  411. ////////////////////////////////////////////// RtlCaptureStackContext
  412. /////////////////////////////////////////////////////////////////////
  413. #if FPO
  414. #pragma optimize( "y", off ) // disable FPO
  415. #endif
  416. ULONG
  417. RtlCaptureStackContext (
  418. OUT PULONG_PTR Callers,
  419. OUT PRTL_STACK_CONTEXT Context,
  420. IN ULONG Limit
  421. )
  422. /*++
  423. Routine Description:
  424. This routine will detect up to `Limit' potential callers from the stack.
  425. A potential caller is a pointer (PVOID) that points into one of the
  426. regions occupied by modules loaded into the process space (user mode -- dlls)
  427. or kernel space (kernel mode -- drivers).
  428. Note. Based on experiments you need to save at least 64 pointers to be sure you
  429. get a complete stack.
  430. Arguments:
  431. Callers - vector to be filled with potential return addresses. Its size is
  432. expected to be `Limit'. If it is not null then Context should be null.
  433. Context - if not null the caller wants the stack context to be saved here
  434. as opposed to the Callers parameter.
  435. Limit - # of pointers that can be written into Callers and Offsets.
  436. Return value:
  437. The number of potential callers detected and written into the
  438. `Callers' buffer.
  439. --*/
  440. {
  441. ULONG_PTR Current;
  442. ULONG_PTR Value;
  443. ULONG Index;
  444. ULONG_PTR Offset;
  445. ULONG_PTR StackStart;
  446. ULONG_PTR StackEnd;
  447. ULONG_PTR Hint;
  448. ULONG_PTR Caller;
  449. ULONG_PTR ContextSize;
  450. #ifdef NTOS_KERNEL_RUNTIME
  451. //
  452. // Avoid weird conditions. Doing this in an ISR is never a good idea.
  453. //
  454. if (KeGetCurrentIrql() > DISPATCH_LEVEL) {
  455. return 0;
  456. }
  457. #endif
  458. if (Limit == 0) {
  459. return 0;
  460. }
  461. Caller = (ULONG_PTR)_ReturnAddress();
  462. if (Context) {
  463. Context->Entry[0].Data = Caller;
  464. ContextSize = sizeof(RTL_STACK_CONTEXT) + (Limit - 1) * sizeof (RTL_STACK_CONTEXT_ENTRY);
  465. }
  466. else {
  467. Callers[0] = Caller;
  468. }
  469. //
  470. // Get stack limits
  471. //
  472. _asm mov Hint, EBP;
  473. if (! RtlpCaptureStackLimits (Hint, &StackStart, &StackEnd)) {
  474. return 0;
  475. }
  476. //
  477. // Synchronize stack traverse pointer to the next word after the first
  478. // return address.
  479. //
  480. for (Current = StackStart; Current < StackEnd; Current += sizeof(ULONG_PTR)) {
  481. if (*((PULONG_PTR)Current) == Caller) {
  482. break;
  483. }
  484. }
  485. if (Context) {
  486. Context->Entry[0].Address = Current;
  487. }
  488. //
  489. // Iterate the stack and pickup potential callers on the way.
  490. //
  491. Current += sizeof(ULONG_PTR);
  492. Index = 1;
  493. for ( ; Current < StackEnd; Current += sizeof(ULONG_PTR)) {
  494. //
  495. // If potential callers buffer is full then wrap this up.
  496. //
  497. if (Index == Limit) {
  498. break;
  499. }
  500. //
  501. // Skip `Callers' buffer because it will give false positives.
  502. // It is very likely for this to happen because most probably the buffer
  503. // is allocated somewhere upper in the call chain.
  504. //
  505. if (Context) {
  506. if (Current >= (ULONG_PTR)Context && Current < (ULONG_PTR)Context + ContextSize ) {
  507. continue;
  508. }
  509. }
  510. else {
  511. if ((PULONG_PTR)Current >= Callers && (PULONG_PTR)Current < Callers + Limit ) {
  512. continue;
  513. }
  514. }
  515. Value = *((PULONG_PTR)Current);
  516. //
  517. // Skip small numbers.
  518. //
  519. if (Value <= 0x10000) {
  520. continue;
  521. }
  522. //
  523. // Skip stack pointers.
  524. //
  525. if (Value >= StackStart && Value <= StackEnd) {
  526. continue;
  527. }
  528. //
  529. // Check if `Value' points inside one of the loaded modules.
  530. //
  531. if (RtlpStkIsPointerInDllRange (Value)) {
  532. if (Context) {
  533. Context->Entry[Index].Address = Current;
  534. Context->Entry[Index].Data = Value;
  535. }
  536. else {
  537. Callers[Index] = Value;
  538. }
  539. Index += 1;
  540. }
  541. }
  542. if (Context) {
  543. Context->NumberOfEntries = Index;
  544. }
  545. return Index;
  546. }
  547. /////////////////////////////////////////////////////////////////////
  548. /////////////////////////////////////////////////// Dll ranges bitmap
  549. /////////////////////////////////////////////////////////////////////
  550. //
  551. // DLL ranges bitmap
  552. //
  553. // This range scheme is needed in order to capture stack contexts on x86
  554. // machines fast. On IA64 there are totally different algorithms for getting
  555. // stack traces.
  556. //
  557. // Every bit represents 1Mb of virtual space. Since we use the code either
  558. // in user mode or kernel mode the first bit of a pointer is not interesting.
  559. // Therefore we have to represent 2Gb / 1Mb regions. This totals 256 bytes.
  560. //
  561. // The bits are set only in loader code paths when a DLL (or driver) gets loaded.
  562. // The writing is protected by the loader lock. The bits are read in stack
  563. // capturing function.The reading does not require lock protection.
  564. //
  565. UCHAR RtlpStkDllRanges [2048 / 8];
  566. #if !defined(NTOS_KERNEL_RUNTIME)
  567. ULONG_PTR RtlpStkNtdllStart;
  568. ULONG_PTR RtlpStkNtdllEnd;
  569. BOOLEAN
  570. RtlpStkIsPointerInNtdllRange (
  571. ULONG_PTR Value
  572. )
  573. {
  574. if (RtlpStkNtdllStart == 0) {
  575. return FALSE;
  576. }
  577. if (RtlpStkNtdllStart <= Value && Value < RtlpStkNtdllEnd) {
  578. return TRUE;
  579. }
  580. else {
  581. return FALSE;
  582. }
  583. }
  584. #endif
  585. BOOLEAN
  586. RtlpStkIsPointerInDllRange (
  587. ULONG_PTR Value
  588. )
  589. {
  590. ULONG Index;
  591. Value &= ~0x80000000;
  592. Index = (ULONG)(Value >> 20);
  593. if (RtlpStkDllRanges[Index >> 3] & (UCHAR)(1 << (Index & 7))) {
  594. return TRUE;
  595. }
  596. else {
  597. return FALSE;
  598. }
  599. }
  600. VOID
  601. RtlpStkMarkDllRange (
  602. PLDR_DATA_TABLE_ENTRY DllEntry
  603. )
  604. /*++
  605. Routine description:
  606. This routine marks the corresponding bits for the loaded dll in the
  607. RtlpStkDllRanges variable. This global is used within RtlpDetectDllReferences
  608. to save a stack context.
  609. Arguments:
  610. Loader structure for a loaded dll.
  611. Return value:
  612. None.
  613. Environment:
  614. In user mode this function is called from loader code paths. The Peb->LoaderLock
  615. is always held while executing this function.
  616. --*/
  617. {
  618. PVOID Base;
  619. ULONG Size;
  620. PCHAR Current, Start;
  621. ULONG Index;
  622. ULONG_PTR Value;
  623. Base = DllEntry->DllBase;
  624. Size = DllEntry->SizeOfImage;
  625. //
  626. // Find out where is ntdll loaded if we do not know yet.
  627. //
  628. #if !defined(NTOS_KERNEL_RUNTIME)
  629. if (RtlpStkNtdllStart == 0) {
  630. UNICODE_STRING NtdllName;
  631. RtlInitUnicodeString (&NtdllName, L"ntdll.dll");
  632. if (RtlEqualUnicodeString (&(DllEntry->BaseDllName), &NtdllName, TRUE)) {
  633. RtlpStkNtdllStart = (ULONG_PTR)Base;
  634. RtlpStkNtdllEnd = (ULONG_PTR)Base + Size;
  635. }
  636. }
  637. #endif
  638. Start = (PCHAR)Base;
  639. for (Current = Start; Current < Start + Size; Current += 0x100000) {
  640. Value = (ULONG_PTR)Current & ~0x80000000;
  641. Index = (ULONG)(Value >> 20);
  642. RtlpStkDllRanges[Index >> 3] |= (UCHAR)(1 << (Index & 7));
  643. }
  644. }