debug.c 27 KB

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