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.

1620 lines
38 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Debuggee state buffers.
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999-2000.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include "precomp.hxx"
  9. #pragma hdrstop
  10. #include <malloc.h>
  11. #if 0
  12. #define DBG_BUFFER
  13. #endif
  14. StateBuffer g_UiOutputCapture(256);
  15. //----------------------------------------------------------------------------
  16. //
  17. // StateBuffer.
  18. //
  19. //----------------------------------------------------------------------------
  20. StateBuffer::StateBuffer(ULONG ChangeBy)
  21. {
  22. Dbg_InitializeCriticalSection(&m_Lock);
  23. Flink = NULL;
  24. Blink = NULL;
  25. m_ChangeBy = ChangeBy;
  26. m_Win = NULL;
  27. m_UpdateTypes = 0;
  28. m_UpdateType = UPDATE_BUFFER;
  29. m_UpdateMessage = WU_UPDATE;
  30. m_Status = S_OK;
  31. // The buffer must start out with an outstanding
  32. // read request to indicate that it doesn't have valid content.
  33. m_ReadRequest = 1;
  34. m_ReadDone = 0;
  35. SetNoData();
  36. }
  37. StateBuffer::~StateBuffer(void)
  38. {
  39. Free();
  40. Dbg_DeleteCriticalSection(&m_Lock);
  41. }
  42. PVOID
  43. StateBuffer::AddData(ULONG Len)
  44. {
  45. PVOID Ret;
  46. ULONG Needed;
  47. Needed = m_DataUsed + Len;
  48. if (Needed > m_DataLen)
  49. {
  50. if (Resize(Needed) != S_OK)
  51. {
  52. return NULL;
  53. }
  54. }
  55. Ret = m_Data + m_DataUsed;
  56. m_DataUsed += Len;
  57. return Ret;
  58. }
  59. BOOL
  60. StateBuffer::AddString(PCSTR Str, BOOL SoftTerminate)
  61. {
  62. ULONG Len = strlen(Str) + 1;
  63. PSTR Buf = (PSTR)AddData(Len);
  64. if (Buf != NULL)
  65. {
  66. memcpy(Buf, Str, Len);
  67. if (SoftTerminate)
  68. {
  69. // Back up to pack strings without intervening
  70. // terminators. Buffer isn't shrunk so terminator
  71. // remains to terminate the overall buffer until
  72. // new data.
  73. RemoveTail(1);
  74. }
  75. return TRUE;
  76. }
  77. return FALSE;
  78. }
  79. void
  80. StateBuffer::RemoveHead(ULONG Len)
  81. {
  82. if (Len > m_DataUsed)
  83. {
  84. Len = m_DataUsed;
  85. }
  86. ULONG Left = m_DataUsed - Len;
  87. if (Len > 0 && Left > 0)
  88. {
  89. memmove(m_Data, (PBYTE)m_Data + Len, Left);
  90. }
  91. m_DataUsed = Left;
  92. }
  93. void
  94. StateBuffer::RemoveMiddle(ULONG Start, ULONG Len)
  95. {
  96. if (Start >= m_DataUsed)
  97. {
  98. return;
  99. }
  100. if (Start + Len > m_DataUsed)
  101. {
  102. Len = m_DataUsed - Start;
  103. }
  104. ULONG Left = m_DataUsed - Len - Start;
  105. if (Len > 0 && Left > 0)
  106. {
  107. memmove(m_Data + Start, (PBYTE)m_Data + Start + Len, Left);
  108. }
  109. m_DataUsed = Start + Left;
  110. }
  111. void
  112. StateBuffer::RemoveTail(ULONG Len)
  113. {
  114. if (Len > m_DataUsed)
  115. {
  116. Len = m_DataUsed;
  117. }
  118. m_DataUsed -= Len;
  119. }
  120. HRESULT
  121. StateBuffer::Resize(ULONG Len)
  122. {
  123. PBYTE NewData;
  124. ULONG NewLen;
  125. if (Len == m_DataLen)
  126. {
  127. return S_OK;
  128. }
  129. NewLen = m_DataLen;
  130. if (Len < NewLen)
  131. {
  132. do
  133. {
  134. NewLen -= m_ChangeBy;
  135. }
  136. while (NewLen > Len);
  137. NewLen += m_ChangeBy;
  138. }
  139. else
  140. {
  141. do
  142. {
  143. NewLen += m_ChangeBy;
  144. }
  145. while (NewLen < Len);
  146. }
  147. #if DBG
  148. // Force every resize to go to a new memory block
  149. // and backfill the old block to make it obvious
  150. // when pointers are being held across resizes.
  151. if (NewLen == 0)
  152. {
  153. free(m_Data);
  154. NewData = NULL;
  155. }
  156. else
  157. {
  158. NewData = (PBYTE)malloc(NewLen);
  159. if (NewData != NULL && m_Data != NULL)
  160. {
  161. ULONG OldLen = _msize(m_Data);
  162. ULONG CopyLen = min(OldLen, NewLen);
  163. memcpy(NewData, m_Data, CopyLen);
  164. memset(m_Data, 0xfe, OldLen);
  165. free(m_Data);
  166. }
  167. }
  168. #else
  169. NewData = (PBYTE)realloc(m_Data, NewLen);
  170. #endif
  171. if (NewLen > 0 && NewData == NULL)
  172. {
  173. return E_OUTOFMEMORY;
  174. }
  175. m_Data = NewData;
  176. m_DataLen = NewLen;
  177. return S_OK;
  178. }
  179. void
  180. StateBuffer::Free(void)
  181. {
  182. free(m_Data);
  183. SetNoData();
  184. }
  185. HRESULT
  186. StateBuffer::Update(void)
  187. {
  188. ULONG Request;
  189. // First sample the request value. This
  190. // value will be set as the done value if
  191. // a read is performed and therefore must
  192. // be sampled first to make it the most
  193. // conservative estimate of what was done.
  194. Request = m_ReadRequest;
  195. if (Request != m_ReadDone)
  196. {
  197. LockStateBuffer(this);
  198. m_Status = ReadState();
  199. // Always mark the buffer with the latest completed
  200. // sequence so that errors get picked up in addition
  201. // to successful reads.
  202. m_ReadDone = Request;
  203. #ifdef DBG_BUFFER
  204. if (m_Status != S_OK)
  205. {
  206. DebugPrint("State buffer %p:%d fill failed, 0x%X\n",
  207. this, m_enumType, m_Status);
  208. }
  209. if (m_ReadRequest != m_ReadDone)
  210. {
  211. DebugPrint("State buffer %p:%d fill out of date, "
  212. "req %X, done %X\n",
  213. this, m_enumType, m_ReadRequest, m_ReadDone);
  214. }
  215. #endif
  216. UnlockStateBuffer(this);
  217. if (m_Win != NULL)
  218. {
  219. PostMessage(m_Win, m_UpdateMessage, 0, 0);
  220. }
  221. if (m_Status == S_OK && m_UpdateTypes)
  222. {
  223. UpdateBufferWindows(m_UpdateTypes, m_UpdateType);
  224. }
  225. }
  226. return m_Status;
  227. }
  228. void
  229. StateBuffer::UiRequestRead(void)
  230. {
  231. //
  232. // Called on the UI thread.
  233. //
  234. // No need to lock here as a race for
  235. // the read request value is not a problem.
  236. // If the read request value is sampled early
  237. // and a read request does not occur it'll
  238. // happen the next time around since this routine
  239. // also wakes the engine.
  240. RequestRead();
  241. UpdateEngine();
  242. }
  243. HRESULT
  244. StateBuffer::UiLockForRead(void)
  245. {
  246. ULONG Done;
  247. //
  248. // Called on the UI thread.
  249. //
  250. // First sample the read count without locking.
  251. Done = m_ReadDone;
  252. // Now check whether the request is newer than the
  253. // last read done. The UI thread is the only thread
  254. // that updates the request count so this should be safe.
  255. if (Done == m_ReadRequest)
  256. {
  257. HRESULT Status;
  258. LockStateBuffer(this);
  259. Status = m_Status;
  260. if (FAILED(Status))
  261. {
  262. // If there was an error when filling the buffer
  263. // return it and leave the buffer unlocked.
  264. UnlockStateBuffer(this);
  265. return Status;
  266. }
  267. // Buffer is locked and valid.
  268. return S_OK;
  269. }
  270. else
  271. {
  272. // Buffer content is out-of-date so don't lock.
  273. // Make sure the engine is active to update the buffer.
  274. return S_FALSE;
  275. }
  276. }
  277. HRESULT
  278. StateBuffer::ReadState(void)
  279. {
  280. return S_OK;
  281. }
  282. //----------------------------------------------------------------------------
  283. //
  284. // OutputToStateBuffer.
  285. //
  286. //----------------------------------------------------------------------------
  287. HRESULT
  288. OutputToStateBuffer::Start(BOOL Empty)
  289. {
  290. if (Empty)
  291. {
  292. m_Buffer->Empty();
  293. }
  294. m_DataStart = m_Buffer->GetDataLen();
  295. m_Status = S_OK;
  296. m_NewLineCount = 0;
  297. m_PartialLine = 0;
  298. return S_OK;
  299. }
  300. HRESULT
  301. OutputToStateBuffer::End(BOOL RemoveLastNewLine)
  302. {
  303. if (RemoveLastNewLine && m_PartialLine == 0)
  304. {
  305. // Remove the final newline so that richedit doesn't leave
  306. // a blank line at the bottom of the window when the
  307. // text is displayed.
  308. *((PSTR)m_Buffer->GetDataBuffer() + m_Buffer->GetDataLen() - 1) = 0;
  309. }
  310. else
  311. {
  312. // Every individual line allocates space for a terminator
  313. // and then backs up. This requested space should always
  314. // be available.
  315. PVOID Data = m_Buffer->AddData(1);
  316. Assert(Data != NULL);
  317. }
  318. return m_Status;
  319. }
  320. void
  321. OutputToStateBuffer::ReplaceChar(char From, char To)
  322. {
  323. PSTR Buf = (PSTR)m_Buffer->GetDataBuffer() + m_DataStart;
  324. PSTR End = (PSTR)m_Buffer->GetDataBuffer() + m_Buffer->GetDataLen();
  325. while (Buf < End)
  326. {
  327. if (*Buf == From)
  328. {
  329. *Buf = To;
  330. }
  331. Buf++;
  332. }
  333. }
  334. STDMETHODIMP
  335. OutputToStateBuffer::Output(
  336. THIS_
  337. IN ULONG Mask,
  338. IN PCSTR Text
  339. )
  340. {
  341. if (!m_Buffer->AddString(Text, TRUE))
  342. {
  343. return E_OUTOFMEMORY;
  344. }
  345. AddLines(Text);
  346. return S_OK;
  347. }
  348. void
  349. OutputToStateBuffer::AddLines(PCSTR Start)
  350. {
  351. PCSTR LastNl = Start;
  352. PCSTR Nl;
  353. for (;;)
  354. {
  355. Nl = strchr(LastNl, '\n');
  356. if (Nl == NULL)
  357. {
  358. break;
  359. }
  360. m_NewLineCount++;
  361. LastNl = Nl + 1;
  362. }
  363. // If the last newline wasn't at the end of the text there's
  364. // a partial line which needs to count in the line count
  365. // but only until a finishing newline comes in.
  366. m_PartialLine = *LastNl != 0 ? 1 : 0;
  367. }
  368. OutputToStateBuffer g_OutStateBuf;
  369. OutputToStateBuffer g_UiOutStateBuf;
  370. //----------------------------------------------------------------------------
  371. //
  372. // Dynamic state buffers.
  373. //
  374. //----------------------------------------------------------------------------
  375. LIST_ENTRY g_StateList;
  376. DBG_CRITICAL_SECTION g_QuickLock;
  377. ULONG64 g_CodeIp;
  378. char g_CodeFileFound[MAX_SOURCE_PATH];
  379. char g_CodeSymFile[MAX_SOURCE_PATH];
  380. ULONG g_CodeLine;
  381. BOOL g_CodeUserActivated;
  382. ULONG g_CodeBufferSequence;
  383. ULONG64 g_EventIp;
  384. ULONG64 g_EventReturnAddr = DEBUG_INVALID_OFFSET;
  385. ULONG g_CurProcessId, g_CurProcessSysId;
  386. ULONG g_CurThreadId, g_CurThreadSysId;
  387. ULONG g_EventBufferRequest;
  388. ULONG g_EventBufferDone;
  389. void
  390. FillCodeBuffer(ULONG64 Ip, BOOL UserActivated)
  391. {
  392. char File[MAX_SOURCE_PATH];
  393. char Found[MAX_SOURCE_PATH];
  394. ULONG Line;
  395. ULONG64 Disp;
  396. // Fill local information rather than global information
  397. // to avoid changing the global information until all
  398. // event information has been collected.
  399. if (g_pDbgSymbols->
  400. GetLineByOffset(Ip, &Line, File, sizeof(File), NULL, &Disp) != S_OK)
  401. {
  402. // This will be hit if the buffer is too small
  403. // to hold the filename. This could be switched to dynamically
  404. // allocate the filename buffer but that seems like overkill.
  405. File[0] = 0;
  406. Found[0] = 0;
  407. }
  408. else
  409. {
  410. // Source information is one-based but the source
  411. // window lines are zero-based.
  412. Line--;
  413. // Look up the reported file along the source path.
  414. // XXX drewb - Use first-match and then element walk to
  415. // determine ambiguities and display resolution UI.
  416. if (g_pLocSymbols->
  417. FindSourceFile(0, File,
  418. DEBUG_FIND_SOURCE_BEST_MATCH |
  419. DEBUG_FIND_SOURCE_FULL_PATH,
  420. NULL, Found, sizeof(Found), NULL) != S_OK)
  421. {
  422. // XXX drewb - Display UI instead of just disabling source?
  423. Found[0] = 0;
  424. }
  425. }
  426. // Now that all of the information has been collected
  427. // take the lock and update the global state.
  428. Dbg_EnterCriticalSection(&g_QuickLock);
  429. g_CodeIp = Ip;
  430. strcpy(g_CodeFileFound, Found);
  431. strcpy(g_CodeSymFile, File);
  432. g_CodeLine = Line;
  433. g_CodeUserActivated = UserActivated;
  434. g_CodeBufferSequence++;
  435. Dbg_LeaveCriticalSection(&g_QuickLock);
  436. // Wake up the UI thread to process the new event location.
  437. UpdateUi();
  438. }
  439. void
  440. FillEventBuffer(void)
  441. {
  442. ULONG64 Ip;
  443. ULONG64 ScopeIp;
  444. ULONG64 RetAddr;
  445. ULONG ProcessId, ProcessSysId;
  446. ULONG ThreadId, ThreadSysId;
  447. ULONG Done = g_EventBufferRequest;
  448. HRESULT Status;
  449. if (g_pDbgRegisters->GetInstructionOffset(&Ip) != S_OK ||
  450. g_pDbgControl->GetReturnOffset(&RetAddr) != S_OK ||
  451. g_pDbgSystem->GetCurrentProcessId(&ProcessId) != S_OK ||
  452. g_pDbgSystem->GetCurrentThreadId(&ThreadId) != S_OK)
  453. {
  454. return;
  455. }
  456. // Kernel mode doesn't implement system IDs as the process
  457. // and threads are fakes and not real system objects. Just
  458. // use zero if E_NOTIMPL is returned.
  459. if ((Status = g_pDbgSystem->
  460. GetCurrentProcessSystemId(&ProcessSysId)) != S_OK)
  461. {
  462. if (Status == E_NOTIMPL)
  463. {
  464. ProcessSysId = 0;
  465. }
  466. else
  467. {
  468. // Unexpected error, must be a real problem.
  469. return;
  470. }
  471. }
  472. if ((Status = g_pDbgSystem->
  473. GetCurrentThreadSystemId(&ThreadSysId)) != S_OK)
  474. {
  475. if (Status == E_NOTIMPL)
  476. {
  477. ThreadSysId = 0;
  478. }
  479. else
  480. {
  481. // Unexpected error, must be a real problem.
  482. return;
  483. }
  484. }
  485. // Fill code buffer with scope Ip
  486. if (g_pDbgSymbols->GetScope(&ScopeIp, NULL, NULL, 0) != S_OK)
  487. {
  488. return;
  489. }
  490. FillCodeBuffer(ScopeIp, FALSE);
  491. g_EventIp = Ip;
  492. g_EventReturnAddr = RetAddr;
  493. g_CurProcessId = ProcessId;
  494. g_CurProcessSysId = ProcessSysId;
  495. g_CurThreadId = ThreadId;
  496. g_CurThreadSysId = ThreadSysId;
  497. if (!g_CodeLevelLocked)
  498. {
  499. ULONG CodeLevel;
  500. if (g_CodeFileFound[0] == 0)
  501. {
  502. // No source so switch to assembly mode.
  503. CodeLevel = DEBUG_LEVEL_ASSEMBLY;
  504. }
  505. else
  506. {
  507. if (GetSrcMode_StatusBar())
  508. {
  509. CodeLevel = DEBUG_LEVEL_SOURCE;
  510. }
  511. else
  512. {
  513. CodeLevel = DEBUG_LEVEL_ASSEMBLY;
  514. }
  515. }
  516. g_IgnoreCodeLevelChange = TRUE;
  517. g_pDbgControl->SetCodeLevel(CodeLevel);
  518. g_IgnoreCodeLevelChange = FALSE;
  519. }
  520. g_EventBufferDone = Done;
  521. PostMessage(g_hwndFrame, WU_UPDATE, UPDATE_EXEC, 0);
  522. }
  523. class BpStateBuffer : public StateBuffer
  524. {
  525. public:
  526. BpStateBuffer(void) :
  527. StateBuffer(256)
  528. {
  529. m_enumType = BP_BIT;
  530. m_UpdateMessage = LB_RESETCONTENT;
  531. m_UpdateTypes = (1 << DOC_WINDOW) | (1 << DISASM_WINDOW);
  532. m_UpdateType = UPDATE_BP;
  533. }
  534. virtual HRESULT ReadState(void);
  535. };
  536. // #define DBG_BPBUF
  537. #define BP_EXTRA_ENTRIES 8
  538. ULONG g_BpCount;
  539. BpStateBuffer g_PrivateBpBuffer;
  540. StateBuffer* g_BpBuffer = &g_PrivateBpBuffer;
  541. ULONG g_BpTextOffset;
  542. HRESULT
  543. BpStateBuffer::ReadState(void)
  544. {
  545. HRESULT Status;
  546. ULONG Count;
  547. ULONG TextOffset;
  548. ULONG FileOffset;
  549. BpBufferData* Data;
  550. ULONG i;
  551. PDEBUG_BREAKPOINT_PARAMETERS Params;
  552. char FileBuf[MAX_SOURCE_PATH];
  553. // Reserve room for BP descriptions in front of the text.
  554. // When doing so, reserve extra slots to allow for free
  555. // slots the next time around.
  556. Empty();
  557. Status = g_pDbgControl->GetNumberBreakpoints(&Count);
  558. if (Status != S_OK)
  559. {
  560. return Status;
  561. }
  562. TextOffset = (Count + BP_EXTRA_ENTRIES) * sizeof(BpBufferData);
  563. Data = (BpBufferData*)AddData(TextOffset);
  564. if (Data == NULL)
  565. {
  566. return E_OUTOFMEMORY;
  567. }
  568. // Allocate a temporary buffer for bulk breakpoint retrieval.
  569. Params = new DEBUG_BREAKPOINT_PARAMETERS[Count];
  570. if (Params == NULL)
  571. {
  572. return E_OUTOFMEMORY;
  573. }
  574. // GetBreakpointParameters can return S_FALSE when there
  575. // are hidden breakpoints.
  576. if (FAILED(Status = g_pDbgControl->
  577. GetBreakpointParameters(Count, NULL, 0, Params)) != S_OK)
  578. {
  579. delete Params;
  580. return Status;
  581. }
  582. // Iterate over breakpoints and retrieve offsets for
  583. // all execution breakpoints.
  584. // Take advantage of the fact that Empty does not actually
  585. // discard data to distinguish changed breakpoints from
  586. // unchanged breakpoints.
  587. ULONG Write = 0;
  588. for (i = 0; i < Count; i++)
  589. {
  590. if (Params[i].Id == DEBUG_ANY_ID ||
  591. Params[i].Offset == DEBUG_INVALID_OFFSET ||
  592. (Params[i].BreakType == DEBUG_BREAKPOINT_DATA &&
  593. Params[i].DataAccessType != DEBUG_BREAK_EXECUTE))
  594. {
  595. // Not a breakpoint that we care about, skip.
  596. continue;
  597. }
  598. // Check and see if this offset is already known.
  599. ULONG Match;
  600. for (Match = 0; Match < g_BpCount; Match++)
  601. {
  602. // NOTE: This compresses duplicate breakpoints
  603. // with a first-writer-wins on the ID.
  604. if (Data[Match].Offset == Params[i].Offset)
  605. {
  606. break;
  607. }
  608. }
  609. if (Match < g_BpCount)
  610. {
  611. BpBufferData Temp;
  612. // Keep the old record for this offset to minimize
  613. // UI updates.
  614. if (Match > Write)
  615. {
  616. Temp = Data[Match];
  617. Data[Match] = Data[Write];
  618. Data[Write] = Temp;
  619. Match = Write;
  620. }
  621. #ifdef DBG_BPBUF
  622. DebugPrint("Match %d:%I64X %d:%d into %d\n",
  623. Params[i].Id, Params[i].Offset, Data[Match].Id,
  624. Match, Write);
  625. #endif
  626. Write++;
  627. // We mostly ignore flag differences. ENABLED, however,
  628. // is important to have accurate and in the most-enabled
  629. // way.
  630. if ((Data[Match].Flags ^ Params[i].Flags) &
  631. DEBUG_BREAKPOINT_ENABLED)
  632. {
  633. if (Data[Match].Id != Params[i].Id)
  634. {
  635. Data[Match].Flags |=
  636. Params[i].Flags & DEBUG_BREAKPOINT_ENABLED;
  637. }
  638. else
  639. {
  640. Data[Match].Flags = Params[i].Flags;
  641. }
  642. Data[Match].Thread = Params[i].MatchThread;
  643. Data[Match].Sequence = g_CommandSequence;
  644. }
  645. }
  646. else
  647. {
  648. // Fill in a new record. This will potentially destroy
  649. // an old record and so reduce the effectivess of delta
  650. // checking but the front of the buffer is packed
  651. // with the extra entries to handle these changes hopefully
  652. // without eating into the actual entries.
  653. #ifdef DBG_BPBUF
  654. DebugPrint("Write %d:%I64X into %d\n", Params[i].Id,
  655. Params[i].Offset, Write);
  656. #endif
  657. Data[Write].Offset = Params[i].Offset;
  658. Data[Write].Id = Params[i].Id;
  659. Data[Write].Flags = Params[i].Flags;
  660. Data[Write].Thread = Params[i].MatchThread;
  661. Data[Write].Sequence = g_CommandSequence;
  662. Write++;
  663. }
  664. }
  665. delete Params;
  666. // Pack unused entries at the front of the buffer so that
  667. // they get used first in the next delta computation.
  668. Count += BP_EXTRA_ENTRIES;
  669. #ifdef DBG_BPBUF
  670. DebugPrint("Used %d of %d\n", Write, Count);
  671. #endif
  672. if (Write < Count)
  673. {
  674. ULONG Extra = Count - Write;
  675. memmove(Data + Extra, Data, Write * sizeof(*Data));
  676. for (i = 0; i < Extra; i++)
  677. {
  678. Data[i].Offset = DEBUG_INVALID_OFFSET;
  679. }
  680. }
  681. //
  682. // Now go through the valid breakpoints and look up
  683. // what file they're in, if any.
  684. //
  685. for (i = 0; i < Count; i++)
  686. {
  687. ULONG Line;
  688. PSTR FileSpace;
  689. // Refresh every time since growth may have caused
  690. // a realloc.
  691. Data = (BpBufferData*)m_Data;
  692. Data[i].FileOffset = 0;
  693. if (Data[i].Offset != DEBUG_INVALID_OFFSET &&
  694. g_pDbgSymbols->GetLineByOffset(Data[i].Offset, &Line,
  695. FileBuf, sizeof(FileBuf), NULL,
  696. NULL) == S_OK)
  697. {
  698. // Do this first before m_DataUsed is updated and
  699. // Data is invalidated.
  700. Data[i].FileOffset = m_DataUsed;
  701. FileSpace = (PSTR)AddData(sizeof(Line) + strlen(FileBuf) + 1);
  702. if (FileSpace == NULL)
  703. {
  704. return E_OUTOFMEMORY;
  705. }
  706. *(ULONG UNALIGNED *)FileSpace = Line;
  707. FileSpace += sizeof(Line);
  708. strcpy(FileSpace, FileBuf);
  709. }
  710. }
  711. TextOffset = m_DataUsed;
  712. g_OutStateBuf.SetBuffer(this);
  713. if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
  714. {
  715. return Status;
  716. }
  717. // Get breakpoint list.
  718. Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
  719. DEBUG_OUTCTL_OVERRIDE_MASK |
  720. DEBUG_OUTCTL_NOT_LOGGED,
  721. "bl", DEBUG_EXECUTE_NOT_LOGGED |
  722. DEBUG_EXECUTE_NO_REPEAT);
  723. if (Status == S_OK)
  724. {
  725. Status = g_OutStateBuf.End(FALSE);
  726. if (Status == S_OK)
  727. {
  728. // Separate lines by nulls to make them easier
  729. // to process as individual strings.
  730. g_OutStateBuf.ReplaceChar('\n', 0);
  731. }
  732. }
  733. else
  734. {
  735. g_OutStateBuf.End(FALSE);
  736. }
  737. if (Status == S_OK)
  738. {
  739. g_BpCount = Count;
  740. g_BpTextOffset = TextOffset;
  741. }
  742. return Status;
  743. }
  744. class BpCmdsStateBuffer : public StateBuffer
  745. {
  746. public:
  747. BpCmdsStateBuffer(void) :
  748. StateBuffer(256)
  749. {
  750. m_enumType = BP_CMDS_BIT;
  751. }
  752. virtual HRESULT ReadState(void);
  753. };
  754. BpCmdsStateBuffer g_PrivateBpCmdsBuffer;
  755. StateBuffer* g_BpCmdsBuffer = &g_PrivateBpCmdsBuffer;
  756. HRESULT
  757. BpCmdsStateBuffer::ReadState(void)
  758. {
  759. HRESULT Status;
  760. g_OutStateBuf.SetBuffer(this);
  761. if ((Status = g_OutStateBuf.Start(TRUE)) != S_OK)
  762. {
  763. return Status;
  764. }
  765. // Get breakpoint commands.
  766. Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
  767. DEBUG_OUTCTL_OVERRIDE_MASK |
  768. DEBUG_OUTCTL_NOT_LOGGED,
  769. ".bpcmds -e -m -p 0",
  770. DEBUG_EXECUTE_NOT_LOGGED |
  771. DEBUG_EXECUTE_NO_REPEAT);
  772. if (Status == S_OK)
  773. {
  774. Status = g_OutStateBuf.End(FALSE);
  775. }
  776. else
  777. {
  778. g_OutStateBuf.End(FALSE);
  779. }
  780. return Status;
  781. }
  782. class FilterTextStateBuffer : public StateBuffer
  783. {
  784. public:
  785. FilterTextStateBuffer(void) :
  786. StateBuffer(256)
  787. {
  788. m_enumType = MINVAL_WINDOW;
  789. m_UpdateMessage = 0;
  790. }
  791. virtual HRESULT ReadState(void);
  792. };
  793. FilterTextStateBuffer g_PrivateFilterTextBuffer;
  794. StateBuffer* g_FilterTextBuffer = &g_PrivateFilterTextBuffer;
  795. HRESULT
  796. FilterTextStateBuffer::ReadState(void)
  797. {
  798. HRESULT Status;
  799. ULONG SpecEvents, SpecEx, ArbEx;
  800. ULONG i;
  801. PSTR Text;
  802. if ((Status = g_pDbgControl->
  803. GetNumberEventFilters(&SpecEvents, &SpecEx, &ArbEx)) != S_OK)
  804. {
  805. return Status;
  806. }
  807. Empty();
  808. DEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
  809. for (i = 0; i < SpecEvents; i++)
  810. {
  811. if ((Status = g_pDbgControl->
  812. GetSpecificFilterParameters(i, 1, &SpecParams)) != S_OK)
  813. {
  814. return Status;
  815. }
  816. if (SpecParams.TextSize == 0)
  817. {
  818. // Put a terminator in anyway to keep the
  819. // indexing correct.
  820. if ((Text = (PSTR)AddData(1)) == NULL)
  821. {
  822. return E_OUTOFMEMORY;
  823. }
  824. *Text = 0;
  825. }
  826. else
  827. {
  828. if ((Text = (PSTR)AddData(SpecParams.TextSize)) == NULL)
  829. {
  830. return E_OUTOFMEMORY;
  831. }
  832. if ((Status = g_pDbgControl->
  833. GetEventFilterText(i, Text, SpecParams.TextSize,
  834. NULL)) != S_OK)
  835. {
  836. return Status;
  837. }
  838. }
  839. }
  840. DEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
  841. for (i = 0; i < SpecEx; i++)
  842. {
  843. if ((Status = g_pDbgControl->
  844. GetExceptionFilterParameters(1, NULL, i + SpecEvents,
  845. &ExParams)) != S_OK)
  846. {
  847. return Status;
  848. }
  849. if (ExParams.TextSize == 0)
  850. {
  851. // Put a terminator in anyway to keep the
  852. // indexing correct.
  853. if ((Text = (PSTR)AddData(1)) == NULL)
  854. {
  855. return E_OUTOFMEMORY;
  856. }
  857. *Text = 0;
  858. }
  859. else
  860. {
  861. if ((Text = (PSTR)AddData(ExParams.TextSize)) == NULL)
  862. {
  863. return E_OUTOFMEMORY;
  864. }
  865. if ((Status = g_pDbgControl->
  866. GetEventFilterText(i + SpecEvents, Text, ExParams.TextSize,
  867. NULL)) != S_OK)
  868. {
  869. return Status;
  870. }
  871. }
  872. }
  873. return S_OK;
  874. }
  875. class FilterStateBuffer : public StateBuffer
  876. {
  877. public:
  878. FilterStateBuffer(void) :
  879. StateBuffer(256)
  880. {
  881. m_enumType = FILTER_BIT;
  882. m_UpdateMessage = LB_RESETCONTENT;
  883. }
  884. virtual HRESULT ReadState(void);
  885. };
  886. FilterStateBuffer g_PrivateFilterBuffer;
  887. StateBuffer* g_FilterBuffer = &g_PrivateFilterBuffer;
  888. ULONG g_FilterArgsOffset;
  889. ULONG g_FilterCmdsOffset;
  890. ULONG g_FilterWspCmdsOffset;
  891. ULONG g_NumSpecEvents, g_NumSpecEx, g_NumArbEx;
  892. HRESULT
  893. FilterStateBuffer::ReadState(void)
  894. {
  895. ULONG SpecEvents, SpecEx, ArbEx;
  896. HRESULT Status;
  897. ULONG ArgsOffset, CmdsOffset, WspCmdsOffset;
  898. PDEBUG_SPECIFIC_FILTER_PARAMETERS SpecParams;
  899. PDEBUG_EXCEPTION_FILTER_PARAMETERS ExParams;
  900. ULONG i;
  901. if ((Status = g_pDbgControl->
  902. GetNumberEventFilters(&SpecEvents, &SpecEx, &ArbEx)) != S_OK)
  903. {
  904. return Status;
  905. }
  906. Empty();
  907. if ((SpecParams = (PDEBUG_SPECIFIC_FILTER_PARAMETERS)
  908. AddData((SpecEvents * sizeof(*SpecParams) +
  909. (SpecEx + ArbEx) * sizeof(*ExParams)))) == NULL)
  910. {
  911. return E_OUTOFMEMORY;
  912. }
  913. ExParams = (PDEBUG_EXCEPTION_FILTER_PARAMETERS)(SpecParams + SpecEvents);
  914. if ((Status = g_pDbgControl->
  915. GetSpecificFilterParameters(0, SpecEvents, SpecParams)) != S_OK ||
  916. (Status = g_pDbgControl->
  917. GetExceptionFilterParameters(SpecEx + ArbEx, NULL, SpecEvents,
  918. ExParams)) != S_OK)
  919. {
  920. return Status;
  921. }
  922. ArgsOffset = m_DataUsed;
  923. for (i = 0; i < SpecEvents; i++)
  924. {
  925. if (SpecParams[i].ArgumentSize > 1)
  926. {
  927. PSTR Arg = (PSTR)AddData(SpecParams[i].ArgumentSize);
  928. if (Arg == NULL)
  929. {
  930. return E_OUTOFMEMORY;
  931. }
  932. if ((Status = g_pDbgControl->
  933. GetSpecificFilterArgument(i, Arg, SpecParams[i].ArgumentSize,
  934. NULL)) != S_OK)
  935. {
  936. return Status;
  937. }
  938. }
  939. }
  940. CmdsOffset = m_DataUsed;
  941. for (i = 0; i < SpecEvents; i++)
  942. {
  943. if (SpecParams[i].CommandSize > 0)
  944. {
  945. PSTR Cmd = (PSTR)AddData(SpecParams[i].CommandSize);
  946. if (Cmd == NULL)
  947. {
  948. return E_OUTOFMEMORY;
  949. }
  950. if ((Status = g_pDbgControl->
  951. GetEventFilterCommand(i, Cmd, SpecParams[i].CommandSize,
  952. NULL)) != S_OK)
  953. {
  954. return Status;
  955. }
  956. }
  957. }
  958. for (i = 0; i < SpecEx + ArbEx; i++)
  959. {
  960. if (ExParams[i].CommandSize > 0)
  961. {
  962. PSTR Cmd = (PSTR)AddData(ExParams[i].CommandSize);
  963. if (Cmd == NULL)
  964. {
  965. return E_OUTOFMEMORY;
  966. }
  967. if ((Status = g_pDbgControl->
  968. GetEventFilterCommand(i + SpecEvents,
  969. Cmd, ExParams[i].CommandSize,
  970. NULL)) != S_OK)
  971. {
  972. return Status;
  973. }
  974. }
  975. if (ExParams[i].SecondCommandSize > 0)
  976. {
  977. PSTR Cmd = (PSTR)AddData(ExParams[i].SecondCommandSize);
  978. if (Cmd == NULL)
  979. {
  980. return E_OUTOFMEMORY;
  981. }
  982. if ((Status = g_pDbgControl->
  983. GetExceptionFilterSecondCommand(i + SpecEvents,
  984. Cmd,
  985. ExParams[i].SecondCommandSize,
  986. NULL)) != S_OK)
  987. {
  988. return Status;
  989. }
  990. }
  991. }
  992. WspCmdsOffset = m_DataUsed;
  993. g_OutStateBuf.SetBuffer(this);
  994. if ((Status = g_OutStateBuf.Start(FALSE)) != S_OK)
  995. {
  996. return Status;
  997. }
  998. // Get filter commands.
  999. Status = g_pOutCapControl->Execute(DEBUG_OUTCTL_THIS_CLIENT |
  1000. DEBUG_OUTCTL_OVERRIDE_MASK |
  1001. DEBUG_OUTCTL_NOT_LOGGED,
  1002. ".sxcmds",
  1003. DEBUG_EXECUTE_NOT_LOGGED |
  1004. DEBUG_EXECUTE_NO_REPEAT);
  1005. if (Status == S_OK)
  1006. {
  1007. Status = g_OutStateBuf.End(FALSE);
  1008. }
  1009. else
  1010. {
  1011. g_OutStateBuf.End(FALSE);
  1012. }
  1013. if (Status == S_OK)
  1014. {
  1015. g_FilterArgsOffset = ArgsOffset;
  1016. g_FilterCmdsOffset = CmdsOffset;
  1017. g_FilterWspCmdsOffset = WspCmdsOffset;
  1018. g_NumSpecEvents = SpecEvents;
  1019. g_NumSpecEx = SpecEx;
  1020. g_NumArbEx = ArbEx;
  1021. }
  1022. return Status;
  1023. }
  1024. class ModuleStateBuffer : public StateBuffer
  1025. {
  1026. public:
  1027. ModuleStateBuffer(void) :
  1028. StateBuffer(256)
  1029. {
  1030. m_enumType = MODULE_BIT;
  1031. m_UpdateMessage = LB_RESETCONTENT;
  1032. }
  1033. virtual HRESULT ReadState(void);
  1034. };
  1035. ModuleStateBuffer g_PrivateModuleBuffer;
  1036. StateBuffer* g_ModuleBuffer = &g_PrivateModuleBuffer;
  1037. ULONG g_NumModules;
  1038. HRESULT
  1039. ModuleStateBuffer::ReadState(void)
  1040. {
  1041. HRESULT Status;
  1042. ULONG NumModules, Loaded, Unloaded;
  1043. PDEBUG_MODULE_PARAMETERS Params;
  1044. if ((Status = g_pDbgSymbols->GetNumberModules(&Loaded,
  1045. &Unloaded)) != S_OK)
  1046. {
  1047. return Status;
  1048. }
  1049. Empty();
  1050. NumModules = Loaded + Unloaded;
  1051. if (NumModules > 0)
  1052. {
  1053. if ((Params = (PDEBUG_MODULE_PARAMETERS)
  1054. AddData(NumModules * sizeof(*Params))) == NULL)
  1055. {
  1056. return E_OUTOFMEMORY;
  1057. }
  1058. if ((Status = g_pDbgSymbols->
  1059. GetModuleParameters(NumModules, NULL, 0, Params)) != S_OK)
  1060. {
  1061. return Status;
  1062. }
  1063. }
  1064. g_NumModules = NumModules;
  1065. return S_OK;
  1066. }
  1067. void
  1068. ReadStateBuffers(void)
  1069. {
  1070. // Fill event information first so other fills can
  1071. // refer to it.
  1072. if (g_EventBufferRequest != g_EventBufferDone)
  1073. {
  1074. FillEventBuffer();
  1075. }
  1076. g_BpBuffer->Update();
  1077. g_BpCmdsBuffer->Update();
  1078. g_FilterBuffer->Update();
  1079. g_ModuleBuffer->Update();
  1080. // No need to lock to sample the list head.
  1081. StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
  1082. StateBuffer* BufferNext;
  1083. while (Buffer != &g_StateList)
  1084. {
  1085. BufferNext = (StateBuffer*)Buffer->Flink;
  1086. if (Buffer->m_Win == NULL)
  1087. {
  1088. // This window has been closed and can be cleaned up.
  1089. Dbg_EnterCriticalSection(&g_QuickLock);
  1090. RemoveEntryList(Buffer);
  1091. Dbg_LeaveCriticalSection(&g_QuickLock);
  1092. delete Buffer;
  1093. }
  1094. else
  1095. {
  1096. Buffer->Update();
  1097. }
  1098. Buffer = BufferNext;
  1099. }
  1100. }
  1101. void
  1102. InvalidateStateBuffers(ULONG Types)
  1103. {
  1104. // This routine can be called from both
  1105. // the engine thread and the UI thread.
  1106. // Care should be taken to make the code
  1107. // here work in both threads.
  1108. if (Types & (1 << EVENT_BIT))
  1109. {
  1110. InterlockedIncrement((PLONG)&g_EventBufferRequest);
  1111. }
  1112. if (Types & (1 << BP_BIT))
  1113. {
  1114. g_BpBuffer->RequestRead();
  1115. }
  1116. if (Types & (1 << BP_CMDS_BIT))
  1117. {
  1118. g_BpCmdsBuffer->RequestRead();
  1119. }
  1120. if (Types & (1 << FILTER_BIT))
  1121. {
  1122. g_FilterBuffer->RequestRead();
  1123. }
  1124. if (Types & (1 << MODULE_BIT))
  1125. {
  1126. g_ModuleBuffer->RequestRead();
  1127. }
  1128. // This routine must hold the list lock so that it
  1129. // can traverse the list properly in the UI thread
  1130. // when the engine thread might be deleting things.
  1131. // The code in the lock should execute quickly to
  1132. // avoid contention.
  1133. Dbg_EnterCriticalSection(&g_QuickLock);
  1134. StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
  1135. while (Buffer != &g_StateList)
  1136. {
  1137. if (Types & (1 << Buffer->m_enumType))
  1138. {
  1139. // Request a read but do not send an update to
  1140. // the window. The window will display the old
  1141. // content until the buffer is updated.
  1142. Buffer->RequestRead();
  1143. }
  1144. Buffer = (StateBuffer*)Buffer->Flink;
  1145. }
  1146. Dbg_LeaveCriticalSection(&g_QuickLock);
  1147. }
  1148. void
  1149. UpdateBufferWindows(ULONG Types, UpdateType Type)
  1150. {
  1151. // This routine can be called from both
  1152. // the engine thread and the UI thread.
  1153. // Care should be taken to make the code
  1154. // here work in both threads.
  1155. // This routine must hold the list lock so that it
  1156. // can traverse the list properly in the UI thread
  1157. // when the engine thread might be deleting things.
  1158. // The code in the lock should execute quickly to
  1159. // avoid contention.
  1160. Dbg_EnterCriticalSection(&g_QuickLock);
  1161. StateBuffer* Buffer = (StateBuffer*)g_StateList.Flink;
  1162. while (Buffer != &g_StateList)
  1163. {
  1164. if ((Types & (1 << Buffer->m_enumType)) &&
  1165. Buffer->m_Win != NULL)
  1166. {
  1167. PostMessage(Buffer->m_Win, WU_UPDATE, Type, 0);
  1168. }
  1169. Buffer = (StateBuffer*)Buffer->Flink;
  1170. }
  1171. Dbg_LeaveCriticalSection(&g_QuickLock);
  1172. }
  1173. //----------------------------------------------------------------------------
  1174. //
  1175. // Static state buffers.
  1176. //
  1177. //----------------------------------------------------------------------------
  1178. class RegisterNamesStateBuffer : public StateBuffer
  1179. {
  1180. public:
  1181. RegisterNamesStateBuffer(void)
  1182. : StateBuffer(128)
  1183. {
  1184. }
  1185. virtual HRESULT ReadState(void);
  1186. };
  1187. RegisterNamesStateBuffer g_PrivateRegisterNamesBuffer;
  1188. StateBuffer* g_RegisterNamesBuffer = &g_PrivateRegisterNamesBuffer;
  1189. HRESULT
  1190. RegisterNamesStateBuffer::ReadState(void)
  1191. {
  1192. char Name[1024];
  1193. DEBUG_REGISTER_DESCRIPTION Desc;
  1194. ULONG i;
  1195. HRESULT Hr;
  1196. PSTR BufName;
  1197. ULONG Len;
  1198. Empty();
  1199. for (i = 0; i < g_NumRegisters; i++)
  1200. {
  1201. if ((Hr = g_pDbgRegisters->GetDescription(i, Name, sizeof(Name),
  1202. NULL, &Desc)) != S_OK)
  1203. {
  1204. ErrorExit(g_pDbgClient,
  1205. "Debug target initialization failed, 0x%X\n", Hr);
  1206. }
  1207. Len = strlen(Name) + 1;
  1208. BufName = (PSTR)AddData(Len);
  1209. if (BufName == NULL)
  1210. {
  1211. ErrorExit(g_pDbgClient,
  1212. "Debug target initialization failed, 0x%X\n", Hr);
  1213. }
  1214. memcpy(BufName, Name, Len);
  1215. }
  1216. return S_OK;
  1217. }
  1218. class WatchWinStateBuffer : public StateBuffer
  1219. {
  1220. public:
  1221. WatchWinStateBuffer(void)
  1222. : StateBuffer(1024)
  1223. {
  1224. }
  1225. virtual HRESULT ReadState(void);
  1226. };
  1227. WatchWinStateBuffer g_PrivateWatchWinBuffer;
  1228. StateBuffer* g_WatchWinBuffer = &g_PrivateWatchWinBuffer;
  1229. extern IDebugSymbolGroup * g_pDbgSymbolGroup;
  1230. HRESULT
  1231. WatchWinStateBuffer::ReadState(void)
  1232. {
  1233. char Name[1024];
  1234. ULONG i;
  1235. HRESULT Hr;
  1236. PSTR BufName;
  1237. ULONG Count;
  1238. Empty();
  1239. // g_pDbgSymbolGroup->GetSymbolParameters(0, 10, &SymParams[0]);
  1240. // g_pDbgSymbolGroup->OutputSymbols(0, 0, 0, 10);
  1241. return S_OK;
  1242. }
  1243. PUSHORT g_RegisterMap;
  1244. ULONG g_RegisterMapEntries;
  1245. void
  1246. GetRegisterMapText(HWND Edit)
  1247. {
  1248. ULONG i;
  1249. PSTR Name;
  1250. CHARRANGE Range;
  1251. AssertStateBufferLocked(g_RegisterNamesBuffer);
  1252. Range.cpMin = 0;
  1253. Range.cpMax = INT_MAX;
  1254. SendMessage(Edit, EM_EXSETSEL, 0, (LPARAM)&Range);
  1255. for (i = 0; i < g_NumRegisters; i++)
  1256. {
  1257. ULONG MapIndex = MAP_REGISTER(i);
  1258. Name = (PSTR)g_RegisterNamesBuffer->GetDataBuffer();
  1259. while (MapIndex-- > 0)
  1260. {
  1261. Name += strlen(Name) + 1;
  1262. }
  1263. if (i > 0)
  1264. {
  1265. SendMessage(Edit, EM_REPLACESEL, 0, (LPARAM)" ");
  1266. }
  1267. SendMessage(Edit, EM_REPLACESEL, 0, (LPARAM)Name);
  1268. }
  1269. }
  1270. void
  1271. ScanRegisterMapText(HWND Edit)
  1272. {
  1273. PSTR Text, TextBuffer;
  1274. PULONG Used, UsedBuffer;
  1275. ULONG i;
  1276. AssertStateBufferLocked(g_RegisterNamesBuffer);
  1277. //
  1278. // Allocate a buffer for the control text
  1279. // and a new register map.
  1280. //
  1281. i = (ULONG)SendMessage(Edit, WM_GETTEXTLENGTH, 0, 0) + 1;
  1282. TextBuffer = new CHAR[i];
  1283. if (TextBuffer == NULL)
  1284. {
  1285. return;
  1286. }
  1287. Text = TextBuffer;
  1288. UsedBuffer = new ULONG[g_NumRegisters];
  1289. if (UsedBuffer == NULL)
  1290. {
  1291. delete TextBuffer;
  1292. return;
  1293. }
  1294. Used = UsedBuffer;
  1295. // Map may need to change size.
  1296. delete g_RegisterMap;
  1297. g_RegisterMap = new USHORT[g_NumRegisters];
  1298. if (g_RegisterMap == NULL)
  1299. {
  1300. delete TextBuffer;
  1301. delete UsedBuffer;
  1302. return;
  1303. }
  1304. g_RegisterMapEntries = g_NumRegisters;
  1305. ZeroMemory(Used, g_NumRegisters * sizeof(Used[0]));
  1306. //
  1307. // Retrieve the text and scan it for register names.
  1308. //
  1309. GetWindowText(Edit, Text, i);
  1310. Text[i - 1] = 0;
  1311. PSTR Name;
  1312. BOOL End;
  1313. PUSHORT Map;
  1314. PUSHORT Check;
  1315. PSTR Reg;
  1316. Map = g_RegisterMap;
  1317. for (;;)
  1318. {
  1319. while (isspace(*Text))
  1320. {
  1321. Text++;
  1322. }
  1323. if (*Text == 0)
  1324. {
  1325. break;
  1326. }
  1327. // Collect name.
  1328. Name = Text;
  1329. while (*Text && !isspace(*Text))
  1330. {
  1331. Text++;
  1332. }
  1333. End = *Text == 0;
  1334. *Text = 0;
  1335. // Check against known registers.
  1336. Reg = (PSTR)g_RegisterNamesBuffer->GetDataBuffer();
  1337. for (i = 0; i < g_NumRegisters; i++)
  1338. {
  1339. if (!Used[i] && !_strcmpi(Name, Reg))
  1340. {
  1341. Used[i] = TRUE;
  1342. *Map++ = (USHORT)i;
  1343. break;
  1344. }
  1345. Reg += strlen(Reg) + 1;
  1346. }
  1347. if (End)
  1348. {
  1349. break;
  1350. }
  1351. Text++;
  1352. }
  1353. //
  1354. // Fill out any remaining map entries with registers
  1355. // which aren't in the map so far.
  1356. //
  1357. PUSHORT MapEnd = g_RegisterMap + g_RegisterMapEntries;
  1358. i = 0;
  1359. while (Map < MapEnd)
  1360. {
  1361. while (Used[i])
  1362. {
  1363. i++;
  1364. }
  1365. Assert(i < g_NumRegisters);
  1366. *Map++ = (USHORT)(i++);
  1367. }
  1368. delete TextBuffer;
  1369. delete UsedBuffer;
  1370. }