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.

1452 lines
30 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Source file searching and loading.
  4. //
  5. // Copyright (C) Microsoft Corporation, 1997-2001.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include "ntsdp.hpp"
  9. // #define DBG_SRC
  10. // #define VERBOSE_SRC
  11. ULONG g_SrcOptions;
  12. LPSTR g_SrcPath;
  13. PSRCFILE g_SrcFiles;
  14. PSRCFILE g_CurSrcFile;
  15. ULONG g_CurSrcLine;
  16. ULONG g_OciSrcBefore;
  17. ULONG g_OciSrcAfter = 1;
  18. PSRCFILE
  19. LoadSrcFile(
  20. LPSTR FileName,
  21. LPSTR RecordFileName
  22. )
  23. {
  24. PathFile* File;
  25. PSRCFILE SrcFile, Realloc;
  26. ULONG Avail;
  27. ULONG BaseLen, Len, Done;
  28. LPSTR Cur, End;
  29. ULONG Lines;
  30. LPSTR *CurLine, LineStart;
  31. ULONG ReadLen;
  32. if (OpenPathFile(FileName, g_SymOptions, &File) != S_OK)
  33. {
  34. return NULL;
  35. }
  36. BaseLen = sizeof(SRCFILE) + strlen(RecordFileName) + 1;
  37. Len = BaseLen;
  38. SrcFile = NULL;
  39. for (;;)
  40. {
  41. if (File->QueryDataAvailable(&Avail) != S_OK)
  42. {
  43. goto EH_CloseFile;
  44. }
  45. if (Avail == 0)
  46. {
  47. if (SrcFile == NULL)
  48. {
  49. goto EH_CloseFile;
  50. }
  51. break;
  52. }
  53. Realloc = (SRCFILE *)realloc(SrcFile, Len + Avail);
  54. if (Realloc == NULL)
  55. {
  56. goto EH_CloseFile;
  57. }
  58. SrcFile = Realloc;
  59. if (File->Read((LPSTR)SrcFile + Len, Avail, &Done) != S_OK ||
  60. Done < Avail)
  61. {
  62. goto EH_CloseFile;
  63. }
  64. Len += Avail;
  65. }
  66. SrcFile->File = (LPSTR)(SrcFile + 1);
  67. strcpy(SrcFile->File, RecordFileName);
  68. SrcFile->RawText = (LPSTR)SrcFile + BaseLen;
  69. Len -= BaseLen;
  70. // Count lines in the source file. Stop before the last character
  71. // to handle the case where there's a newline at the end of the
  72. // file in the same way as where there isn't one.
  73. Lines = 0;
  74. Cur = SrcFile->RawText;
  75. End = SrcFile->RawText + Len;
  76. while (Cur < End - 1)
  77. {
  78. if (*Cur++ == '\n')
  79. {
  80. Lines++;
  81. }
  82. }
  83. Lines++;
  84. SrcFile->LineText = (char **)malloc(sizeof(LPSTR) * Lines);
  85. if (SrcFile->LineText == NULL)
  86. {
  87. goto EH_CloseFile;
  88. }
  89. SrcFile->Lines = Lines;
  90. Cur = SrcFile->RawText;
  91. CurLine = SrcFile->LineText;
  92. LineStart = Cur;
  93. while (Cur < End - 1)
  94. {
  95. if (*Cur == '\n')
  96. {
  97. *CurLine++ = LineStart;
  98. *Cur = 0;
  99. LineStart = Cur+1;
  100. }
  101. else if (*Cur == '\r')
  102. {
  103. *Cur = 0;
  104. }
  105. Cur++;
  106. }
  107. *CurLine++ = LineStart;
  108. delete File;
  109. SrcFile->Next = g_SrcFiles;
  110. g_SrcFiles = SrcFile;
  111. #ifdef VERBOSE_SRC
  112. dprintf("Loaded '%s' '%s' %d lines\n", FileName, RecordFileName, Lines);
  113. #endif
  114. return SrcFile;
  115. EH_CloseFile:
  116. free(SrcFile);
  117. delete File;
  118. return NULL;
  119. }
  120. void
  121. DeleteSrcFile(
  122. PSRCFILE SrcFile
  123. )
  124. {
  125. if (g_CurSrcFile == SrcFile)
  126. {
  127. g_CurSrcFile = NULL;
  128. g_CurSrcLine = 0;
  129. }
  130. free(SrcFile->LineText);
  131. free(SrcFile);
  132. }
  133. BOOL
  134. MatchSrcFileName(
  135. PSRCFILE SrcFile,
  136. LPSTR File
  137. )
  138. {
  139. LPSTR FileStop, MatchStop;
  140. //
  141. // SRCFILE filenames are saved as the partial path that
  142. // matched a source path component rather than the full path
  143. // of the file as loaded. When matching against potentially full
  144. // path information in debug info it's useful to use the incoming
  145. // string as the filename and the SRCFILE filename as the match
  146. // string. A full match indicates that the partial path matches
  147. // completely and so should be used.
  148. //
  149. // This doesn't work so well for human input where the filename is
  150. // likely to be just a filename with no path. In this case there
  151. // won't be a full match of the match string, nor is just flipping
  152. // the order of strings useful because that would allow submatches
  153. // such as "foo.c" matching "barfoo.c". Instead this code tests
  154. // two conditions:
  155. // 1. Full match string match.
  156. // 2. Full file string match (implies partial match string match)
  157. // and the mismatch character is a path separator.
  158. // This forces filenames to match completely.
  159. //
  160. if (SymMatchFileName(File, SrcFile->File, &FileStop, &MatchStop) ||
  161. (FileStop < File && IS_PATH_DELIM(*MatchStop)))
  162. {
  163. #ifdef DBG_SRC
  164. dprintf("'%s' matches '%s'\n", SrcFile->File, File);
  165. #endif
  166. return TRUE;
  167. }
  168. else
  169. {
  170. #ifdef DBG_SRC
  171. dprintf("'%s' doesn't match '%s'\n", SrcFile->File, File);
  172. #endif
  173. return FALSE;
  174. }
  175. }
  176. BOOL
  177. UnloadSrcFile(
  178. LPSTR File
  179. )
  180. {
  181. PSRCFILE SrcFile, Prev;
  182. Prev = NULL;
  183. for (SrcFile = g_SrcFiles; SrcFile != NULL; SrcFile = SrcFile->Next)
  184. {
  185. if (MatchSrcFileName(SrcFile, File))
  186. {
  187. break;
  188. }
  189. Prev = SrcFile;
  190. }
  191. if (SrcFile == NULL)
  192. {
  193. return FALSE;
  194. }
  195. if (Prev != NULL)
  196. {
  197. Prev->Next = SrcFile->Next;
  198. }
  199. else
  200. {
  201. g_SrcFiles = SrcFile->Next;
  202. }
  203. DeleteSrcFile(SrcFile);
  204. return TRUE;
  205. }
  206. void
  207. UnloadSrcFiles(
  208. void
  209. )
  210. {
  211. PSRCFILE Cur, Next;
  212. for (Cur = g_SrcFiles; Cur != NULL; Cur = Next)
  213. {
  214. Next = Cur->Next;
  215. DeleteSrcFile(Cur);
  216. }
  217. g_SrcFiles = NULL;
  218. }
  219. PSRCFILE
  220. FindLoadedSrcFile(
  221. LPSTR File
  222. )
  223. {
  224. PSRCFILE SrcFile;
  225. for (SrcFile = g_SrcFiles; SrcFile != NULL; SrcFile = SrcFile->Next)
  226. {
  227. if (MatchSrcFileName(SrcFile, File))
  228. {
  229. #ifdef DBG_SRC
  230. dprintf("Found loaded file '%s'\n", SrcFile->File);
  231. #endif
  232. return SrcFile;
  233. }
  234. }
  235. return NULL;
  236. }
  237. void
  238. ConcatPathComponents(
  239. LPSTR Path,
  240. LPSTR PathEnd,
  241. LPSTR* PathOut,
  242. LPSTR FilePath,
  243. LPSTR Buffer
  244. )
  245. {
  246. if (PathEnd == NULL)
  247. {
  248. PathEnd = strchr(Path, ';');
  249. if (PathEnd != NULL)
  250. {
  251. if (PathOut != NULL)
  252. {
  253. *PathOut = PathEnd + 1;
  254. }
  255. }
  256. else
  257. {
  258. PathEnd = Path + strlen(Path);
  259. if (PathOut != NULL)
  260. {
  261. *PathOut = NULL;
  262. }
  263. }
  264. }
  265. if (PathEnd > Path)
  266. {
  267. memcpy(Buffer, Path, (int)(PathEnd - Path));
  268. PathEnd = Buffer + (PathEnd - Path - 1);
  269. // Attempt to avoid duplicating separators while forcing separation.
  270. if ((*PathEnd == ':' && *FilePath == ':') ||
  271. (IS_SLASH(*PathEnd) && IS_SLASH(*FilePath)))
  272. {
  273. FilePath++;
  274. }
  275. else if (!IS_PATH_DELIM(*PathEnd) && !IS_PATH_DELIM(*FilePath))
  276. {
  277. *(++PathEnd) = '\\';
  278. }
  279. strcpy(PathEnd + 1, FilePath);
  280. }
  281. else
  282. {
  283. strcpy(Buffer, FilePath);
  284. }
  285. }
  286. void
  287. EditPathSlashes(
  288. LPSTR Path
  289. )
  290. {
  291. if (!IsUrlPathComponent(Path))
  292. {
  293. return;
  294. }
  295. PSTR Scan = Path;
  296. // Flip all backslashes forwards.
  297. while (*Scan)
  298. {
  299. if (*Scan == '\\')
  300. {
  301. *Scan = '/';
  302. }
  303. Scan++;
  304. }
  305. }
  306. BOOL
  307. SrcFileExists(
  308. LPSTR Path,
  309. LPSTR PathEnd,
  310. LPSTR* PathOut,
  311. LPSTR FilePath,
  312. LPSTR File
  313. )
  314. {
  315. char Buffer[MAX_SOURCE_PATH];
  316. ConcatPathComponents(Path, PathEnd, PathOut, FilePath, Buffer);
  317. if (File != NULL)
  318. {
  319. ULONG Len = strlen(Buffer);
  320. Buffer[Len] = '\\';
  321. strcpy(Buffer + Len + 1, File);
  322. }
  323. EditPathSlashes(Buffer);
  324. #ifdef DBG_SRC
  325. dprintf("Check for existence of '%s'\n", Buffer);
  326. #endif
  327. FILE_IO_TYPE IoType;
  328. return PathFileExists(Buffer, g_SymOptions, &IoType);
  329. }
  330. BOOL
  331. FindSrcFileOnPath(
  332. ULONG StartElement,
  333. LPSTR File,
  334. ULONG Flags,
  335. PSTR Found,
  336. PSTR* MatchPart,
  337. PULONG FoundElement
  338. )
  339. {
  340. LPSTR PathSuff;
  341. LPSTR Path;
  342. LPSTR PathStart;
  343. LPSTR PathSep;
  344. LPSTR PathCharPtr;
  345. char PathChar;
  346. ULONG Elt;
  347. // Find the element of the path to start at.
  348. PathStart = FindPathElement(g_SrcPath, StartElement, &PathSep);
  349. if (PathStart == NULL)
  350. {
  351. goto CheckPlainFile;
  352. }
  353. // Split the given filename into a path prefix and a path
  354. // suffix. Initially the path prefix is any path components
  355. // and the path suffix is just the filename. If there
  356. // are path components attempt to match them against the source
  357. // path. Keep backing up the path one component at a time
  358. // until a match is found or the prefix is emptied. At
  359. // that point just do a plain file search along the source path.
  360. PathSuff = File + strlen(File);
  361. for (;;)
  362. {
  363. while (--PathSuff >= File)
  364. {
  365. if (IS_SLASH(*PathSuff) ||
  366. (*PathSuff == ':' && !IS_SLASH(*(PathSuff + 1))))
  367. {
  368. break;
  369. }
  370. }
  371. PathSuff++;
  372. // If we've run out of path prefix we're done with this
  373. // part of the search.
  374. if (PathSuff == File)
  375. {
  376. break;
  377. }
  378. char Save;
  379. LPSTR BestPathStart;
  380. LPSTR BestPathEnd;
  381. LPSTR BestFile;
  382. ULONG BestElement;
  383. LPSTR MatchPath;
  384. LPSTR MatchFile;
  385. Save = *(PathSuff - 1);
  386. *(PathSuff - 1) = 0;
  387. #ifdef DBG_SRC
  388. dprintf("Check path pre '%s' suff '%s'\n",
  389. File, PathSuff);
  390. #endif
  391. Path = PathStart;
  392. Elt = StartElement;
  393. BestPathStart = NULL;
  394. BestFile = PathSuff - 2;
  395. while (*Path != 0)
  396. {
  397. PathSep = strchr(Path, ';');
  398. if (PathSep == NULL)
  399. {
  400. PathSep = Path + strlen(Path);
  401. }
  402. // Trim trailing slashes on path components as
  403. // the file components have them trimmed so
  404. // leaving them would confuse the matching.
  405. PathCharPtr = PathSep;
  406. if (PathCharPtr > Path && IS_SLASH(PathCharPtr[-1]))
  407. {
  408. PathCharPtr--;
  409. }
  410. PathChar = *PathCharPtr;
  411. if (PathChar != 0)
  412. {
  413. *PathCharPtr = 0;
  414. }
  415. else
  416. {
  417. // Back up off the terminator so that PathSep
  418. // can be advanced the same way for both
  419. // ';' and end-of-string cases.
  420. PathSep--;
  421. }
  422. SymMatchFileName(Path, File, &MatchPath, &MatchFile);
  423. #ifdef DBG_SRC
  424. dprintf("Match '%s' against '%s': %d (match '%s')\n",
  425. Path, File, MatchFile - File, MatchFile + 1);
  426. #endif
  427. *PathCharPtr = PathChar;
  428. if (MatchFile < BestFile &&
  429. SrcFileExists(Path, MatchPath + 1, NULL,
  430. MatchFile + 1, PathSuff))
  431. {
  432. BestPathStart = Path;
  433. BestPathEnd = MatchPath + 1;
  434. BestFile = MatchFile + 1;
  435. BestElement = Elt;
  436. // Check for complete match or first-match mode.
  437. if (MatchPath < Path || MatchFile < File ||
  438. (Flags & DEBUG_FIND_SOURCE_BEST_MATCH) == 0)
  439. {
  440. break;
  441. }
  442. }
  443. Path = PathSep + 1;
  444. Elt++;
  445. }
  446. *(PathSuff - 1) = Save;
  447. if (BestPathStart != NULL)
  448. {
  449. #ifdef DBG_SRC
  450. dprintf("Found partial file '%.*s' on path '%.*s'\n",
  451. PathSuff - BestFile, BestFile,
  452. BestPathEnd - BestPathStart, BestPathStart);
  453. #endif
  454. // Return the match found.
  455. ConcatPathComponents(BestPathStart, BestPathEnd, NULL,
  456. BestFile, Found);
  457. EditPathSlashes(Found);
  458. *MatchPart = BestFile;
  459. *FoundElement = BestElement;
  460. #ifdef DBG_SRC
  461. dprintf("Found partial file '%s' at %d\n",
  462. Found, *FoundElement);
  463. #endif
  464. return TRUE;
  465. }
  466. // Skip past separator.
  467. PathSuff--;
  468. }
  469. // Traverse all directories in the source path and try them with the
  470. // filename given. Start with the given filename
  471. // to make the most restrictive check. If
  472. // no match is found keep trimming components off and
  473. // checking again.
  474. PathSuff = File;
  475. for (;;)
  476. {
  477. #ifdef DBG_SRC
  478. dprintf("Scan all paths for '%s'\n", PathSuff);
  479. #endif
  480. Path = PathStart;
  481. Elt = StartElement;
  482. while (Path != NULL && *Path != 0)
  483. {
  484. if (SrcFileExists(Path, NULL, &PathSep, PathSuff, NULL))
  485. {
  486. // SrcFileExists leaves PathSep set to the
  487. // path element after the separator so back up
  488. // one when forming the return path.
  489. if (PathSep != NULL)
  490. {
  491. PathSep--;
  492. }
  493. #ifdef DBG_SRC
  494. dprintf("Found file suffix '%s' on path '%.*s'\n",
  495. PathSuff, PathSep != NULL ?
  496. PathSep - Path : strlen(Path), Path);
  497. #endif
  498. ConcatPathComponents(Path, PathSep, NULL, PathSuff, Found);
  499. EditPathSlashes(Found);
  500. *MatchPart = PathSuff;
  501. *FoundElement = Elt;
  502. #ifdef DBG_SRC
  503. dprintf("Found file suffix '%s' at %d\n",
  504. Found, *FoundElement);
  505. #endif
  506. return TRUE;
  507. }
  508. Path = PathSep;
  509. Elt++;
  510. }
  511. // Trim a component from the front of the path suffix.
  512. PathSep = PathSuff;
  513. while (*PathSep != 0 &&
  514. !IS_SLASH(*PathSep) &&
  515. (*PathSep != ':' || IS_SLASH(*(PathSep + 1))))
  516. {
  517. PathSep++;
  518. }
  519. if (*PathSep == 0)
  520. {
  521. // Nothing left to trim.
  522. break;
  523. }
  524. PathSuff = PathSep + 1;
  525. }
  526. CheckPlainFile:
  527. #ifdef DBG_SRC
  528. dprintf("Check plain file '%s'\n", File);
  529. #endif
  530. if (GetFileAttributes(File) != -1)
  531. {
  532. strcpy(Found, File);
  533. *MatchPart = File;
  534. *FoundElement = -1;
  535. #ifdef DBG_SRC
  536. dprintf("Found plain file '%s' at %d\n", Found, *FoundElement);
  537. #endif
  538. return TRUE;
  539. }
  540. return FALSE;
  541. }
  542. PSRCFILE
  543. LoadSrcFileOnPath(
  544. LPSTR File
  545. )
  546. {
  547. if (g_SrcPath == NULL)
  548. {
  549. return LoadSrcFile(File, File);
  550. }
  551. char Found[MAX_SOURCE_PATH];
  552. PSTR MatchPart;
  553. ULONG Elt;
  554. if (FindSrcFileOnPath(0, File, DEBUG_FIND_SOURCE_BEST_MATCH,
  555. Found, &MatchPart, &Elt))
  556. {
  557. return LoadSrcFile(Found, MatchPart);
  558. }
  559. dprintf("No source found for '%s'\n", File);
  560. return NULL;
  561. }
  562. PSRCFILE
  563. FindSrcFile(
  564. LPSTR File
  565. )
  566. {
  567. PSRCFILE SrcFile;
  568. #ifdef DBG_SRC
  569. dprintf("Find '%s'\n", File);
  570. #endif
  571. SrcFile = FindLoadedSrcFile(File);
  572. if (SrcFile == NULL)
  573. {
  574. SrcFile = LoadSrcFileOnPath(File);
  575. }
  576. return SrcFile;
  577. }
  578. void
  579. OutputLineAddr(
  580. ULONG64 Offset
  581. )
  582. {
  583. IMAGEHLP_LINE64 Line;
  584. ULONG Disp;
  585. Line.SizeOfStruct = sizeof(Line);
  586. if (SymGetLineFromAddr64(g_CurrentProcess->Handle,
  587. Offset,
  588. &Disp,
  589. &Line))
  590. {
  591. dprintf("%s(%d)", Line.FileName, Line.LineNumber);
  592. if (Disp != 0)
  593. {
  594. dprintf("+0x%x", Disp);
  595. }
  596. dprintf("\n");
  597. }
  598. }
  599. void
  600. OutputSrcLines(
  601. PSRCFILE File,
  602. ULONG First,
  603. ULONG Last,
  604. ULONG Mark
  605. )
  606. {
  607. ULONG i;
  608. LPSTR *Text;
  609. if (First < 1)
  610. {
  611. First = 1;
  612. }
  613. if (Last > File->Lines)
  614. {
  615. Last = File->Lines;
  616. }
  617. Text = &File->LineText[First-1];
  618. for (i = First; i <= Last; i++)
  619. {
  620. if (i == Mark)
  621. {
  622. dprintf(">");
  623. }
  624. else
  625. {
  626. dprintf(" ");
  627. }
  628. dprintf("%5d: %s\n", i, *Text++);
  629. }
  630. }
  631. BOOL
  632. OutputSrcLinesAroundAddr(
  633. ULONG64 Offset,
  634. ULONG Before,
  635. ULONG After
  636. )
  637. {
  638. IMAGEHLP_LINE64 Line;
  639. ULONG Disp;
  640. PSRCFILE SrcFile;
  641. Line.SizeOfStruct = sizeof(Line);
  642. if (!SymGetLineFromAddr64(g_CurrentProcess->Handle,
  643. Offset,
  644. &Disp,
  645. &Line))
  646. {
  647. return FALSE;
  648. }
  649. SrcFile = FindSrcFile(Line.FileName);
  650. if (SrcFile == NULL)
  651. {
  652. return FALSE;
  653. }
  654. OutputSrcLines(SrcFile,
  655. Line.LineNumber-Before, Line.LineNumber+After-1,
  656. Line.LineNumber);
  657. return TRUE;
  658. }
  659. ULONG
  660. GetOffsetFromLine(
  661. PSTR FileLine,
  662. PULONG64 Offset
  663. )
  664. {
  665. IMAGEHLP_LINE64 Line;
  666. LPSTR Mod;
  667. LPSTR File;
  668. LPSTR LineStr;
  669. LPSTR SlashF, SlashB;
  670. ULONG LineNum;
  671. ULONG Disp;
  672. ULONG OldSym;
  673. ULONG NewSym;
  674. BOOL AllowDisp;
  675. BOOL Ret;
  676. PDEBUG_IMAGE_INFO Image = NULL;
  677. if ((g_SymOptions & SYMOPT_LOAD_LINES) == 0)
  678. {
  679. WarnOut("WARNING: Line information loading disabled\n");
  680. }
  681. OldSym = g_SymOptions;
  682. NewSym = g_SymOptions;
  683. // Symbol directives can prefix the source expression.
  684. // These can be given by sufficiently knowledgeable users
  685. // but they're primarily a back-channel communication
  686. // mechanism for windbg's source management.
  687. if (*FileLine == '<')
  688. {
  689. FileLine++;
  690. while (*FileLine != '>')
  691. {
  692. switch(*FileLine)
  693. {
  694. case 'U':
  695. // Restrict the search to just loaded modules.
  696. NewSym |= SYMOPT_NO_UNQUALIFIED_LOADS;
  697. break;
  698. default:
  699. error(SYNTAX);
  700. }
  701. FileLine++;
  702. }
  703. FileLine++;
  704. }
  705. // Crack string of the form [module!][file][:line] into its
  706. // components. Note that ! is a valid filename character so
  707. // it's possible for ambiguity to occur between module references
  708. // and filenames. This code assumes that ! is uncommon and
  709. // handles it as a module separator unless there's a : or \ or /
  710. // before it. : can also occur in paths and is filtered
  711. // in a similar manner.
  712. File = strchr(FileLine, '!');
  713. LineStr = strchr(FileLine, ':');
  714. SlashF = strchr(FileLine, '/');
  715. SlashB = strchr(FileLine, '\\');
  716. if (File != NULL &&
  717. (LineStr != NULL && File > LineStr) ||
  718. (SlashF != NULL && File > SlashF) ||
  719. (SlashB != NULL && File > SlashB))
  720. {
  721. File = NULL;
  722. }
  723. if (File != NULL)
  724. {
  725. if (File == FileLine)
  726. {
  727. error(SYNTAX);
  728. }
  729. Mod = FileLine;
  730. *File++ = 0;
  731. }
  732. else
  733. {
  734. Mod = NULL;
  735. File = FileLine;
  736. }
  737. // If a module was specified check and see if it's
  738. // a module that's currently present as that
  739. // will affect which error code is returned.
  740. if (Mod != NULL)
  741. {
  742. Image = GetImageByName(g_CurrentProcess, Mod, INAME_MODULE);
  743. }
  744. // Look for the first colon after path components.
  745. while (LineStr != NULL &&
  746. (LineStr < File || LineStr < SlashF || LineStr < SlashB))
  747. {
  748. LineStr = strchr(LineStr + 1, ':');
  749. }
  750. LineNum = 1;
  751. if (LineStr != NULL)
  752. {
  753. PSTR NumEnd;
  754. // A specific line was given so don't allow a displacement.
  755. AllowDisp = FALSE;
  756. *LineStr = 0;
  757. LineNum = strtoul(LineStr + 1, &NumEnd, 0);
  758. if (*NumEnd == '+')
  759. {
  760. // Setting the high bit of the line number
  761. // tells dbghelp to search in at-or-greater mode.
  762. // This may produce displacements so allow them.
  763. LineNum |= 0x80000000;
  764. AllowDisp = TRUE;
  765. }
  766. else if (*NumEnd == '~')
  767. {
  768. // Find the closest line number.
  769. AllowDisp = TRUE;
  770. }
  771. else if (*NumEnd && *NumEnd != ' ' && *NumEnd != '\t')
  772. {
  773. error(SYNTAX);
  774. }
  775. }
  776. else
  777. {
  778. AllowDisp = TRUE;
  779. }
  780. Line.SizeOfStruct = sizeof(Line);
  781. Ret = FALSE;
  782. // If this is a pure linenumber reference then we need to fill in
  783. // the line information with a current location before doing
  784. // the line-relative query.
  785. if (*File == 0)
  786. {
  787. ADDR Pc;
  788. if (Mod != NULL)
  789. {
  790. goto EH_Ret;
  791. }
  792. g_Machine->GetPC(&Pc);
  793. if (!SymGetLineFromAddr64(g_CurrentProcess->Handle,
  794. Flat(Pc),
  795. &Disp,
  796. &Line))
  797. {
  798. goto EH_Ret;
  799. }
  800. File = NULL;
  801. }
  802. // Establish any special symbol options requested.
  803. SymSetOptions(NewSym);
  804. Ret = SymGetLineFromName64(g_CurrentProcess->Handle, Mod,
  805. File, LineNum, (PLONG)&Disp, &Line);
  806. SymSetOptions(OldSym);
  807. EH_Ret:
  808. if (Mod != NULL)
  809. {
  810. *(File-1) = '!';
  811. }
  812. if (LineStr != NULL)
  813. {
  814. *LineStr = ':';
  815. }
  816. // Only return a match if it's exact or no line number was specified.
  817. if (Ret && (Disp == 0 || AllowDisp))
  818. {
  819. *Offset = Line.Address;
  820. return LINE_FOUND;
  821. }
  822. else if (Image != NULL)
  823. {
  824. return LINE_NOT_FOUND_IN_MODULE;
  825. }
  826. else
  827. {
  828. return LINE_NOT_FOUND;
  829. }
  830. }
  831. void
  832. ParseSrcOptCmd(
  833. CHAR Cmd
  834. )
  835. {
  836. char Cmd2;
  837. DWORD Opt;
  838. Cmd2 = PeekChar();
  839. if (Cmd2 == 'l')
  840. {
  841. g_CurCmd++;
  842. Opt = SRCOPT_LIST_LINE;
  843. }
  844. else if (Cmd2 == 'o')
  845. {
  846. g_CurCmd++;
  847. Opt = SRCOPT_LIST_SOURCE_ONLY;
  848. }
  849. else if (Cmd2 == 's')
  850. {
  851. g_CurCmd++;
  852. Opt = SRCOPT_LIST_SOURCE;
  853. }
  854. else if (Cmd2 == 't')
  855. {
  856. g_CurCmd++;
  857. Opt = SRCOPT_STEP_SOURCE;
  858. }
  859. else if (Cmd2 == '0')
  860. {
  861. // Numeric options.
  862. if (*(++g_CurCmd) != 'x')
  863. {
  864. error(SYNTAX);
  865. }
  866. else
  867. {
  868. g_CurCmd++;
  869. Opt = (DWORD)HexValue(4);
  870. }
  871. }
  872. else if (Cmd2 == '*')
  873. {
  874. g_CurCmd++;
  875. // All.
  876. Opt = 0xffffffff;
  877. }
  878. else if (Cmd2 != 0 && Cmd2 != ';')
  879. {
  880. error(SYNTAX);
  881. }
  882. else
  883. {
  884. // No character means display current settings.
  885. Opt = 0;
  886. }
  887. ULONG OldSrcOpt = g_SrcOptions;
  888. if (Cmd == '+')
  889. {
  890. g_SrcOptions |= Opt;
  891. if ((SymGetOptions() & SYMOPT_LOAD_LINES) == 0)
  892. {
  893. WarnOut(" WARNING: Line information loading disabled\n");
  894. }
  895. }
  896. else
  897. {
  898. g_SrcOptions &= ~Opt;
  899. }
  900. if ((OldSrcOpt ^ g_SrcOptions) & SRCOPT_STEP_SOURCE)
  901. {
  902. NotifyChangeEngineState(DEBUG_CES_CODE_LEVEL,
  903. (g_SrcOptions & SRCOPT_STEP_SOURCE) ?
  904. DEBUG_LEVEL_SOURCE : DEBUG_LEVEL_ASSEMBLY,
  905. TRUE);
  906. }
  907. dprintf("Source options are %x:\n", g_SrcOptions);
  908. if (g_SrcOptions == 0)
  909. {
  910. dprintf(" None\n");
  911. }
  912. else
  913. {
  914. if (g_SrcOptions & SRCOPT_STEP_SOURCE)
  915. {
  916. dprintf(" %2x/t - Step/trace by source line\n",
  917. SRCOPT_STEP_SOURCE);
  918. }
  919. if (g_SrcOptions & SRCOPT_LIST_LINE)
  920. {
  921. dprintf(" %2x/l - List source line for LN and prompt\n",
  922. SRCOPT_LIST_LINE);
  923. }
  924. if (g_SrcOptions & SRCOPT_LIST_SOURCE)
  925. {
  926. dprintf(" %2x/s - List source code at prompt\n",
  927. SRCOPT_LIST_SOURCE);
  928. }
  929. if (g_SrcOptions & SRCOPT_LIST_SOURCE_ONLY)
  930. {
  931. dprintf(" %2x/o - Only show source code at prompt\n",
  932. SRCOPT_LIST_SOURCE_ONLY);
  933. }
  934. }
  935. }
  936. void
  937. ParseSrcLoadCmd(
  938. void
  939. )
  940. {
  941. LPSTR Semi;
  942. PSRCFILE SrcFile;
  943. char Cur;
  944. BOOL Unload;
  945. // Check for an unload request.
  946. Unload = FALSE;
  947. if (*g_CurCmd == '-')
  948. {
  949. g_CurCmd++;
  950. Unload = TRUE;
  951. }
  952. while ((Cur = *g_CurCmd) == ' ' || Cur == '\t')
  953. {
  954. g_CurCmd++;
  955. }
  956. if (Cur == 0 || Cur == ';')
  957. {
  958. error(SYNTAX);
  959. }
  960. // Look for a semicolon, otherwise assume the whole command
  961. // line is the file path.
  962. Semi = strchr(g_CurCmd, ';');
  963. if (Semi != NULL)
  964. {
  965. *Semi = 0;
  966. }
  967. if (Unload)
  968. {
  969. if (UnloadSrcFile(g_CurCmd))
  970. {
  971. dprintf("Unloaded '%s'\n", g_CurCmd);
  972. }
  973. }
  974. else
  975. {
  976. SrcFile = FindSrcFile(g_CurCmd);
  977. if (SrcFile == NULL)
  978. {
  979. dprintf("Unable to load '%s'\n", g_CurCmd);
  980. }
  981. }
  982. if (Semi != NULL)
  983. {
  984. *Semi = ';';
  985. g_CurCmd = Semi;
  986. }
  987. else
  988. {
  989. g_CurCmd += strlen(g_CurCmd);
  990. }
  991. if (!Unload && SrcFile != NULL)
  992. {
  993. g_CurSrcFile = SrcFile;
  994. g_CurSrcLine = 1;
  995. }
  996. }
  997. void
  998. ParseSrcListCmd(
  999. CHAR Cmd
  1000. )
  1001. {
  1002. LONG First, Count;
  1003. char Cur;
  1004. ULONG OldBase;
  1005. ADDR Addr;
  1006. ULONG Mark;
  1007. if (Cmd == '.')
  1008. {
  1009. g_CurCmd++;
  1010. PDEBUG_SCOPE Scope = GetCurrentScope();
  1011. if (Scope->Frame.InstructionOffset)
  1012. {
  1013. // List current frame
  1014. ADDRFLAT(&Addr, Scope->Frame.InstructionOffset);
  1015. }
  1016. else
  1017. {
  1018. // List at PC.
  1019. g_Machine->GetPC(&Addr);
  1020. }
  1021. Cmd = 'a';
  1022. }
  1023. else if (Cmd == 'a')
  1024. {
  1025. g_CurCmd++;
  1026. // List at address, so get an address.
  1027. GetAddrExpression(SEGREG_CODE, &Addr);
  1028. // Search for and consume trailing ,
  1029. while ((Cur = *g_CurCmd) == ' ' || Cur == '\t')
  1030. {
  1031. g_CurCmd++;
  1032. }
  1033. if (Cur == ',')
  1034. {
  1035. Cur = *++g_CurCmd;
  1036. if (Cur == 0 || Cur == ';')
  1037. {
  1038. error(SYNTAX);
  1039. }
  1040. }
  1041. }
  1042. else if (Cmd == 'c')
  1043. {
  1044. g_CurCmd++;
  1045. if (g_CurSrcFile != NULL)
  1046. {
  1047. dprintf("Current: %s(%d)\n", g_CurSrcFile->File, g_CurSrcLine);
  1048. }
  1049. else
  1050. {
  1051. dprintf("No current source file\n");
  1052. }
  1053. return;
  1054. }
  1055. while ((Cur = *g_CurCmd) == ' ' || Cur == '\t')
  1056. {
  1057. g_CurCmd++;
  1058. }
  1059. // Force base 10 for linenumbers.
  1060. OldBase = g_DefaultRadix;
  1061. g_DefaultRadix = 10;
  1062. if (Cur == 0 || Cur == ';')
  1063. {
  1064. First = Cmd == 'a' ? -4 : g_CurSrcLine;
  1065. Count = 10;
  1066. }
  1067. else if (Cur == ',')
  1068. {
  1069. First = Cmd == 'a' ? -4 : g_CurSrcLine;
  1070. g_CurCmd++;
  1071. Count = (ULONG)GetExpression();
  1072. }
  1073. else
  1074. {
  1075. First = (ULONG)GetExpression();
  1076. if (*g_CurCmd == ',')
  1077. {
  1078. g_CurCmd++;
  1079. Count = (ULONG)GetExpression();
  1080. }
  1081. else
  1082. {
  1083. Count = 10;
  1084. }
  1085. }
  1086. g_DefaultRadix = OldBase;
  1087. if (Count < 1)
  1088. {
  1089. error(SYNTAX);
  1090. }
  1091. Mark = 0;
  1092. if (Cmd == 'a')
  1093. {
  1094. DWORD Disp;
  1095. IMAGEHLP_LINE64 Line;
  1096. PSRCFILE SrcFile;
  1097. // Listing from the source file that Addr is in.
  1098. Line.SizeOfStruct = sizeof(Line);
  1099. if (!SymGetLineFromAddr64(g_CurrentProcess->Handle,
  1100. Flat(Addr),
  1101. &Disp,
  1102. &Line))
  1103. {
  1104. return;
  1105. }
  1106. SrcFile = FindSrcFile(Line.FileName);
  1107. if (SrcFile == NULL)
  1108. {
  1109. return;
  1110. }
  1111. g_CurSrcFile = SrcFile;
  1112. g_CurSrcLine = Line.LineNumber;
  1113. Mark = Line.LineNumber;
  1114. }
  1115. if (g_CurSrcFile == NULL)
  1116. {
  1117. dprintf("No current source file\n");
  1118. return;
  1119. }
  1120. // Address list commands are always relative,
  1121. // as are negative starting positions.
  1122. if (Cmd == 'a' || First < 0)
  1123. {
  1124. g_CurSrcLine += First;
  1125. }
  1126. else
  1127. {
  1128. g_CurSrcLine = First;
  1129. }
  1130. OutputSrcLines(g_CurSrcFile, g_CurSrcLine, g_CurSrcLine+Count-1, Mark);
  1131. g_CurSrcLine += Count;
  1132. }
  1133. void
  1134. ParseOciSrcCmd(
  1135. void
  1136. )
  1137. {
  1138. if (PeekChar() != ';' && *g_CurCmd)
  1139. {
  1140. ULONG64 Val1 = GetExpression();
  1141. ULONG64 Val2 = 0;
  1142. if (PeekChar() != ';' && *g_CurCmd)
  1143. {
  1144. Val2 = GetExpression();
  1145. }
  1146. else
  1147. {
  1148. Val2 = (Val1 + 1) / 2;
  1149. Val1 -= Val2;
  1150. }
  1151. g_OciSrcBefore = (ULONG)Val1;
  1152. g_OciSrcAfter = (ULONG)Val2;
  1153. }
  1154. if ((g_SrcOptions & SRCOPT_LIST_SOURCE) == 0)
  1155. {
  1156. WarnOut("WARNING: Source line display is disabled\n");
  1157. }
  1158. dprintf("At the prompt, display %d source lines before and %d after\n",
  1159. g_OciSrcBefore, g_OciSrcAfter);
  1160. }
  1161. void
  1162. ParseLines(PSTR Args)
  1163. {
  1164. ULONG NewOpts = g_SymOptions ^ SYMOPT_LOAD_LINES;
  1165. while (Args && *Args)
  1166. {
  1167. while (*Args == ' ' || *Args == '\t')
  1168. {
  1169. Args++;
  1170. }
  1171. if (*Args == '-' || *Args == '/')
  1172. {
  1173. Args++;
  1174. switch(*Args++)
  1175. {
  1176. case 'd':
  1177. NewOpts &= ~SYMOPT_LOAD_LINES;
  1178. break;
  1179. case 'e':
  1180. NewOpts |= SYMOPT_LOAD_LINES;
  1181. break;
  1182. case 't':
  1183. // Toggle, already done.
  1184. break;
  1185. default:
  1186. error(SYNTAX);
  1187. }
  1188. }
  1189. else
  1190. {
  1191. break;
  1192. }
  1193. }
  1194. SetSymOptions(NewOpts);
  1195. if (g_SymOptions & SYMOPT_LOAD_LINES)
  1196. {
  1197. dprintf("Line number information will be loaded\n");
  1198. }
  1199. else
  1200. {
  1201. dprintf("Line number information will not be loaded\n");
  1202. }
  1203. }
  1204. void
  1205. ChangeSrcPath(
  1206. PSTR Args,
  1207. BOOL Append
  1208. )
  1209. {
  1210. while (*Args == ' ' || *Args == '\t')
  1211. {
  1212. Args++;
  1213. }
  1214. if (*Args != 0)
  1215. {
  1216. if (ChangePath(&g_SrcPath, Args, Append,
  1217. DEBUG_CSS_PATHS) != S_OK)
  1218. {
  1219. return;
  1220. }
  1221. }
  1222. if (g_SrcPath == NULL)
  1223. {
  1224. dprintf("No source search path\n");
  1225. }
  1226. else
  1227. {
  1228. dprintf("Source search path is: %s\n", g_SrcPath);
  1229. CheckPath(g_SrcPath);
  1230. }
  1231. }
  1232. void
  1233. ChangeExePath(
  1234. PSTR Args,
  1235. BOOL Append
  1236. )
  1237. {
  1238. while (*Args == ' ' || *Args == '\t')
  1239. {
  1240. Args++;
  1241. }
  1242. if (*Args != 0)
  1243. {
  1244. if (ChangePath(&g_ExecutableImageSearchPath, Args, Append,
  1245. DEBUG_CSS_PATHS) != S_OK)
  1246. {
  1247. return;
  1248. }
  1249. }
  1250. if (g_ExecutableImageSearchPath == NULL)
  1251. {
  1252. dprintf("No exectutable image search path\n");
  1253. }
  1254. else
  1255. {
  1256. dprintf("Executable image search path is: %s\n",
  1257. g_ExecutableImageSearchPath);
  1258. CheckPath(g_ExecutableImageSearchPath);
  1259. }
  1260. }