Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1454 lines
32 KiB

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