debug.c 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064
  1. /*++
  2. Copyright (c) 2012 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. debug.c
  9. Abstract:
  10. This module implements the debugging client.
  11. Author:
  12. Evan Green 2-Jul-2012
  13. Environment:
  14. Debug Client
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include <minoca/lib/types.h>
  20. #include <minoca/lib/status.h>
  21. #include <minoca/debug/spproto.h>
  22. #include <minoca/lib/im.h>
  23. #include <minoca/debug/dbgext.h>
  24. #include "symbols.h"
  25. #include "dbgapi.h"
  26. #include "dbgrprof.h"
  27. #include "console.h"
  28. #include "userdbg.h"
  29. #include "dbgrcomm.h"
  30. #include "extsp.h"
  31. #include "consio.h"
  32. #include "remsrv.h"
  33. #include <assert.h>
  34. #include <ctype.h>
  35. #include <errno.h>
  36. #include <getopt.h>
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <time.h>
  41. #include <unistd.h>
  42. //
  43. // ---------------------------------------------------------------- Definitions
  44. //
  45. #define DEBUGGER_VERSION_MAJOR 1
  46. #define DEBUGGER_VERSION_MINOR 1
  47. #define DEBUGGER_COMMAND_BUFFER_SIZE 10000
  48. #define DEBUGGER_COMMAND_HISTORY_SIZE 50
  49. #define DEBUGGER_DEFAULT_BAUD_RATE 115200
  50. //
  51. // Define the program usage.
  52. //
  53. #define DEBUGGER_USAGE \
  54. "Usage: debug [-i] [-s <path>...] [-e <path>...] " \
  55. "[-k <connection>] [-b <baud_rate>] [-r remote:port] \n" \
  56. "[-- <child_parameters...>]\n\n" \
  57. "The Minoca debugger facilitates debugging, tracing, and profiling of \n" \
  58. "user mode programs and remote kernels. Options are:\n" \
  59. " -b, --baud-rate=<baud_rate> -- Specify the baud rate for kernel \n" \
  60. " serial port connections. If not specified, the default is \n" \
  61. " 115200bps.\n" \
  62. " -i, --initial-break -- Request an initial breakpoint upon connection.\n"\
  63. " -e, --extension=<path> -- Load the debugger extension at the given \n" \
  64. " path. This can also be done at runtime using the load command.\n" \
  65. " -k, --kernel=<connection> -- Connect to a kernel on another maching \n" \
  66. " using the given connection string. Connections can be named \n" \
  67. " pipes like '\\\\.\\pipe\\mypipe' or can be serial ports like \n" \
  68. " 'COM1'.\n" \
  69. " -r, --remote=<address:port> -- Connect to a remote debug server \n" \
  70. " using the given form. IPv6 addresses should be enclosed in \n" \
  71. " [square] brackets to disambiguate the colon separating the \n" \
  72. " address from the port.\n" \
  73. " -R, --reverse-remote=<address:port> -- Connect to a remote debug \n" \
  74. " server by opening up a port and waiting for an incoming \n" \
  75. " connection. This is useful when the debug server cannot accept \n" \
  76. " incoming connections.\n" \
  77. " -s, --symbol-path=<path> -- Add the given path to the symbol search \n" \
  78. " path. This option can be specified multiple times, or the path \n" \
  79. " argument can be semicolon-delimited list of paths.\n" \
  80. " -S, --source-path=<prefix=path> -- Add the given path to the source \n" \
  81. " search path. If the optional prefix matches a symbol source \n" \
  82. " path, it will be stripped off and replaced with the path. \n" \
  83. " --help -- Display this help text and exit.\n" \
  84. " --version -- Display the application and kernel protocol version and \n"\
  85. " exit.\n" \
  86. " child_parameters -- Specifies the program name and subsequent \n" \
  87. " arguments of the child process to launch and attach to. \n" \
  88. " Debugging a child process is incompatible with the -k option.\n\n"
  89. #define DEBUG_SHORT_OPTIONS "b:e:ik:r:R:s:S:"
  90. //
  91. // -------------------------------------------------------------------- Globals
  92. //
  93. struct option DbgrLongOptions[] = {
  94. {"baud-rate", required_argument, 0, 'b'},
  95. {"extension", required_argument, 0, 'e'},
  96. {"initial-break", no_argument, 0, 'i'},
  97. {"kernel", required_argument, 0, 'k'},
  98. {"remote", required_argument, 0, 'r'},
  99. {"reverse-remote", required_argument, 0, 'R'},
  100. {"symbol-path", required_argument, 0, 's'},
  101. {"source-path", required_argument, 0, 'S'},
  102. {"help", no_argument, 0, 'h'},
  103. {"version", no_argument, 0, 'V'},
  104. {NULL, 0, 0, 0},
  105. };
  106. //
  107. // ----------------------------------------------- Internal Function Prototypes
  108. //
  109. VOID
  110. DbgrpPrintCommandPrompt (
  111. PDEBUGGER_CONTEXT Context
  112. );
  113. BOOL
  114. DbgrpSplitCommandArguments (
  115. PSTR Input,
  116. PSTR Arguments[DEBUGGER_MAX_COMMAND_ARGUMENTS],
  117. PULONG ArgumentCount
  118. );
  119. //
  120. // ------------------------------------------------------ Data Type Definitions
  121. //
  122. //
  123. // ------------------------------------------------------------------ Functions
  124. //
  125. INT
  126. DbgrMain (
  127. INT ArgumentCount,
  128. CHAR **Arguments
  129. )
  130. /*++
  131. Routine Description:
  132. This routine is the main entry point for the debugger.
  133. Arguments:
  134. ArgumentCount - Supplies the number of arguments on the command line.
  135. Arguments - Supplies an array of strings representing the arguments.
  136. Return Value:
  137. Returns 0 on success, nonzero on failure.
  138. --*/
  139. {
  140. PSTR AfterScan;
  141. ULONG ArgumentIndex;
  142. ULONG BaudRate;
  143. PSTR Channel;
  144. PSTR Command;
  145. ULONG CommandArgumentCount;
  146. PSTR CommandArguments[DEBUGGER_MAX_COMMAND_ARGUMENTS];
  147. PDEBUGGER_COMMAND_ENTRY CommandEntry;
  148. DEBUG_CONNECTION_TYPE ConnectionType;
  149. DEBUGGER_CONTEXT Context;
  150. BOOL ExtensionsInitialized;
  151. ULONG HistoryIndex;
  152. INT Option;
  153. ULONG PathIndex;
  154. PSTR RemoteAddress;
  155. INT Result;
  156. INT ReturnValue;
  157. BOOL ReverseRemote;
  158. ULONG TargetArgumentCount;
  159. PSTR *TargetArguments;
  160. BaudRate = DEBUGGER_DEFAULT_BAUD_RATE;
  161. ConnectionType = DebugConnectionInvalid;
  162. Channel = NULL;
  163. ExtensionsInitialized = FALSE;
  164. RemoteAddress = NULL;
  165. ReverseRemote = FALSE;
  166. TargetArguments = NULL;
  167. TargetArgumentCount = 0;
  168. srand(time(NULL));
  169. memset(&Context, 0, sizeof(DEBUGGER_CONTEXT));
  170. Context.TargetFlags = DEBUGGER_TARGET_RUNNING;
  171. Context.Server.Socket = -1;
  172. Context.Client.Socket = -1;
  173. INITIALIZE_LIST_HEAD(&(Context.Server.ClientList));
  174. INITIALIZE_LIST_HEAD(&(Context.StandardIn.RemoteCommandList));
  175. INITIALIZE_LIST_HEAD(&(Context.SourcePathList));
  176. Context.CommandHistory =
  177. malloc(sizeof(PSTR) * DEBUGGER_COMMAND_HISTORY_SIZE);
  178. if (Context.CommandHistory == NULL) {
  179. Result = ENOMEM;
  180. goto MainEnd;
  181. }
  182. memset(Context.CommandHistory,
  183. 0,
  184. sizeof(PSTR) * DEBUGGER_COMMAND_HISTORY_SIZE);
  185. Context.CommandHistorySize = DEBUGGER_COMMAND_HISTORY_SIZE;
  186. Context.CommandBufferSize = DEBUGGER_COMMAND_BUFFER_SIZE;
  187. Context.CommandBuffer = malloc(Context.CommandBufferSize);
  188. if (Context.CommandBuffer == NULL) {
  189. Result = ENOMEM;
  190. goto MainEnd;
  191. }
  192. Context.CommandBuffer[0] = '\0';
  193. //
  194. // Initialize the console layer.
  195. //
  196. Result = DbgrInitializeConsoleIo(&Context);
  197. if (Result != 0) {
  198. goto MainEnd;
  199. }
  200. //
  201. // Initialize extensions.
  202. //
  203. Result = DbgInitializeExtensions(&Context);
  204. ExtensionsInitialized = TRUE;
  205. if (Result != 0) {
  206. goto MainEnd;
  207. }
  208. DbgOut("Minoca debugger version %d.%d. Protocol version %d.%d.\n",
  209. DEBUGGER_VERSION_MAJOR,
  210. DEBUGGER_VERSION_MINOR,
  211. DEBUG_PROTOCOL_MAJOR_VERSION,
  212. DEBUG_PROTOCOL_REVISION);
  213. //
  214. // Process the control arguments.
  215. //
  216. while (TRUE) {
  217. Option = getopt_long(ArgumentCount,
  218. Arguments,
  219. DEBUG_SHORT_OPTIONS,
  220. DbgrLongOptions,
  221. NULL);
  222. if (Option == -1) {
  223. break;
  224. }
  225. if ((Option == '?') || (Option == ':')) {
  226. Result = EINVAL;
  227. goto MainEnd;
  228. }
  229. switch (Option) {
  230. case 'b':
  231. BaudRate = strtoul(optarg, &AfterScan, 0);
  232. if (AfterScan == optarg) {
  233. DbgOut("Error: Invalid baud rate '%s'.\n", optarg);
  234. Result = EINVAL;
  235. goto MainEnd;
  236. }
  237. break;
  238. case 'e':
  239. Result = DbgLoadExtension(&Context, optarg);
  240. if (Result != 0) {
  241. DbgOut("Failed to load extension '%s'.\n", optarg);
  242. goto MainEnd;
  243. }
  244. break;
  245. case 'i':
  246. Context.Flags |= DEBUGGER_FLAG_INITIAL_BREAK;
  247. break;
  248. case 'k':
  249. if (ConnectionType != DebugConnectionInvalid) {
  250. DbgOut("Error: -k conflicts with a previous argument that "
  251. "defines the debugger connection type.\n");
  252. Result = EINVAL;
  253. goto MainEnd;
  254. }
  255. Channel = optarg;
  256. ConnectionType = DebugConnectionKernel;
  257. break;
  258. case 'R':
  259. ReverseRemote = TRUE;
  260. //
  261. // Fall through.
  262. //
  263. case 'r':
  264. if (ConnectionType != DebugConnectionInvalid) {
  265. DbgOut("Error: -r conflicts with a previously specified "
  266. "connection type.\n");
  267. Result = EINVAL;
  268. goto MainEnd;
  269. }
  270. ConnectionType = DebugConnectionRemote;
  271. RemoteAddress = optarg;
  272. break;
  273. case 's':
  274. Result = DbgrSetSymbolPath(&Context, optarg, TRUE);
  275. if (Result != 0) {
  276. DbgOut("Failed to set initial symbol path.\n");
  277. goto MainEnd;
  278. }
  279. break;
  280. case 'S':
  281. Result = DbgrpAddSourcePath(&Context, optarg);
  282. if (Result != 0) {
  283. DbgOut("Failed to add source path %s.\n", optarg);
  284. goto MainEnd;
  285. }
  286. break;
  287. case 'V':
  288. //
  289. // The version information was already printed above.
  290. //
  291. return 1;
  292. case 'h':
  293. DbgOut(DEBUGGER_USAGE);
  294. return 1;
  295. default:
  296. assert(FALSE);
  297. Result = EINVAL;
  298. goto MainEnd;
  299. }
  300. }
  301. ArgumentIndex = optind;
  302. if (ArgumentIndex > ArgumentCount) {
  303. ArgumentIndex = ArgumentCount;
  304. }
  305. //
  306. // Any additional arguments imply a usermode debugger. If kernel parameters
  307. // were supplied then this is an invalid configuration.
  308. //
  309. if (ArgumentIndex < ArgumentCount) {
  310. if (ConnectionType != DebugConnectionInvalid) {
  311. DbgOut("Error: Additional command line arguments imply a user "
  312. "mode debugger, but an alternate form (such as a kernel "
  313. "connection parameter) was specified in the arguments.\n");
  314. Result = EINVAL;
  315. goto MainEnd;
  316. }
  317. TargetArguments = &(Arguments[ArgumentIndex]);
  318. TargetArgumentCount = ArgumentCount - ArgumentIndex;
  319. ConnectionType = DebugConnectionUser;
  320. }
  321. //
  322. // Chide the user and exit if there's nothing valid to do.
  323. //
  324. if (ConnectionType == DebugConnectionInvalid) {
  325. DbgOut(DEBUGGER_USAGE);
  326. Result = FALSE;
  327. goto MainEnd;
  328. }
  329. Result = DbgrInitialize(&Context, ConnectionType);
  330. if (Result != 0) {
  331. goto MainEnd;
  332. }
  333. //
  334. // For kernel debugging, set up the communications channel.
  335. //
  336. if (ConnectionType == DebugConnectionKernel) {
  337. Result = InitializeCommunications(Channel, BaudRate);
  338. if (Result == FALSE) {
  339. DbgOut("Unable to setup communcations using %s\n", Channel);
  340. Result = EINVAL;
  341. goto MainEnd;
  342. }
  343. //
  344. // Connect to the target.
  345. //
  346. Result = DbgrConnect(&Context);
  347. if (Result != 0) {
  348. DbgOut("Unable to connect to target!\n");
  349. goto MainEnd;
  350. }
  351. //
  352. // For user mode debugging, set up the child process.
  353. //
  354. } else if (ConnectionType == DebugConnectionUser) {
  355. assert(TargetArguments != NULL);
  356. //
  357. // Anything with an equals sign at the beginning gets set as an
  358. // environment variable.
  359. //
  360. while (TargetArgumentCount != 0) {
  361. if (strchr(TargetArguments[0], '=') != NULL) {
  362. DbgOut("Setting environment variable: %s\n",
  363. TargetArguments[0]);
  364. putenv(TargetArguments[0]);
  365. TargetArguments += 1;
  366. TargetArgumentCount -= 1;
  367. } else {
  368. break;
  369. }
  370. }
  371. if (TargetArgumentCount == 0) {
  372. DbgOut("Error: No command to launch!\n");
  373. Result = -1;
  374. goto MainEnd;
  375. }
  376. DbgOut("Launching: ");
  377. for (ArgumentIndex = 0;
  378. ArgumentIndex < TargetArgumentCount;
  379. ArgumentIndex += 1) {
  380. DbgOut("%s ", TargetArguments[ArgumentIndex]);
  381. }
  382. DbgOut("\n");
  383. Result = LaunchChildProcess(TargetArgumentCount, TargetArguments);
  384. if (Result == FALSE) {
  385. DbgOut("Error: Failed to launch target process \"%s\".\n",
  386. TargetArguments[0]);
  387. Result = EINVAL;
  388. goto MainEnd;
  389. }
  390. } else {
  391. assert(ConnectionType == DebugConnectionRemote);
  392. Result = DbgrClientMainLoop(&Context, RemoteAddress, ReverseRemote);
  393. goto MainEnd;
  394. }
  395. //
  396. // Loop breaking in and waiting for the target.
  397. //
  398. while ((Context.Flags & DEBUGGER_FLAG_EXITING) == 0) {
  399. //
  400. // Loop waiting for the target to break in.
  401. //
  402. while ((Context.TargetFlags & DEBUGGER_TARGET_RUNNING) != 0) {
  403. //
  404. // Acquire the standard out lock to synchronize with remote threads
  405. // trying to send updated source information.
  406. //
  407. AcquireDebuggerLock(Context.StandardOut.Lock);
  408. DbgrUnhighlightCurrentLine(&Context);
  409. ReleaseDebuggerLock(Context.StandardOut.Lock);
  410. Result = DbgrWaitForEvent(&Context);
  411. if (Result != 0) {
  412. DbgOut("Error getting data from target!\n");
  413. goto MainEnd;
  414. }
  415. }
  416. //
  417. // Process a command from the user.
  418. //
  419. DbgrpPrintCommandPrompt(&Context);
  420. Result = DbgrGetCommand(&Context);
  421. UiEnableCommands(FALSE);
  422. if (Result == FALSE) {
  423. DbgOut("Failed to get command.\n");
  424. Result = EINVAL;
  425. goto MainEnd;
  426. }
  427. if (Context.CommandBuffer[0] == '\0') {
  428. continue;
  429. }
  430. Result = DbgrpSplitCommandArguments(Context.CommandBuffer,
  431. CommandArguments,
  432. &CommandArgumentCount);
  433. if (Result == FALSE) {
  434. Result = EINVAL;
  435. goto MainEnd;
  436. }
  437. Command = CommandArguments[0];
  438. CommandEntry = DbgrLookupCommand(Command);
  439. if (CommandEntry == NULL) {
  440. DbgOut("Error: Unrecognized command \"%s\"\n", Command);
  441. continue;
  442. }
  443. //
  444. // Run the command.
  445. //
  446. CommandEntry->CommandRoutine(&Context,
  447. CommandArguments,
  448. CommandArgumentCount);
  449. DbgrpSetPromptText(&Context, NULL);
  450. }
  451. Result = 0;
  452. MainEnd:
  453. DbgrDestroy(&Context, ConnectionType);
  454. if (ExtensionsInitialized != FALSE) {
  455. DbgUnloadAllExtensions(&Context);
  456. }
  457. DestroyCommunications();
  458. if (Context.SymbolPath != NULL) {
  459. for (PathIndex = 0;
  460. PathIndex < Context.SymbolPathCount;
  461. PathIndex += 1) {
  462. if (Context.SymbolPath[PathIndex] != NULL) {
  463. free(Context.SymbolPath[PathIndex]);
  464. }
  465. }
  466. free(Context.SymbolPath);
  467. }
  468. ReturnValue = 0;
  469. if (Result != 0) {
  470. DbgOut("*** Session Ended ***\n");
  471. DbgrOsPrepareToReadInput();
  472. DbgrOsGetCharacter(NULL, NULL);
  473. DbgrOsPostInputCallback();
  474. ReturnValue = 1;
  475. }
  476. DbgrpServerDestroy(&Context);
  477. DbgrDestroyConsoleIo(&Context);
  478. for (HistoryIndex = 0;
  479. HistoryIndex < Context.CommandHistorySize;
  480. HistoryIndex += 1) {
  481. if (Context.CommandHistory[HistoryIndex] != NULL) {
  482. free(Context.CommandHistory[HistoryIndex]);
  483. }
  484. }
  485. if (Context.CommandHistory != NULL) {
  486. free(Context.CommandHistory);
  487. }
  488. if (Context.CommandBuffer != NULL) {
  489. free(Context.CommandBuffer);
  490. }
  491. if (Context.SourceFile.Path != NULL) {
  492. free(Context.SourceFile.Path);
  493. }
  494. if (Context.SourceFile.ActualPath != NULL) {
  495. free(Context.SourceFile.ActualPath);
  496. }
  497. if (Context.SourceFile.Contents != NULL) {
  498. free(Context.SourceFile.Contents);
  499. }
  500. DbgrpDestroyAllSourcePaths(&Context);
  501. exit(ReturnValue);
  502. return ReturnValue;
  503. }
  504. BOOL
  505. DbgrGetCommand (
  506. PDEBUGGER_CONTEXT Context
  507. )
  508. /*++
  509. Routine Description:
  510. This routine retrieves a command from the user or a remote client.
  511. Arguments:
  512. Context - Supplies a pointer to the application context.
  513. Return Value:
  514. Returns TRUE on success, FALSE on failure.
  515. --*/
  516. {
  517. ULONG BufferSize;
  518. PSTR CommandBuffer;
  519. ULONG CommandLength;
  520. UCHAR ControlKey;
  521. BOOL Done;
  522. PSTR *History;
  523. ULONG HistoryIndex;
  524. ULONG HistoryNextIndex;
  525. LONG HistoryOffset;
  526. ULONG HistorySize;
  527. UCHAR Key;
  528. LONG NextHistoryOffset;
  529. PSTR PreviousCommand;
  530. PDEBUGGER_REMOTE_COMMAND RemoteCommand;
  531. BOOL Result;
  532. CommandBuffer = Context->CommandBuffer;
  533. BufferSize = Context->CommandBufferSize;
  534. assert((CommandBuffer != NULL) && (BufferSize != 0));
  535. History = Context->CommandHistory;
  536. HistoryOffset = 0;
  537. HistorySize = Context->CommandHistorySize;
  538. HistoryNextIndex = Context->CommandHistoryNextIndex;
  539. if (HistoryNextIndex == 0) {
  540. PreviousCommand = History[HistorySize - 1];
  541. } else {
  542. PreviousCommand = History[HistoryNextIndex - 1];
  543. }
  544. DbgrOsPrepareToReadInput();
  545. CommandLength = 0;
  546. Done = FALSE;
  547. while (Done == FALSE) {
  548. //
  549. // Retrieve one key.
  550. //
  551. Result = DbgrOsGetCharacter(&Key, &ControlKey);
  552. if (Result == FALSE) {
  553. goto GetCommandEnd;
  554. }
  555. //
  556. // Process non-printing keys.
  557. //
  558. if (Key == 0) {
  559. switch (ControlKey) {
  560. //
  561. // Enter signals the completion of a command.
  562. //
  563. case KEY_RETURN:
  564. Done = TRUE;
  565. break;
  566. //
  567. // Escape deletes everything on the current line.
  568. //
  569. case KEY_ESCAPE:
  570. UiSetCommandText("");
  571. CommandLength = 0;
  572. break;
  573. //
  574. // Up and down get recently entered commands.
  575. //
  576. case KEY_DOWN:
  577. case KEY_UP:
  578. NextHistoryOffset = HistoryOffset;
  579. if (ControlKey == KEY_UP) {
  580. if (HistoryOffset + 1 < HistorySize) {
  581. NextHistoryOffset = HistoryOffset + 1;
  582. }
  583. } else {
  584. if (HistoryOffset > 0) {
  585. NextHistoryOffset = HistoryOffset - 1;
  586. }
  587. }
  588. if (NextHistoryOffset > HistoryNextIndex) {
  589. HistoryIndex = HistoryNextIndex + HistorySize -
  590. NextHistoryOffset;
  591. } else {
  592. HistoryIndex = HistoryNextIndex - NextHistoryOffset;
  593. }
  594. if (History[HistoryIndex] != NULL) {
  595. UiSetCommandText(History[HistoryIndex]);
  596. CommandLength = 0;
  597. HistoryOffset = NextHistoryOffset;
  598. }
  599. break;
  600. //
  601. // Check for a remote command.
  602. //
  603. case KEY_REMOTE:
  604. RemoteCommand = NULL;
  605. AcquireDebuggerLock(Context->StandardIn.Lock);
  606. if (LIST_EMPTY(&(Context->StandardIn.RemoteCommandList)) ==
  607. FALSE) {
  608. RemoteCommand = LIST_VALUE(
  609. Context->StandardIn.RemoteCommandList.Next,
  610. DEBUGGER_REMOTE_COMMAND,
  611. ListEntry);
  612. LIST_REMOVE(&(RemoteCommand->ListEntry));
  613. }
  614. ReleaseDebuggerLock(Context->StandardIn.Lock);
  615. if (RemoteCommand != NULL) {
  616. strncpy(CommandBuffer, RemoteCommand->Command, BufferSize);
  617. CommandBuffer[BufferSize - 1] = '\0';
  618. DbgOut("%s\t\t[%s@%s]\n",
  619. CommandBuffer,
  620. RemoteCommand->User,
  621. RemoteCommand->Host);
  622. free(RemoteCommand->Command);
  623. free(RemoteCommand->User);
  624. free(RemoteCommand->Host);
  625. free(RemoteCommand);
  626. goto GetCommandEnd;
  627. }
  628. break;
  629. default:
  630. break;
  631. }
  632. } else {
  633. //
  634. // Copy the key into the current buffer position.
  635. //
  636. CommandBuffer[CommandLength] = Key;
  637. CommandLength += 1;
  638. if ((Context->Flags & DEBUGGER_FLAG_ECHO_COMMANDS) != 0) {
  639. DbgOut("%c", Key);
  640. }
  641. if (CommandLength + 1 >= BufferSize) {
  642. Done = TRUE;
  643. }
  644. }
  645. }
  646. DbgrOsPostInputCallback();
  647. CommandBuffer[CommandLength] = '\0';
  648. //
  649. // If the command was not empty, copy it into the history as the most
  650. // recent entry.
  651. //
  652. if (CommandLength != 0) {
  653. if ((PreviousCommand == NULL) ||
  654. (strcmp(CommandBuffer, PreviousCommand) != 0)) {
  655. if (History[HistoryNextIndex] != NULL) {
  656. free(History[HistoryNextIndex]);
  657. }
  658. History[HistoryNextIndex] = strdup(CommandBuffer);
  659. HistoryNextIndex += 1;
  660. if (HistoryNextIndex == HistorySize) {
  661. HistoryNextIndex = 0;
  662. }
  663. }
  664. //
  665. // If the command was empty, repeat the most recent command.
  666. //
  667. } else {
  668. *CommandBuffer = '\0';
  669. if (PreviousCommand != NULL) {
  670. strcpy(CommandBuffer, PreviousCommand);
  671. }
  672. if (CommandLength == 0) {
  673. DbgOut(CommandBuffer);
  674. }
  675. if ((Context->Flags & DEBUGGER_FLAG_ECHO_COMMANDS) == 0) {
  676. DbgOut("\n");
  677. }
  678. }
  679. if ((Context->Flags & DEBUGGER_FLAG_ECHO_COMMANDS) != 0) {
  680. DbgOut("\n");
  681. }
  682. Result = TRUE;
  683. GetCommandEnd:
  684. Context->CommandHistoryNextIndex = HistoryNextIndex;
  685. return Result;
  686. }
  687. VOID
  688. DbgrpSetPromptText (
  689. PDEBUGGER_CONTEXT Context,
  690. PSTR Prompt
  691. )
  692. /*++
  693. Routine Description:
  694. This routine sets the command prompt to the given string.
  695. Arguments:
  696. Context - Supplies a pointer to the applicaton context.
  697. Prompt - Supplies a pointer to the null terminated string containing the
  698. prompt to set.
  699. Return Value:
  700. None.
  701. --*/
  702. {
  703. AcquireDebuggerLock(Context->StandardOut.Lock);
  704. if (Context->StandardOut.Prompt != NULL) {
  705. free(Context->StandardOut.Prompt);
  706. Context->StandardOut.Prompt = NULL;
  707. }
  708. if (Prompt != NULL) {
  709. Context->StandardOut.Prompt = strdup(Prompt);
  710. UiSetPromptText(Prompt);
  711. } else {
  712. UiSetPromptText("");
  713. }
  714. DbgrpServerNotifyClients(Context);
  715. ReleaseDebuggerLock(Context->StandardOut.Lock);
  716. return;
  717. }
  718. BOOL
  719. DbgrpSplitCommandArguments (
  720. PSTR Input,
  721. PSTR Arguments[DEBUGGER_MAX_COMMAND_ARGUMENTS],
  722. PULONG ArgumentCount
  723. )
  724. /*++
  725. Routine Description:
  726. This routine splits a command line into its arguments.
  727. Arguments:
  728. Input - Supplies a pointer to the input command line buffer.
  729. Arguments - Supplies a pointer to an array of strings that will receive any
  730. additional arguments.
  731. ArgumentCount - Supplies a pointer where the count of arguments will be
  732. returned.
  733. Return Value:
  734. Returns TRUE on success, FALSE on failure.
  735. --*/
  736. {
  737. ULONG ArgumentIndex;
  738. BOOL Result;
  739. Result = TRUE;
  740. ArgumentIndex = 0;
  741. while (TRUE) {
  742. //
  743. // Remove spaces before the argument.
  744. //
  745. while (isspace(*Input) != 0) {
  746. Input += 1;
  747. }
  748. if (*Input == '\0') {
  749. break;
  750. }
  751. if (ArgumentIndex < DEBUGGER_MAX_COMMAND_ARGUMENTS) {
  752. Arguments[ArgumentIndex] = Input;
  753. ArgumentIndex += 1;
  754. }
  755. //
  756. // Loop until there's a space.
  757. //
  758. while ((*Input != '\0') && (isspace(*Input) == 0)) {
  759. Input += 1;
  760. }
  761. //
  762. // If it's a terminator, stop. Otherwise, terminate the argument and
  763. // keep moving.
  764. //
  765. if (*Input == '\0') {
  766. break;
  767. } else {
  768. *Input = '\0';
  769. Input += 1;
  770. }
  771. }
  772. *ArgumentCount = ArgumentIndex;
  773. return Result;
  774. }
  775. //
  776. // --------------------------------------------------------- Internal Functions
  777. //
  778. VOID
  779. DbgrpPrintCommandPrompt (
  780. PDEBUGGER_CONTEXT Context
  781. )
  782. /*++
  783. Routine Description:
  784. This routine prints the debugger command prompt, indicating to the user to
  785. enter a command.
  786. Arguments:
  787. Context - Supplies a pointer to the debugger context.
  788. Return Value:
  789. None.
  790. --*/
  791. {
  792. PBREAK_NOTIFICATION Break;
  793. CHAR Prompt[32];
  794. assert(Context->CurrentEvent.Type == DebuggerEventBreak);
  795. Break = &(Context->CurrentEvent.BreakNotification);
  796. if (Context->ConnectionType == DebugConnectionKernel) {
  797. if (Break->ProcessorOrThreadCount > 1) {
  798. sprintf(Prompt,
  799. "%d : kd>",
  800. Break->ProcessorOrThreadNumber);
  801. } else {
  802. sprintf(Prompt, "kd>");
  803. }
  804. } else {
  805. assert(Context->ConnectionType == DebugConnectionUser);
  806. sprintf(Prompt,
  807. "%x:%x>",
  808. Break->Process,
  809. Break->ProcessorOrThreadNumber);
  810. }
  811. DbgrpSetPromptText(Context, Prompt);
  812. DbgOut(Prompt);
  813. UiSetCommandText("");
  814. UiEnableCommands(TRUE);
  815. return;
  816. }