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.

1624 lines
40 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Handles stepping, tracing and go.
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997-2001.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include "ntsdp.hpp"
  9. #define DBG_KWT 0
  10. #define DBG_UWT 0
  11. Breakpoint* g_GoBreakpoints[MAX_GO_BPS];
  12. ULONG g_NumGoBreakpoints;
  13. // Pass count of trace breakpoint.
  14. ULONG g_StepTracePassCount;
  15. ULONG64 g_StepTraceInRangeStart = (ULONG64)-1;
  16. ULONG64 g_StepTraceInRangeEnd;
  17. IMAGEHLP_LINE64 g_SrcLine; // Current source line for step/trace
  18. BOOL g_SrcLineValid; // Validity of SrcLine information
  19. BOOL g_WatchTrace;
  20. BOOL g_WatchWhole;
  21. ADDR g_WatchTarget;
  22. ULONG64 g_WatchInitialSP;
  23. ULONG64 g_WatchBeginCurFunc = 1;
  24. ULONG64 g_WatchEndCurFunc;
  25. WatchFunctions g_WatchFunctions;
  26. //----------------------------------------------------------------------------
  27. //
  28. // WatchFunctions.
  29. //
  30. //----------------------------------------------------------------------------
  31. WatchFunctions::WatchFunctions(void)
  32. {
  33. m_Started = FALSE;
  34. }
  35. void
  36. WatchFunctions::Start(void)
  37. {
  38. ULONG i;
  39. m_TotalInstr = 0;
  40. m_TotalWatchTraceEvents = 0;
  41. m_TotalWatchThreadMismatches = 0;
  42. for (i = 0; i < WF_BUCKETS; i++)
  43. {
  44. m_Funcs[i] = NULL;
  45. }
  46. m_Sorted = NULL;
  47. m_CallTop = NULL;
  48. m_CallBot = NULL;
  49. m_CallLevel = 0;
  50. m_Started = TRUE;
  51. }
  52. void
  53. WatchFunctions::End(PADDR PcAddr)
  54. {
  55. g_StepTracePassCount = 0;
  56. g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
  57. if (IS_CONN_KERNEL_TARGET())
  58. {
  59. g_DbgKdTransport->m_BreakIn = FALSE;
  60. }
  61. if (!m_Started)
  62. {
  63. return;
  64. }
  65. ULONG TotalInstr;
  66. if (IS_KERNEL_TARGET())
  67. {
  68. PDBGKD_TRACE_DATA td = (PDBGKD_TRACE_DATA)g_StateChangeData;
  69. if (g_WatchWhole)
  70. {
  71. if (td[1].s.Instructions == TRACE_DATA_INSTRUCTIONS_BIG)
  72. {
  73. TotalInstr = td[2].LongNumber;
  74. }
  75. else
  76. {
  77. TotalInstr = td[1].s.Instructions;
  78. }
  79. }
  80. else
  81. {
  82. if (PcAddr != NULL)
  83. {
  84. g_Target->ProcessWatchTraceEvent(td, *PcAddr);
  85. }
  86. while (m_CallTop != NULL)
  87. {
  88. PopCall();
  89. }
  90. TotalInstr = m_TotalInstr;
  91. }
  92. g_BreakpointsSuspended = FALSE;
  93. g_WatchInitialSP = 0;
  94. }
  95. else
  96. {
  97. if (m_CallTop != NULL)
  98. {
  99. OutputCall(m_CallTop);
  100. }
  101. while (m_CallTop != NULL)
  102. {
  103. PopCall();
  104. }
  105. TotalInstr = m_TotalInstr;
  106. dprintf("\n");
  107. }
  108. m_Started = FALSE;
  109. dprintf("%d instructions were executed in %d events "
  110. "(%d from other threads)\n",
  111. TotalInstr,
  112. m_TotalWatchTraceEvents,
  113. m_TotalWatchThreadMismatches);
  114. if (!g_WatchWhole)
  115. {
  116. OutputFunctions();
  117. }
  118. if (!IS_KERNEL_TARGET())
  119. {
  120. OutputSysCallFunctions();
  121. }
  122. dprintf("\n");
  123. Clear();
  124. }
  125. void
  126. WatchFunctions::OutputFunctions(void)
  127. {
  128. WatchFunction* Func;
  129. dprintf("\n%-43.43s Invocations MinInst MaxInst AvgInst\n",
  130. "Function Name");
  131. for (Func = m_Sorted; Func != NULL; Func = Func->Sort)
  132. {
  133. dprintf("%-47.47s%8d%8d%8d%8d\n",
  134. Func->Symbol, Func->Calls,
  135. Func->MinInstr, Func->MaxInstr,
  136. Func->Calls ? Func->TotalInstr / Func->Calls : 0);
  137. }
  138. }
  139. void
  140. WatchFunctions::OutputSysCallFunctions(void)
  141. {
  142. WatchFunction* Func;
  143. ULONG TotalSysCalls = 0;
  144. for (Func = m_Sorted; Func != NULL; Func = Func->Sort)
  145. {
  146. TotalSysCalls += Func->SystemCalls;
  147. }
  148. if (TotalSysCalls == 1)
  149. {
  150. dprintf("\n%d system call was executed\n", TotalSysCalls);
  151. }
  152. else
  153. {
  154. dprintf("\n%d system calls were executed\n", TotalSysCalls);
  155. }
  156. if (TotalSysCalls == 0)
  157. {
  158. return;
  159. }
  160. dprintf("\nCalls System Call\n");
  161. for (Func = m_Sorted; Func != NULL; Func = Func->Sort)
  162. {
  163. if (Func->SystemCalls > 0)
  164. {
  165. dprintf("%5d %s\n", Func->SystemCalls, Func->Symbol);
  166. }
  167. }
  168. }
  169. WatchFunction*
  170. WatchFunctions::FindAlways(PSTR Sym, ULONG64 Start)
  171. {
  172. WatchFunction* Func = Find(Sym);
  173. if (Func == NULL)
  174. {
  175. Func = Add(Sym, Start);
  176. }
  177. return Func;
  178. }
  179. WatchCallStack*
  180. WatchFunctions::PushCall(WatchFunction* Func)
  181. {
  182. WatchCallStack* Call = new WatchCallStack;
  183. if (Call != NULL)
  184. {
  185. ZeroMemory(Call, sizeof(*Call));
  186. Call->Prev = m_CallTop;
  187. Call->Next = NULL;
  188. if (m_CallTop == NULL)
  189. {
  190. m_CallBot = Call;
  191. }
  192. else
  193. {
  194. m_CallTop->Next = Call;
  195. m_CallLevel++;
  196. }
  197. m_CallTop = Call;
  198. Call->Func = Func;
  199. Call->Level = m_CallLevel;
  200. }
  201. return Call;
  202. }
  203. void
  204. WatchFunctions::PopCall(void)
  205. {
  206. if (m_CallTop == NULL)
  207. {
  208. return;
  209. }
  210. WatchCallStack* Call = m_CallTop;
  211. if (Call->Prev != NULL)
  212. {
  213. Call->Prev->Next = Call->Next;
  214. }
  215. else
  216. {
  217. m_CallBot = Call->Next;
  218. }
  219. if (Call->Next != NULL)
  220. {
  221. Call->Next->Prev = Call->Prev;
  222. }
  223. else
  224. {
  225. m_CallTop = Call->Prev;
  226. }
  227. m_CallLevel = m_CallTop != NULL ? m_CallTop->Level : 0;
  228. ReuseCall(Call, NULL);
  229. delete Call;
  230. }
  231. #define MAXPCOFFSET 10
  232. WatchCallStack*
  233. WatchFunctions::PopCallsToCallSite(PADDR Pc)
  234. {
  235. WatchCallStack* Call = m_CallTop;
  236. while (Call != NULL)
  237. {
  238. if ((Flat(*Pc) - Flat(Call->CallSite)) < MAXPCOFFSET)
  239. {
  240. break;
  241. }
  242. Call = Call->Prev;
  243. }
  244. if (Call == NULL)
  245. {
  246. // No matching call site found.
  247. return NULL;
  248. }
  249. // Pop off calls above the call site.
  250. while (m_CallTop != Call)
  251. {
  252. PopCall();
  253. }
  254. return m_CallTop;
  255. }
  256. WatchCallStack*
  257. WatchFunctions::PopCallsToFunctionStart(ULONG64 Start)
  258. {
  259. WatchCallStack* Call = m_CallTop;
  260. while (Call != NULL)
  261. {
  262. if (Start == Call->Func->StartOffset)
  263. {
  264. break;
  265. }
  266. Call = Call->Prev;
  267. }
  268. if (Call == NULL)
  269. {
  270. // No matching calling function found.
  271. return NULL;
  272. }
  273. // Pop off calls above the calling function.
  274. while (m_CallTop != Call)
  275. {
  276. PopCall();
  277. }
  278. return m_CallTop;
  279. }
  280. void
  281. WatchFunctions::ReuseCall(WatchCallStack* Call,
  282. WatchFunction* ReinitFunc)
  283. {
  284. if (Call->Prev != NULL)
  285. {
  286. Call->Prev->ChildInstrCount +=
  287. Call->InstrCount + Call->ChildInstrCount;
  288. }
  289. WatchFunction* Func = Call->Func;
  290. if (Func != NULL)
  291. {
  292. Func->Calls++;
  293. Func->TotalInstr += Call->InstrCount;
  294. m_TotalInstr += Call->InstrCount;
  295. if (Func->MinInstr > Call->InstrCount)
  296. {
  297. Func->MinInstr = Call->InstrCount;
  298. }
  299. if (Func->MaxInstr < Call->InstrCount)
  300. {
  301. Func->MaxInstr = Call->InstrCount;
  302. }
  303. }
  304. ZeroMemory(&Call->CallSite, sizeof(Call->CallSite));
  305. Call->Func = ReinitFunc;
  306. Call->Level = m_CallLevel;
  307. Call->InstrCount = 0;
  308. Call->ChildInstrCount = 0;
  309. }
  310. #define MAX_INDENT_LEVEL 50
  311. void
  312. WatchFunctions::OutputCall(WatchCallStack* Call)
  313. {
  314. LONG i;
  315. dprintf("%5ld %5ld [%3ld]", Call->InstrCount, Call->ChildInstrCount,
  316. Call->Level);
  317. if (Call->Level < MAX_INDENT_LEVEL)
  318. {
  319. for (i = 0; i < Call->Level; i++)
  320. {
  321. dprintf(" ");
  322. }
  323. }
  324. else
  325. {
  326. for (i = 0; i < MAX_INDENT_LEVEL + 1; i++)
  327. {
  328. dprintf(" ");
  329. }
  330. }
  331. dprintf(" %s\n", Call->Func->Symbol);
  332. }
  333. WatchFunction*
  334. WatchFunctions::Add(PSTR Sym, ULONG64 Start)
  335. {
  336. WatchFunction* Func = new WatchFunction;
  337. if (Func == NULL)
  338. {
  339. return NULL;
  340. }
  341. ZeroMemory(Func, sizeof(*Func));
  342. Func->StartOffset = Start;
  343. Func->MinInstr = -1;
  344. Func->SymbolLength = strlen(Sym);
  345. strncat(Func->Symbol, Sym, sizeof(Func->Symbol) - 1);
  346. //
  347. // Add into appropriate hash bucket.
  348. //
  349. // Hash under full name as that's what searches will
  350. // hash with.
  351. int Bucket = Hash(Sym, Func->SymbolLength);
  352. Func->Next = m_Funcs[Bucket];
  353. m_Funcs[Bucket] = Func;
  354. //
  355. // Add into sorted list.
  356. //
  357. WatchFunction* Cur, *Prev;
  358. Prev = NULL;
  359. for (Cur = m_Sorted; Cur != NULL; Cur = Cur->Sort)
  360. {
  361. if (strcmp(Func->Symbol, Cur->Symbol) <= 0)
  362. {
  363. break;
  364. }
  365. Prev = Cur;
  366. }
  367. Func->Sort = Cur;
  368. if (Prev == NULL)
  369. {
  370. m_Sorted = Func;
  371. }
  372. else
  373. {
  374. Prev->Sort = Func;
  375. }
  376. return Func;
  377. }
  378. WatchFunction*
  379. WatchFunctions::Find(PSTR Sym)
  380. {
  381. int SymLen = strlen(Sym);
  382. int Bucket = Hash(Sym, SymLen);
  383. WatchFunction* Func = m_Funcs[Bucket];
  384. while (Func != NULL)
  385. {
  386. if (SymLen == Func->SymbolLength &&
  387. !strncmp(Sym, Func->Symbol, sizeof(Func->Symbol) - 1))
  388. {
  389. break;
  390. }
  391. Func = Func->Next;
  392. }
  393. return Func;
  394. }
  395. void
  396. WatchFunctions::Clear(void)
  397. {
  398. ULONG i;
  399. for (i = 0; i < WF_BUCKETS; i++)
  400. {
  401. WatchFunction* Func;
  402. while (m_Funcs[i] != NULL)
  403. {
  404. Func = m_Funcs[i]->Next;
  405. delete m_Funcs[i];
  406. m_Funcs[i] = Func;
  407. }
  408. }
  409. m_Sorted = NULL;
  410. }
  411. //----------------------------------------------------------------------------
  412. //
  413. // ConnLiveKernelTargetInfo watch trace methods.
  414. //
  415. //----------------------------------------------------------------------------
  416. typedef struct _TRACE_DATA_SYM
  417. {
  418. ULONG64 SymMin;
  419. ULONG64 SymMax;
  420. } TRACE_DATA_SYM, *PTRACE_DATA_SYM;
  421. TRACE_DATA_SYM TraceDataSyms[256];
  422. UCHAR NextTraceDataSym = 0; // what's the next one to be replaced
  423. UCHAR NumTraceDataSyms = 0; // how many are valid?
  424. void
  425. ConnLiveKernelTargetInfo::InitializeWatchTrace(void)
  426. {
  427. ADDR SpAddr;
  428. g_Machine->GetSP(&SpAddr);
  429. g_WatchInitialSP = Flat(SpAddr);
  430. g_BreakpointsSuspended = TRUE;
  431. NextTraceDataSym = 0;
  432. NumTraceDataSyms = 0;
  433. }
  434. LONG
  435. SymNumFor (
  436. ULONG64 Pc
  437. )
  438. {
  439. long index;
  440. for ( index = 0; index < NumTraceDataSyms; index++ )
  441. {
  442. if ( (TraceDataSyms[index].SymMin <= Pc) &&
  443. (TraceDataSyms[index].SymMax > Pc) )
  444. {
  445. return index;
  446. }
  447. }
  448. return -1;
  449. }
  450. VOID
  451. PotentialNewSymbol (
  452. ULONG64 Pc
  453. )
  454. {
  455. if ( -1 != SymNumFor(Pc) )
  456. {
  457. return; // we've already seen this one
  458. }
  459. TraceDataSyms[NextTraceDataSym].SymMin = g_WatchBeginCurFunc;
  460. TraceDataSyms[NextTraceDataSym].SymMax = g_WatchEndCurFunc;
  461. //
  462. // Bump the "next" pointer, wrapping if necessary. Also bump the
  463. // "valid" pointer if we need to.
  464. //
  465. NextTraceDataSym = (NextTraceDataSym + 1) %
  466. (sizeof(TraceDataSyms) / sizeof(TraceDataSyms[0]));;
  467. if ( NumTraceDataSyms < NextTraceDataSym )
  468. {
  469. NumTraceDataSyms = NextTraceDataSym;
  470. }
  471. }
  472. void
  473. ConnLiveKernelTargetInfo::ProcessWatchTraceEvent(
  474. PDBGKD_TRACE_DATA TraceData,
  475. ADDR PcAddr
  476. )
  477. {
  478. //
  479. // All of the real information is captured in the TraceData unions
  480. // sent to us by the kernel. Here we have two main jobs:
  481. //
  482. // 1) Print out the data in the TraceData record.
  483. // 2) See if we need up update the SymNum table before
  484. // returning to the kernel.
  485. //
  486. char SymName[MAX_SYMBOL_LEN];
  487. ULONG index;
  488. ULONG64 qw;
  489. ADDR CurSP;
  490. g_WatchFunctions.RecordEvent();
  491. g_Machine->GetSP(&CurSP);
  492. if ( AddrEqu(g_WatchTarget, PcAddr) && (Flat(CurSP) >= g_WatchInitialSP) )
  493. {
  494. //
  495. // HACK HACK HACK
  496. //
  497. // fix up the last trace entry.
  498. //
  499. ULONG lastEntry = TraceData[0].LongNumber;
  500. if (lastEntry != 0)
  501. {
  502. TraceData[lastEntry].s.LevelChange = -1;
  503. // this is wrong if we
  504. // filled the symbol table!
  505. TraceData[lastEntry].s.SymbolNumber = 0;
  506. }
  507. }
  508. for ( index = 1; index < TraceData[0].LongNumber; index++ )
  509. {
  510. WatchFunction* Func;
  511. WatchCallStack* Call;
  512. ULONG64 SymOff = TraceDataSyms[TraceData[index].s.SymbolNumber].SymMin;
  513. GetSymbolStdCall(SymOff, SymName, sizeof(SymName), &qw, NULL);
  514. if (!SymName[0])
  515. {
  516. SymName[0] = '0';
  517. SymName[1] = 'x';
  518. strcpy(SymName + 2, FormatAddr64(SymOff));
  519. qw = 0;
  520. }
  521. #if DBG_KWT
  522. dprintf("!%2d: lev %2d instr %4u %s %s\n",
  523. index,
  524. TraceData[index].s.LevelChange,
  525. TraceData[index].s.Instructions ==
  526. TRACE_DATA_INSTRUCTIONS_BIG ?
  527. TraceData[index + 1].LongNumber :
  528. TraceData[index].s.Instructions,
  529. FormatAddr64(SymOff), SymName);
  530. #endif
  531. Func = g_WatchFunctions.FindAlways(SymName, SymOff - qw);
  532. if (Func == NULL)
  533. {
  534. ErrOut("Unable to allocate watch function\n");
  535. goto Flush;
  536. }
  537. Call = g_WatchFunctions.GetTopCall();
  538. if (Call == NULL || TraceData[index].s.LevelChange > 0)
  539. {
  540. if (Call == NULL)
  541. {
  542. // Treat the initial entry as a pseudo-call to
  543. // get it pushed.
  544. TraceData[index].s.LevelChange = 1;
  545. }
  546. while (TraceData[index].s.LevelChange != 0)
  547. {
  548. Call = g_WatchFunctions.PushCall(Func);
  549. if (Call == NULL)
  550. {
  551. ErrOut("Unable to allocate watch call level\n");
  552. goto Flush;
  553. }
  554. TraceData[index].s.LevelChange--;
  555. }
  556. }
  557. else if (TraceData[index].s.LevelChange < 0)
  558. {
  559. while (TraceData[index].s.LevelChange != 0)
  560. {
  561. g_WatchFunctions.PopCall();
  562. TraceData[index].s.LevelChange++;
  563. }
  564. // The level change may not actually be accurate, so
  565. // attempt to match up the current symbol offset with
  566. // some level of the call stack.
  567. Call = g_WatchFunctions.PopCallsToFunctionStart(SymOff);
  568. if (Call == NULL)
  569. {
  570. WarnOut(">> Unable to match return to %s\n", SymName);
  571. Call = g_WatchFunctions.GetTopCall();
  572. }
  573. }
  574. else
  575. {
  576. // We just made a horizontal call.
  577. g_WatchFunctions.ReuseCall(Call, Func);
  578. }
  579. ULONG InstrCount;
  580. if (TraceData[index].s.Instructions == TRACE_DATA_INSTRUCTIONS_BIG)
  581. {
  582. InstrCount = TraceData[++index].LongNumber;
  583. }
  584. else
  585. {
  586. InstrCount = TraceData[index].s.Instructions;
  587. }
  588. if (Call != NULL)
  589. {
  590. Call->InstrCount += InstrCount;
  591. g_WatchFunctions.OutputCall(Call);
  592. }
  593. }
  594. //
  595. // now see if we need to add a new symbol
  596. //
  597. index = SymNumFor(Flat(PcAddr));
  598. if (-1 == index)
  599. {
  600. /* yup, add the symbol */
  601. GetAdjacentSymOffsets(Flat(PcAddr),
  602. &g_WatchBeginCurFunc, &g_WatchEndCurFunc);
  603. if ((g_WatchBeginCurFunc == 0) ||
  604. (g_WatchEndCurFunc == (ULONG64)-1))
  605. {
  606. // Couldn't determine function, fake up
  607. // a single-byte function.
  608. g_WatchBeginCurFunc = g_WatchEndCurFunc = Flat(PcAddr);
  609. }
  610. PotentialNewSymbol(Flat(PcAddr));
  611. }
  612. else
  613. {
  614. g_WatchBeginCurFunc = TraceDataSyms[index].SymMin;
  615. g_WatchEndCurFunc = TraceDataSyms[index].SymMax;
  616. }
  617. if ((g_WatchBeginCurFunc <= Flat(g_WatchTarget)) &&
  618. (Flat(g_WatchTarget) < g_WatchEndCurFunc))
  619. {
  620. // The "exit" address is in the symbol range;
  621. // fix it so this isn't the case.
  622. if (Flat(PcAddr) < Flat(g_WatchTarget))
  623. {
  624. g_WatchEndCurFunc = Flat(g_WatchTarget);
  625. }
  626. else
  627. {
  628. g_WatchBeginCurFunc = Flat(g_WatchTarget) + 1;
  629. }
  630. }
  631. Flush:
  632. FlushCallbacks();
  633. }
  634. //----------------------------------------------------------------------------
  635. //
  636. // UserTargetInfo watch trace methods.
  637. //
  638. //----------------------------------------------------------------------------
  639. LONG g_DeferredLevelChange;
  640. void
  641. UserTargetInfo::InitializeWatchTrace(void)
  642. {
  643. g_DeferredLevelChange = 0;
  644. }
  645. void
  646. UserTargetInfo::ProcessWatchTraceEvent(
  647. PDBGKD_TRACE_DATA TraceData,
  648. ADDR PcAddr
  649. )
  650. {
  651. WatchFunction* Func;
  652. WatchCallStack* Call;
  653. ULONG64 Disp64;
  654. CHAR Disasm[MAX_DISASM_LEN];
  655. g_WatchFunctions.RecordEvent();
  656. //
  657. // Get current function and see if it matches current. If so, bump
  658. // count in current, otherwise, update to new level
  659. //
  660. GetSymbolStdCall(Flat(PcAddr), Disasm, sizeof(Disasm),
  661. &Disp64, NULL);
  662. // If there's no symbol for the current address create a
  663. // fake symbol for the instruction address.
  664. if (!Disasm[0])
  665. {
  666. Disasm[0] = '0';
  667. Disasm[1] = 'x';
  668. strcpy(Disasm + 2, FormatAddr64(Flat(PcAddr)));
  669. Disp64 = 0;
  670. }
  671. Func = g_WatchFunctions.FindAlways(Disasm, Flat(PcAddr) - Disp64);
  672. if (Func == NULL)
  673. {
  674. ErrOut("Unable to allocate watch symbol\n");
  675. goto Flush;
  676. }
  677. g_Machine->Disassemble(&PcAddr, Disasm, FALSE);
  678. Call = g_WatchFunctions.GetTopCall();
  679. if (Call == NULL)
  680. {
  681. //
  682. // First symbol in the list
  683. //
  684. Call = g_WatchFunctions.PushCall(Func);
  685. if (Call == NULL)
  686. {
  687. ErrOut("Unable to allocate watch symbol\n");
  688. goto Flush;
  689. }
  690. // At least one instruction must have executed
  691. // in this call to register it so initialize to one.
  692. // Also, one instruction was executed to get to the
  693. // first trace point so count it here.
  694. Call->InstrCount += 2;
  695. }
  696. else
  697. {
  698. if (g_DeferredLevelChange < 0)
  699. {
  700. g_DeferredLevelChange = 0;
  701. g_WatchFunctions.OutputCall(Call);
  702. // We have to see if this is really returning to a call site.
  703. // We do this because of try-finally funnies
  704. LONG OldLevel = g_WatchFunctions.GetCallLevel();
  705. WatchCallStack* CallSite =
  706. g_WatchFunctions.PopCallsToCallSite(&PcAddr);
  707. if (CallSite == NULL)
  708. {
  709. WarnOut(">> No match on ret %s\n", Disasm);
  710. }
  711. else
  712. {
  713. if (OldLevel - 1 != CallSite->Level)
  714. {
  715. WarnOut(">> More than one level popped %d -> %d\n",
  716. OldLevel, CallSite->Level);
  717. }
  718. ZeroMemory(&CallSite->CallSite, sizeof(CallSite->CallSite));
  719. Call = CallSite;
  720. }
  721. }
  722. if (Call->Func == Func && g_DeferredLevelChange == 0)
  723. {
  724. Call->InstrCount++;
  725. }
  726. else
  727. {
  728. g_WatchFunctions.OutputCall(Call);
  729. if (g_DeferredLevelChange > 0)
  730. {
  731. g_DeferredLevelChange = 0;
  732. Call = g_WatchFunctions.PushCall(Func);
  733. if (Call == NULL)
  734. {
  735. ErrOut("Unable to allocate watch symbol\n");
  736. goto Flush;
  737. }
  738. }
  739. else
  740. {
  741. g_WatchFunctions.ReuseCall(Call, Func);
  742. }
  743. // At least one instruction must have executed
  744. // in this call to register it so initialize to one.
  745. Call->InstrCount++;
  746. }
  747. }
  748. #if DBG_UWT
  749. dprintf("! %3d %s", Call != NULL ? Call->InstrCount : -1, Disasm);
  750. #endif
  751. //
  752. // Adjust watch level to compensate for kernel-mode callbacks
  753. //
  754. if (Call->InstrCount == 1)
  755. {
  756. if (!_stricmp(Call->Func->Symbol,
  757. "ntdll!_KiUserCallBackDispatcher"))
  758. {
  759. g_WatchFunctions.ChangeCallLevel(1);
  760. Call->Level = g_WatchFunctions.GetCallLevel();
  761. }
  762. else if (!_stricmp(Call->Func->Symbol, "ntdll!_ZwCallbackReturn"))
  763. {
  764. g_WatchFunctions.ChangeCallLevel(-2);
  765. Call->Level = g_WatchFunctions.GetCallLevel();
  766. }
  767. }
  768. if (g_Machine->IsCallDisasm(Disasm))
  769. {
  770. Call->CallSite = PcAddr;
  771. g_DeferredLevelChange = 1;
  772. }
  773. else if (g_Machine->IsReturnDisasm(Disasm))
  774. {
  775. g_DeferredLevelChange = -1;
  776. }
  777. else if (g_Machine->IsSystemCallDisasm(Disasm))
  778. {
  779. PSTR CallName;
  780. ULONG i;
  781. WatchCallStack* SysCall = Call;
  782. CallName = strchr(Call->Func->Symbol, '!');
  783. if (!CallName)
  784. {
  785. CallName = Call->Func->Symbol;
  786. }
  787. else
  788. {
  789. CallName++;
  790. }
  791. if (!strcmp(CallName, "*SharedUserSystemCall"))
  792. {
  793. // We're in a Whistler system call thunk
  794. // and the interesting system call symbol
  795. // is the previous level.
  796. SysCall = Call->Prev;
  797. }
  798. if (SysCall != NULL)
  799. {
  800. SysCall->Func->SystemCalls++;
  801. // ZwRaiseException returns out two levels after the call.
  802. if (!_stricmp(SysCall->Func->Symbol, "ntdll!ZwRaiseException") ||
  803. !_stricmp(SysCall->Func->Symbol, "ntdll!_ZwRaiseException"))
  804. {
  805. g_WatchFunctions.ChangeCallLevel(-1);
  806. }
  807. }
  808. g_WatchFunctions.ChangeCallLevel(-1);
  809. }
  810. Flush:
  811. FlushCallbacks();
  812. }
  813. //----------------------------------------------------------------------------
  814. //
  815. // Support functions.
  816. //
  817. //----------------------------------------------------------------------------
  818. /*** parseStepTrace - parse step or trace
  819. *
  820. * Purpose:
  821. * Parse the step ("p") or trace ("t") command. Command
  822. * syntax is "[~<thread>]p|t[b] [=<addr>][count]. Once parsed,
  823. * call fnStepTrace to step or trace the program.
  824. *
  825. * Input:
  826. * pThread - nonNULL for breakpoint on specific thread
  827. * fThreadFreeze - TRUE if all threads except pThread are frozen
  828. * *g_CurCmd - pointer to operands in command string
  829. * chcmd - 'p' for step, 't' for trace, 'w' for watch
  830. *
  831. * Output:
  832. * None.
  833. *
  834. * Exceptions:
  835. * error exit:
  836. * SYNTAX - indirectly through GetExpression
  837. *
  838. *************************************************************************/
  839. VOID
  840. parseStepTrace (
  841. PTHREAD_INFO pThread,
  842. BOOL fThreadFreeze,
  843. UCHAR chcmd
  844. )
  845. {
  846. ADDR addr1;
  847. ULONG64 value2;
  848. UCHAR ch;
  849. CHAR chAddrBuffer[MAX_SYMBOL_LEN];
  850. ULONG64 displacement;
  851. if (!IS_EXECUTION_POSSIBLE())
  852. {
  853. error(SESSIONNOTSUP);
  854. }
  855. if (!IS_MACHINE_ACCESSIBLE())
  856. {
  857. error(BADTHREAD);
  858. }
  859. if (IS_LIVE_USER_TARGET())
  860. {
  861. if (g_AllProcessFlags & ENG_PROC_EXAMINED)
  862. {
  863. ErrOut("The debugger is not attached so "
  864. "process execution cannot be monitored\n");
  865. return;
  866. }
  867. else if ((g_EngDefer & ENG_DEFER_CONTINUE_EVENT) == 0)
  868. {
  869. ErrOut("Due to the break-in timeout the debugger "
  870. "cannot step or trace\n");
  871. return;
  872. }
  873. }
  874. if (chcmd == 'w')
  875. {
  876. if (IS_KERNEL_TARGET() &&
  877. g_TargetMachineType != IMAGE_FILE_MACHINE_I386)
  878. {
  879. error(UNIMPLEMENT);
  880. }
  881. if ((PeekChar() == 't') ||
  882. (IS_KERNEL_TARGET() && PeekChar() == 'w'))
  883. {
  884. g_WatchTrace = TRUE;
  885. g_WatchWhole = *g_CurCmd == 'w';
  886. g_WatchBeginCurFunc = g_WatchEndCurFunc = 0;
  887. g_CurCmd++;
  888. }
  889. else
  890. {
  891. error(SYNTAX);
  892. }
  893. }
  894. else
  895. {
  896. g_WatchTrace = FALSE;
  897. //
  898. // if next character is 'b' and command is 't' perform branch trace
  899. //
  900. ch = PeekChar();
  901. if ((chcmd == 't') && (tolower(ch) == 'b'))
  902. {
  903. if (!g_Machine->IsStepStatusSupported(DEBUG_STATUS_STEP_BRANCH))
  904. {
  905. error(SESSIONNOTSUP);
  906. }
  907. chcmd = 'b';
  908. g_CurCmd++;
  909. }
  910. //
  911. // if next character is 'r', toggle flag to output registers
  912. // on display on breakpoint.
  913. //
  914. ch = PeekChar();
  915. if (tolower(ch) == 'r')
  916. {
  917. g_CurCmd++;
  918. g_OciOutputRegs = !g_OciOutputRegs;
  919. }
  920. }
  921. g_Machine->GetPC(&addr1); // default to current PC
  922. if (PeekChar() == '=')
  923. {
  924. g_CurCmd++;
  925. GetAddrExpression(SEGREG_CODE, &addr1);
  926. }
  927. value2 = 1;
  928. if ((ch = PeekChar()) != '\0' && ch != ';')
  929. {
  930. value2 = GetExpression();
  931. }
  932. else if (chcmd == 'w')
  933. {
  934. GetSymbolStdCall(Flat(addr1),
  935. chAddrBuffer,
  936. sizeof(chAddrBuffer),
  937. &displacement,
  938. NULL);
  939. if (displacement == 0 && chAddrBuffer[ 0 ] != '\0')
  940. {
  941. ADDR Addr2;
  942. g_Machine->GetRetAddr(&Addr2);
  943. value2 = Flat(Addr2);
  944. dprintf("Tracing %s to return address %s\n",
  945. chAddrBuffer,
  946. FormatAddr64(value2));
  947. if (g_WatchWhole)
  948. {
  949. g_WatchBeginCurFunc = value2;
  950. g_WatchEndCurFunc = 0;
  951. }
  952. }
  953. }
  954. if (((LONG)value2 <= 0) && (!g_WatchTrace))
  955. {
  956. error(SYNTAX);
  957. }
  958. fnStepTrace(&addr1,
  959. value2, // count or watch end address
  960. pThread,
  961. fThreadFreeze,
  962. chcmd);
  963. }
  964. //
  965. // Returns TRUE if the current step/trace should be passed over.
  966. //
  967. BOOL
  968. StepTracePass(
  969. PADDR pcaddr
  970. )
  971. {
  972. // If we have valid source line information and we're stepping
  973. // by source line, check and see if we moved from one line to another.
  974. if ((g_SrcOptions & SRCOPT_STEP_SOURCE) && g_SrcLineValid)
  975. {
  976. IMAGEHLP_LINE64 Line;
  977. DWORD Disp;
  978. Line.SizeOfStruct = sizeof(Line);
  979. if (SymGetLineFromAddr64(g_CurrentProcess->Handle,
  980. Flat(*pcaddr),
  981. &Disp,
  982. &Line))
  983. {
  984. if (Line.LineNumber == g_SrcLine.LineNumber)
  985. {
  986. // The common case is that we're still in the same line,
  987. // so check for a name match by pointer as a very quick
  988. // trivial accept. If there's a mismatch we need to
  989. // do the hard comparison.
  990. if (Line.FileName == g_SrcLine.FileName ||
  991. _strcmpi(Line.FileName, g_SrcLine.FileName) == 0)
  992. {
  993. // We're still on the same line so don't treat
  994. // this as motion.
  995. return TRUE;
  996. }
  997. }
  998. // We've changed lines so we drop one from the pass count.
  999. // SrcLine also needs to be updated.
  1000. g_SrcLine = Line;
  1001. }
  1002. else
  1003. {
  1004. // If we can't get line number information for the current
  1005. // address we treat it as a transition on the theory that
  1006. // it's better to stop than to skip interesting code.
  1007. g_SrcLineValid = FALSE;
  1008. }
  1009. }
  1010. if (--g_StepTracePassCount > 0)
  1011. {
  1012. if (!g_WatchFunctions.IsStarted())
  1013. {
  1014. // If the engine doesn't break for some other reason
  1015. // on this intermediate step it should output the
  1016. // step information to show the user the stepping
  1017. // path.
  1018. g_EngDefer |= ENG_DEFER_OUTPUT_CURRENT_INFO;
  1019. }
  1020. return TRUE;
  1021. }
  1022. return FALSE;
  1023. }
  1024. /*** fnStepTrace - step or trace the program
  1025. *
  1026. * Purpose:
  1027. * To continue execution of the program with a temporary
  1028. * breakpoint set to stop after the next instruction
  1029. * executed (trace - 't') or the instruction in the next
  1030. * memory location (step - 'p'). The PC is also set
  1031. * as well as a pass count variable.
  1032. *
  1033. * Input:
  1034. * addr - new value of PC
  1035. * count - passcount for step or trace
  1036. * pThread - thread pointer to qualify step/trace, NULL for all
  1037. * chStepType - 't' for trace; 'p' for step
  1038. *
  1039. * Output:
  1040. * cmdState - set to 't' for trace; 'p' for step
  1041. * g_StepTracePassCount - pass count for step/trace
  1042. *
  1043. *************************************************************************/
  1044. void
  1045. fnStepTrace (
  1046. PADDR Addr,
  1047. ULONG64 Count,
  1048. PTHREAD_INFO Thread,
  1049. BOOL ThreadFreeze,
  1050. UCHAR StepType
  1051. )
  1052. {
  1053. // If we're stepping a particular thread it better
  1054. // be the current context thread so that the machine
  1055. // activity occurs on the appropriate thread.
  1056. DBG_ASSERT(Thread == NULL || Thread == g_RegContextThread);
  1057. if ((g_SrcOptions & SRCOPT_STEP_SOURCE) && fFlat(*Addr))
  1058. {
  1059. ULONG Disp;
  1060. // Get the current line information so it's possible to
  1061. // tell when the line changes.
  1062. g_SrcLine.SizeOfStruct = sizeof(g_SrcLine);
  1063. g_SrcLineValid = SymGetLineFromAddr64(g_CurrentProcess->Handle,
  1064. Flat(*Addr),
  1065. &Disp,
  1066. &g_SrcLine);
  1067. }
  1068. g_Machine->SetPC(Addr);
  1069. g_StepTracePassCount = (ULONG)Count;
  1070. g_SelectedThread = Thread;
  1071. g_SelectExecutionThread = ThreadFreeze ? SELTHREAD_THREAD : SELTHREAD_ANY;
  1072. if (StepType == 'w')
  1073. {
  1074. ULONG NextMachine;
  1075. g_Target->InitializeWatchTrace();
  1076. g_WatchFunctions.Start();
  1077. g_WatchTarget = *Addr;
  1078. g_Machine->GetNextOffset(TRUE, &g_WatchTarget, &NextMachine);
  1079. if ( Flat(g_WatchTarget) != OFFSET_TRACE || Count != 1)
  1080. {
  1081. if (IS_KERNEL_TARGET())
  1082. {
  1083. SetupSpecialCalls();
  1084. }
  1085. g_StepTracePassCount = 0xfffffff;
  1086. if ( Count != 1 )
  1087. {
  1088. Flat(g_WatchTarget) = Count;
  1089. }
  1090. }
  1091. StepType = 't';
  1092. }
  1093. g_CmdState = StepType;
  1094. switch(StepType)
  1095. {
  1096. case 'b':
  1097. g_ExecutionStatusRequest = DEBUG_STATUS_STEP_BRANCH;
  1098. break;
  1099. case 't':
  1100. g_ExecutionStatusRequest = DEBUG_STATUS_STEP_INTO;
  1101. break;
  1102. case 'p':
  1103. default:
  1104. g_ExecutionStatusRequest = DEBUG_STATUS_STEP_OVER;
  1105. break;
  1106. }
  1107. if (Thread)
  1108. {
  1109. g_StepTraceBp->m_Process = Thread->Process;
  1110. g_StepTraceBp->m_MatchThread = Thread;
  1111. }
  1112. else
  1113. {
  1114. g_StepTraceBp->m_Process = g_CurrentProcess;
  1115. g_StepTraceBp->m_MatchThread = g_CurrentProcess->CurrentThread;
  1116. }
  1117. if (StepType == 'b')
  1118. {
  1119. // Assume that taken branch trace is always performed by
  1120. // hardware so set the g_StepTraceBp address to OFFSET_TRACE
  1121. // (the value returned by GetNextOffset to signal the
  1122. // hardware stepping mode).
  1123. DBG_ASSERT(g_Machine->
  1124. IsStepStatusSupported(DEBUG_STATUS_STEP_BRANCH));
  1125. ADDRFLAT(g_StepTraceBp->GetAddr(), OFFSET_TRACE);
  1126. }
  1127. else
  1128. {
  1129. ULONG NextMachine;
  1130. g_Machine->GetNextOffset(g_CmdState == 'p',
  1131. g_StepTraceBp->GetAddr(),
  1132. &NextMachine);
  1133. g_StepTraceBp->SetProcType(NextMachine);
  1134. }
  1135. GetCurrentMemoryOffsets(&g_StepTraceInRangeStart,
  1136. &g_StepTraceInRangeEnd);
  1137. g_StepTraceBp->m_Flags |= DEBUG_BREAKPOINT_ENABLED;
  1138. g_StepTraceCmdState = g_CmdState;
  1139. g_EngStatus &= ~ENG_STATUS_USER_INTERRUPT;
  1140. NotifyChangeEngineState(DEBUG_CES_EXECUTION_STATUS,
  1141. g_ExecutionStatusRequest, TRUE);
  1142. }
  1143. /*** fnGoExecution - continue execution with temporary breakpoints
  1144. *
  1145. * Purpose:
  1146. * Function of the "[~[<thrd>]g[=<startaddr>][<bpaddr>]*" command.
  1147. *
  1148. * Set the PC to startaddr. Set the temporary breakpoints in
  1149. * golist with the addresses pointed by *bparray and the count
  1150. * in gocnt to bpcount. Set cmdState to exit the command processor
  1151. * and continue program execution.
  1152. *
  1153. * Input:
  1154. * startaddr - new starting address
  1155. * bpcount - number of temporary breakpoints
  1156. * pThread - thread pointer to qualify <bpaddr>, NULL for all
  1157. * fThreadFreeze - TRUE if freezing all but pThread thread
  1158. * bpaddr - pointer to array of temporary breakpoints
  1159. *
  1160. * Output:
  1161. * cmdState - set to continue execution
  1162. *
  1163. *************************************************************************/
  1164. void
  1165. fnGoExecution (
  1166. ULONG ExecStatus,
  1167. PADDR StartAddr,
  1168. ULONG BpCount,
  1169. PTHREAD_INFO Thread,
  1170. BOOL ThreadFreeze,
  1171. PADDR BpArray
  1172. )
  1173. {
  1174. ULONG Count;
  1175. // If we're resuming a particular thread it better
  1176. // be the current context thread so that the machine
  1177. // activity occurs on the appropriate thread.
  1178. DBG_ASSERT(Thread == NULL || Thread == g_RegContextThread);
  1179. if (IS_CONTEXT_ACCESSIBLE())
  1180. {
  1181. g_Machine->SetPC(StartAddr);
  1182. }
  1183. // Remove old go breakpoints.
  1184. for (Count = 0; Count < g_NumGoBreakpoints; Count++)
  1185. {
  1186. if (g_GoBreakpoints[Count] != NULL)
  1187. {
  1188. RemoveBreakpoint(g_GoBreakpoints[Count]);
  1189. g_GoBreakpoints[Count] = NULL;
  1190. }
  1191. }
  1192. DBG_ASSERT(BpCount <= MAX_GO_BPS);
  1193. g_NumGoBreakpoints = BpCount;
  1194. // Add new go breakpoints.
  1195. for (Count = 0; Count < g_NumGoBreakpoints; Count++)
  1196. {
  1197. HRESULT Status;
  1198. // First try to put the breakpoint at an ID up
  1199. // and out of the way of user breakpoints.
  1200. Status = AddBreakpoint(NULL, DEBUG_BREAKPOINT_CODE |
  1201. BREAKPOINT_HIDDEN, 10000 + Count,
  1202. &g_GoBreakpoints[Count]);
  1203. if (Status != S_OK)
  1204. {
  1205. // That didn't work so try letting the engine
  1206. // pick an ID.
  1207. Status =
  1208. AddBreakpoint(NULL, DEBUG_BREAKPOINT_CODE |
  1209. BREAKPOINT_HIDDEN, DEBUG_ANY_ID,
  1210. &g_GoBreakpoints[Count]);
  1211. }
  1212. if (Status != S_OK)
  1213. {
  1214. WarnOut("Temp bp at ");
  1215. MaskOutAddr(DEBUG_OUTPUT_WARNING, BpArray);
  1216. WarnOut("failed.\n");
  1217. }
  1218. else
  1219. {
  1220. // Matches must be allowed so that temporary breakpoints
  1221. // don't interfere with permanent breakpoints.
  1222. g_GoBreakpoints[Count]->SetAddr(BpArray,
  1223. BREAKPOINT_ALLOW_MATCH);
  1224. g_GoBreakpoints[Count]->m_Flags |=
  1225. (DEBUG_BREAKPOINT_GO_ONLY |
  1226. DEBUG_BREAKPOINT_ENABLED);
  1227. }
  1228. BpArray++;
  1229. }
  1230. g_CmdState = 'g';
  1231. if (Thread == NULL || Thread == g_StepTraceBp->m_MatchThread)
  1232. {
  1233. g_StepTraceBp->m_Flags &= ~DEBUG_BREAKPOINT_ENABLED;
  1234. }
  1235. g_ExecutionStatusRequest = ExecStatus;
  1236. g_SelectExecutionThread = ThreadFreeze ? SELTHREAD_THREAD : SELTHREAD_ANY;
  1237. g_SelectedThread = Thread;
  1238. NotifyChangeEngineState(DEBUG_CES_EXECUTION_STATUS, ExecStatus, TRUE);
  1239. }
  1240. /*** parseGoCmd - parse go command
  1241. *
  1242. * Purpose:
  1243. * Parse the go ("g") command. Once parsed, call fnGoExecution
  1244. * restart program execution.
  1245. *
  1246. * Input:
  1247. * pThread - nonNULL for breakpoint on specific thread
  1248. * fThreadFreeze - TRUE if all threads except pThread are frozen
  1249. * *g_CurCmd - pointer to operands in command string
  1250. *
  1251. * Output:
  1252. * None.
  1253. *
  1254. * Exceptions:
  1255. * error exit:
  1256. * LISTSIZE - breakpoint address list too large
  1257. *
  1258. *************************************************************************/
  1259. void
  1260. parseGoCmd (
  1261. PTHREAD_INFO pThread,
  1262. BOOL fThreadFreeze
  1263. )
  1264. {
  1265. ULONG count;
  1266. ADDR addr[MAX_GO_BPS];
  1267. CHAR ch;
  1268. ADDR pcaddr;
  1269. CHAR ch2;
  1270. ULONG ExecStatus;
  1271. if (!IS_EXECUTION_POSSIBLE())
  1272. {
  1273. error(SESSIONNOTSUP);
  1274. }
  1275. if (IS_LIVE_USER_TARGET() &&
  1276. (g_AllProcessFlags & ENG_PROC_EXAMINED))
  1277. {
  1278. ErrOut("The debugger is not attached so "
  1279. "process execution cannot be monitored\n");
  1280. return;
  1281. }
  1282. if (!IS_MACHINE_SET() || IS_RUNNING(g_CmdState))
  1283. {
  1284. ErrOut("Debuggee is busy, cannot go\n");
  1285. return;
  1286. }
  1287. ExecStatus = DEBUG_STATUS_GO;
  1288. ch = (CHAR)tolower(*g_CurCmd);
  1289. if (ch == 'h' || ch == 'n')
  1290. {
  1291. ch2 = *(g_CurCmd + 1);
  1292. if (ch2 == ' ' || ch2 == '\t' || ch2 == '\0')
  1293. {
  1294. g_CurCmd++;
  1295. ExecStatus = ch == 'h' ? DEBUG_STATUS_GO_HANDLED :
  1296. DEBUG_STATUS_GO_NOT_HANDLED;
  1297. }
  1298. }
  1299. g_PrefixSymbols = TRUE;
  1300. if (IS_CONTEXT_ACCESSIBLE())
  1301. {
  1302. g_Machine->GetPC(&pcaddr); // default to current PC
  1303. }
  1304. else
  1305. {
  1306. ZeroMemory(&pcaddr, sizeof(pcaddr));
  1307. }
  1308. if (PeekChar() == '=')
  1309. {
  1310. g_CurCmd++;
  1311. GetAddrExpression(SEGREG_CODE, &pcaddr);
  1312. }
  1313. count = 0;
  1314. while ((ch = PeekChar()) != '\0' && ch != ';')
  1315. {
  1316. ULONG AddrSpace, AddrFlags;
  1317. if (count == DIMA(addr))
  1318. {
  1319. error(LISTSIZE);
  1320. }
  1321. GetAddrExpression(SEGREG_CODE, addr + (count++));
  1322. if (g_Target->
  1323. QueryAddressInformation(Flat(addr[count - 1]),
  1324. DBGKD_QUERY_MEMORY_VIRTUAL,
  1325. &AddrSpace, &AddrFlags) != S_OK)
  1326. {
  1327. ErrOut("Invalid breakpoint address\n");
  1328. error(MEMORY);
  1329. }
  1330. if (AddrSpace == DBGKD_QUERY_MEMORY_SESSION ||
  1331. !(AddrFlags & DBGKD_QUERY_MEMORY_WRITE) ||
  1332. (AddrFlags & DBGKD_QUERY_MEMORY_FIXED))
  1333. {
  1334. ErrOut("Software breakpoints cannot be used on session code, "
  1335. "ROM code or other\nread-only memory. "
  1336. "Use hardware execution breakpoints (ba e) instead.\n");
  1337. error(MEMORY);
  1338. }
  1339. }
  1340. g_PrefixSymbols = FALSE;
  1341. if (IS_USER_TARGET())
  1342. {
  1343. g_LastCommand[0] = '\0'; // null out g command
  1344. }
  1345. fnGoExecution(ExecStatus, &pcaddr, count, pThread, fThreadFreeze, addr);
  1346. }
  1347. VOID
  1348. SetupSpecialCalls(
  1349. VOID
  1350. )
  1351. {
  1352. ULONG64 SpecialCalls[10];
  1353. PULONG64 Call = SpecialCalls;
  1354. g_SrcLineValid = FALSE;
  1355. g_StepTracePassCount = 0xfffffffe;
  1356. // Set the special calls (overkill, once per boot
  1357. // would be enough, but this is easier).
  1358. if (!GetOffsetFromSym("hal!KfLowerIrql", Call, NULL) &&
  1359. !GetOffsetFromSym("hal!KeLowerIrql", Call, NULL))
  1360. {
  1361. dprintf( "Cannot find hal!KfLowerIrql/KeLowerIrql\n" );
  1362. }
  1363. else
  1364. {
  1365. Call++;
  1366. }
  1367. if (!GetOffsetFromSym("hal!KfReleaseSpinLock", Call, NULL) &&
  1368. !GetOffsetFromSym("hal!KeReleaseSpinLock", Call, NULL))
  1369. {
  1370. dprintf( "Cannot find hal!KfReleaseSpinLock/KeReleaseSpinLock\n" );
  1371. }
  1372. else
  1373. {
  1374. Call++;
  1375. }
  1376. #define GetSymWithErr(s) \
  1377. if (!GetOffsetFromSym(s, Call, NULL)) \
  1378. { \
  1379. dprintf("Cannot find " s "\n"); \
  1380. } \
  1381. else \
  1382. { \
  1383. Call++; \
  1384. }
  1385. GetSymWithErr("hal!HalRequestSoftwareInterrupt");
  1386. if (g_SystemVersion >= NT_SVER_W2K)
  1387. {
  1388. GetSymWithErr("hal!ExReleaseFastMutex");
  1389. GetSymWithErr("hal!KeReleaseQueuedSpinLock");
  1390. if (g_SystemVersion >= NT_SVER_W2K_WHISTLER)
  1391. {
  1392. GetSymWithErr("hal!KeReleaseInStackQueuedSpinLock");
  1393. }
  1394. }
  1395. GetSymWithErr("nt!SwapContext");
  1396. GetSymWithErr("nt!KiUnlockDispatcherDatabase");
  1397. GetSymWithErr("nt!KiCallUserMode");
  1398. DBG_ASSERT((ULONG)(Call - SpecialCalls) <=
  1399. sizeof(SpecialCalls) / sizeof(SpecialCalls[0]));
  1400. DbgKdSetSpecialCalls((ULONG)(Call - SpecialCalls), SpecialCalls);
  1401. }