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.

1115 lines
38 KiB

  1. //----------------------------------------------------------------------------
  2. //
  3. // Command-line parsing and main routine.
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999-2001.
  6. //
  7. //----------------------------------------------------------------------------
  8. #include "pch.cpp"
  9. #pragma hdrstop
  10. #include "conio.hpp"
  11. #include "engine.hpp"
  12. #include "ini.hpp"
  13. #include "main.hpp"
  14. // Values set from command-line arguments.
  15. BOOL g_RemoteClient;
  16. BOOL g_DetachOnExitRequired;
  17. BOOL g_DetachOnExitImplied;
  18. PSTR g_DumpFile;
  19. PSTR g_DumpPageFile;
  20. PSTR g_InitialCommand;
  21. PSTR g_ConnectOptions;
  22. PSTR g_CommandLine;
  23. PSTR g_RemoteOptions;
  24. PSTR g_ProcessServer;
  25. PSTR g_ProcNameToDebug;
  26. ULONG g_IoRequested = IO_CONSOLE;
  27. ULONG g_IoMode;
  28. ULONG g_CreateFlags = DEBUG_ONLY_THIS_PROCESS;
  29. ULONG g_AttachKernelFlags = DEBUG_ATTACH_KERNEL_CONNECTION;
  30. ULONG g_PidToDebug;
  31. ULONG64 g_EventToSignal;
  32. ULONG g_HistoryLines = 10000;
  33. ULONG g_AttachProcessFlags = DEBUG_ATTACH_DEFAULT;
  34. PSTR g_DebuggerName = DEBUGGER_NAME;
  35. PSTR g_InitialInputFile = "ntsd.ini";
  36. FILE* g_InputFile;
  37. FILE* g_OldInputFiles[MAX_INPUT_NESTING];
  38. ULONG g_NextOldInputFile;
  39. // Command line temporaries.
  40. int g_Argc;
  41. PSTR* g_Argv;
  42. PSTR g_CurArg = "program name";
  43. PSTR g_CmdStr, g_PrevCmdStr;
  44. void
  45. ExecuteCmd(PSTR Cmd, char CmdExtra, char Sep, PSTR Args)
  46. {
  47. PSTR CmdLine;
  48. char Buffer[MAX_PATH * 2];
  49. strcpy(Buffer, Cmd);
  50. CmdLine = Buffer + strlen(Buffer);
  51. if (CmdExtra)
  52. {
  53. *CmdLine++ = CmdExtra;
  54. }
  55. if (Sep)
  56. {
  57. *CmdLine++ = Sep;
  58. }
  59. strcpy(CmdLine, Args);
  60. g_DbgControl->Execute(DEBUG_OUTCTL_IGNORE, Buffer,
  61. DEBUG_EXECUTE_NOT_LOGGED);
  62. }
  63. char BlanksForPadding[] =
  64. " "
  65. " "
  66. " ";
  67. void
  68. Usage(void)
  69. {
  70. // Dump an initial debug message about the invalid command
  71. // line. This will show up on kd if kd is hooked up,
  72. // handling the case of an ntsd -d with bad parameters
  73. // where the console may not be useful.
  74. OutputDebugStringA(g_DebuggerName);
  75. OutputDebugStringA(": Bad command line: '");
  76. OutputDebugStringA(GetCommandLineA());
  77. OutputDebugStringA("'\n");
  78. int cbPrefix;
  79. cbPrefix = 7 + strlen(g_DebuggerName);
  80. ConOut( "usage: %s", g_DebuggerName );
  81. #ifndef KERNEL
  82. ConOut( " [-?] [-2] [-d] [-g] [-G] [-myob] [-lines] [-n] [-o] [-s] [-v] [-w] \n" );
  83. ConOut( "%.*s [-r BreakErrorLevel] [-t PrintErrorLevel]\n", cbPrefix, BlanksForPadding );
  84. ConOut( "%.*s [-hd] [-pd] [-pe] [-pt #] [-pv] [-x | -x{e|d|n|i} <event>] \n", cbPrefix, BlanksForPadding );
  85. ConOut( "%.*s [-- | -p pid | -pn name | command-line | -z CrashDmpFile]\n", cbPrefix, BlanksForPadding );
  86. ConOut( "%.*s [-zp CrashPageFile] [-premote transport] [-robp]\n", cbPrefix, BlanksForPadding );
  87. #else // #ifndef KERNEL
  88. ConOut(" [-?] [-b | -x] [-d] [-m] [-myob] [-lines] [-n] [-r] [-s] [-v] \n" );
  89. ConOut("%.*s [-k ConnectOptions] [-kl] [-kx ConnectOptions]\n", cbPrefix, BlanksForPadding );
  90. ConOut("%.*s [-z CrashDmpFile] [-zp CrashPageFile]\n", cbPrefix, BlanksForPadding );
  91. #endif
  92. ConOut("%.*s [-aDllName] [-c \"command\"] [-i ImagePath] [-y SymbolsPath] \n", cbPrefix, BlanksForPadding );
  93. ConOut("%.*s [-clines #] [-srcpath SourcePath] [-QR \\\\machine] [-wake <pid>]\n", cbPrefix, BlanksForPadding );
  94. ConOut("%.*s [-remote transport:server=name,portid] [-server transport:portid]\n", cbPrefix, BlanksForPadding );
  95. ConOut("%.*s [-ses] [-sfce] [-sicv] [-snul] [-noio] [-failinc] [-noshell]\n", cbPrefix, BlanksForPadding );
  96. ConOut( "\n" );
  97. cbPrefix = 7;
  98. ConOut( "where: " );
  99. ConOut( "-? displays this help text\n" );
  100. #ifndef KERNEL
  101. ConOut( "%.*scommand-line is the command to run under the debugger\n", cbPrefix, BlanksForPadding );
  102. ConOut( "%.*s-- is the same as -G -g -o -p -1 -d -pd\n", cbPrefix, BlanksForPadding );
  103. #endif
  104. ConOut( "%.*s-aDllName sets the default extension DLL\n", cbPrefix, BlanksForPadding );
  105. #ifdef KERNEL
  106. ConOut("%.*s-b Break into kernel when connection is established\n", cbPrefix, BlanksForPadding);
  107. #endif
  108. ConOut("%.*s-c executes the following debugger command\n", cbPrefix, BlanksForPadding );
  109. ConOut("%.*s-clines number of lines of output history retrieved by a remote client\n", cbPrefix, BlanksForPadding );
  110. ConOut("%.*s-failinc causes incomplete symbol and module loads to fail\n", cbPrefix, BlanksForPadding );
  111. #ifndef KERNEL
  112. ConOut( "%.*s-d sends all debugger output to kernel debugger via DbgPrint\n", cbPrefix, BlanksForPadding );
  113. ConOut( "%.*s -d cannot be used with debugger remoting\n", cbPrefix, BlanksForPadding );
  114. ConOut( "%.*s -d can only be used when the kernel debugger is enabled\n", cbPrefix, BlanksForPadding );
  115. ConOut( "%.*s-g ignores initial breakpoint in debuggee\n", cbPrefix, BlanksForPadding );
  116. ConOut( "%.*s-G ignores final breakpoint at process termination\n", cbPrefix, BlanksForPadding );
  117. ConOut( "%.*s-hd specifies that the debug heap should not be used\n", cbPrefix, BlanksForPadding );
  118. ConOut( "%.*s for created processes. This only works on Windows Whistler.\n", cbPrefix, BlanksForPadding );
  119. ConOut( "%.*s-o debugs all processes launched by debuggee\n", cbPrefix, BlanksForPadding );
  120. ConOut( "%.*s-p pid specifies the decimal process Id to attach to\n", cbPrefix, BlanksForPadding );
  121. ConOut( "%.*s-pd specifies that the debugger should automatically detach\n", cbPrefix, BlanksForPadding );
  122. ConOut( "%.*s-pe specifies that any attach should be to an existing debug port\n", cbPrefix, BlanksForPadding );
  123. ConOut( "%.*s-pn name specifies the name of the process to attach to\n", cbPrefix, BlanksForPadding );
  124. ConOut( "%.*s-pt # specifies the interrupt timeout\n", cbPrefix, BlanksForPadding );
  125. ConOut( "%.*s-pv specifies that any attach should be noninvasive\n", cbPrefix, BlanksForPadding );
  126. ConOut( "%.*s-r specifies the (0-3) error level to break on (SeeSetErrorLevel)\n", cbPrefix, BlanksForPadding );
  127. ConOut( "%.*s-robp allows breakpoints to be set in read-only memory\n", cbPrefix, BlanksForPadding );
  128. ConOut( "%.*s-t specifies the (0-3) error level to display (SeeSetErrorLevel)\n", cbPrefix, BlanksForPadding );
  129. ConOut( "%.*s-w specifies to debug 16 bit applications in a separate VDM\n", cbPrefix, BlanksForPadding );
  130. ConOut( "%.*s-x sets second-chance break on AV exceptions\n", cbPrefix, BlanksForPadding );
  131. ConOut( "%.*s-x{e|d|n|i} <event> sets the break status for the specified event\n", cbPrefix, BlanksForPadding );
  132. ConOut( "%.*s-2 creates a separate console window for debuggee\n", cbPrefix, BlanksForPadding );
  133. #else // #ifndef KERNEL
  134. ConOut("%.*s-d Breaks into kernel on first module load\n", cbPrefix, BlanksForPadding);
  135. ConOut("%.*s-k Tell the debugger how to connect to the target\n", cbPrefix, BlanksForPadding);
  136. ConOut("%.*s serial:modem connects through a modem\n", cbPrefix, BlanksForPadding);
  137. ConOut("%.*s com:port=id,baud=rate connects through a COM port\n", cbPrefix, BlanksForPadding);
  138. ConOut("%.*s id: com port name, of the form com2 or \\\\.\\com12 \n", cbPrefix, BlanksForPadding);
  139. ConOut("%.*s rate: valid baudrate value, such as 57600 \n", cbPrefix, BlanksForPadding);
  140. ConOut("%.*s 1394:channel=chan connects over 1394\n", cbPrefix, BlanksForPadding);
  141. ConOut("%.*s chan: 1394 channel number, must match channel used at boot\n", cbPrefix, BlanksForPadding);
  142. ConOut("%.*s-kl Tell the debugger to connect to the local machine\n", cbPrefix, BlanksForPadding);
  143. ConOut("%.*s-kx Tell the debugger to connect to an eXDI driver\n", cbPrefix, BlanksForPadding);
  144. ConOut("%.*s-m Serial port is a modem, watch for carrier detect\n", cbPrefix, BlanksForPadding);
  145. ConOut("%.*s-r Display registers\n", cbPrefix, BlanksForPadding);
  146. ConOut("%.*s-x Same as -b, except uses an initial command of eb NtGlobalFlag 9;g\n", cbPrefix, BlanksForPadding);
  147. #endif // #ifndef KERNEL
  148. ConOut("%.*s-i ImagePath specifies the location of the executables that generated\n", cbPrefix, BlanksForPadding);
  149. ConOut("%.*s the fault (see _NT_EXECUTABLE_IMAGE_PATH)\n", cbPrefix, BlanksForPadding);
  150. ConOut("%.*s-lines requests that line number information be used if present\n", cbPrefix, BlanksForPadding );
  151. ConOut("%.*s-myob ignores version mismatches in DBGHELP.DLL\n", cbPrefix, BlanksForPadding );
  152. ConOut("%.*s-n enables verbose output from symbol handler\n", cbPrefix, BlanksForPadding );
  153. ConOut("%.*s-noio disables all I/O for dedicated remoting servers\n", cbPrefix, BlanksForPadding );
  154. ConOut("%.*s-noshell disables the .shell (!!) command\n", cbPrefix, BlanksForPadding );
  155. ConOut("%.*s-QR <\\\\machine> queries for remote servers\n", cbPrefix, BlanksForPadding );
  156. ConOut("%.*s-s disables lazy symbol loading\n", cbPrefix, BlanksForPadding );
  157. ConOut("%.*s-ses enables strict symbol loading\n", cbPrefix, BlanksForPadding );
  158. ConOut("%.*s-sfce fails critical errors encountered during file searching\n", cbPrefix, BlanksForPadding );
  159. ConOut("%.*s-sicv ignores the CV record when symbol loading\n", cbPrefix, BlanksForPadding );
  160. ConOut("%.*s-snul disables automatic symbol loading for unqualified names\n", cbPrefix, BlanksForPadding );
  161. ConOut("%.*s-srcpath <SourcePath> specifies the source search path\n", cbPrefix, BlanksForPadding );
  162. ConOut("%.*s-v enables verbose output from debugger\n", cbPrefix, BlanksForPadding );
  163. ConOut("%.*s-wake <pid> wakes up a sleeping debugger and exits\n", cbPrefix, BlanksForPadding );
  164. ConOut("%.*s-y <SymbolsPath> specifies the symbol search path (see _NT_SYMBOL_PATH)\n", cbPrefix, BlanksForPadding);
  165. ConOut("%.*s-z <CrashDmpFile> specifies the name of a crash dump file to debug\n", cbPrefix, BlanksForPadding);
  166. ConOut("%.*s-zp <CrashPageFile> specifies the name of a page.dmp file\n", cbPrefix, BlanksForPadding);
  167. ConOut("%.*s to use with a crash dump\n", cbPrefix, BlanksForPadding);
  168. ConOut("%.*s-remote lets you connect to a debugger session started with -server \n", cbPrefix, BlanksForPadding);
  169. ConOut("%.*s must be the first argument if present\n", cbPrefix, BlanksForPadding);
  170. ConOut("%.*s transport: tcp | npipe | ssl | spipe | 1394 | com\n", cbPrefix, BlanksForPadding);
  171. ConOut("%.*s name: machine name on which the debug server was created\n", cbPrefix, BlanksForPadding);
  172. ConOut("%.*s portid: id of the port the debugger server was created on\n", cbPrefix, BlanksForPadding);
  173. ConOut("%.*s for tcp use: port=<socket port #>\n", cbPrefix, BlanksForPadding);
  174. ConOut("%.*s for npipe use: pipe=<name of pipe>\n", cbPrefix, BlanksForPadding);
  175. ConOut("%.*s for 1394 use: channel=<channel #>\n", cbPrefix, BlanksForPadding);
  176. ConOut("%.*s for com use: port=<COM port>,baud=<baud rate>,\n", cbPrefix, BlanksForPadding);
  177. ConOut("%.*s channel=<channel #>\n", cbPrefix, BlanksForPadding);
  178. ConOut("%.*s for ssl and spipe see the documentation\n", cbPrefix, BlanksForPadding);
  179. ConOut("%.*s example: ... -remote npipe:server=yourmachine,pipe=foobar \n", cbPrefix, BlanksForPadding);
  180. ConOut("%.*s-server creates a debugger session other people can connect to\n", cbPrefix, BlanksForPadding);
  181. ConOut("%.*s must be the first argument if present\n", cbPrefix, BlanksForPadding);
  182. ConOut("%.*s transport: tcp | npipe | ssl | spipe | 1394 | com\n", cbPrefix, BlanksForPadding);
  183. ConOut("%.*s portid: id of the port remote users can connect to\n", cbPrefix, BlanksForPadding);
  184. ConOut("%.*s for tcp use: port=<socket port #>\n", cbPrefix, BlanksForPadding);
  185. ConOut("%.*s for npipe use: pipe=<name of pipe>\n", cbPrefix, BlanksForPadding);
  186. ConOut("%.*s for 1394 use: channel=<channel #>\n", cbPrefix, BlanksForPadding);
  187. ConOut("%.*s for com use: port=<COM port>,baud=<baud rate>,\n", cbPrefix, BlanksForPadding);
  188. ConOut("%.*s channel=<channel #>\n", cbPrefix, BlanksForPadding);
  189. ConOut("%.*s for ssl and spipe see the documentation\n", cbPrefix, BlanksForPadding);
  190. ConOut("%.*s example: ... -server npipe:pipe=foobar \n", cbPrefix, BlanksForPadding);
  191. #ifndef KERNEL
  192. ConOut( "%.*s-premote transport specifies the process server to connect to\n", cbPrefix, BlanksForPadding );
  193. ConOut("%.*s transport arguments are given as with remoting\n", cbPrefix, BlanksForPadding);
  194. #endif
  195. ConOut("\n");
  196. ConOut("Environment Variables:\n\n");
  197. ConOut(" _NT_SYMBOL_PATH=[Drive:][Path]\n");
  198. ConOut(" Specify symbol image path.\n\n");
  199. ConOut(" _NT_ALT_SYMBOL_PATH=[Drive:][Path]\n");
  200. ConOut(" Specify an alternate symbol image path.\n\n");
  201. ConOut(" _NT_DEBUGGER_EXTENSION_PATH=[Drive:][Path]\n");
  202. ConOut(" Specify a path which should be searched first for extensions dlls\n\n");
  203. ConOut(" _NT_EXECUTABLE_IMAGE_PATH=[Drive:][Path]\n");
  204. ConOut(" Specify executable image path.\n\n");
  205. ConOut(" _NT_SOURCE_PATH=[Drive:][Path]\n");
  206. ConOut(" Specify source file path.\n\n");
  207. ConOut(" _NT_DEBUG_LOG_FILE_OPEN=filename\n");
  208. ConOut(" If specified, all output will be written to this file from offset 0.\n\n");
  209. ConOut(" _NT_DEBUG_LOG_FILE_APPEND=filename\n");
  210. ConOut(" If specified, all output will be APPENDed to this file.\n\n");
  211. ConOut(" _NT_DEBUG_HISTORY_SIZE=size\n");
  212. ConOut(" Specifies the size of a server's output history in kilobytes\n");
  213. #ifdef KERNEL
  214. ConOut(" _NT_DEBUG_BUS=1394\n");
  215. ConOut(" Specifies the type of BUS the kernel debugger will use to communicate with the target\n\n");
  216. ConOut(" _NT_DEBUG_1394_CHANNEL=number\n");
  217. ConOut(" Specifies the channel to be used over the 1394 bus\n\n");
  218. ConOut(" _NT_DEBUG_PORT=com[1|2|...]\n");
  219. ConOut(" Specify which com port to use. (Default = com1)\n\n");
  220. ConOut(" _NT_DEBUG_BAUD_RATE=baud rate\n");
  221. ConOut(" Specify the baud rate used by debugging serial port. (Default = 19200)\n\n");
  222. ConOut(" _NT_DEBUG_CACHE_SIZE=x\n");
  223. ConOut(" If specified, gives the number of bytes cached on debugger side\n");
  224. ConOut(" of kernel debugger serial connection (default is 102400).\n\n");
  225. ConOut(" KDQUIET=anything\n" );
  226. ConOut(" If defined, disables obnoxious warning message displayed when user\n");
  227. ConOut(" presses Ctrl-C\n\n");
  228. #endif
  229. ConOut("\n");
  230. ConOut("Control Keys:\n\n");
  231. #ifdef KERNEL
  232. ConOut(" <Ctrl-A><Enter> Toggle BaudRate\n");
  233. #endif
  234. ConOut(" <Ctrl-B><Enter> Quit debugger\n");
  235. ConOut(" <Ctrl-C> Break into Target\n");
  236. #ifdef KERNEL
  237. ConOut(" <Ctrl-D><Enter> Display debugger debugging information\n");
  238. ConOut(" <Ctrl-F><Enter> Force a break into the kernel (same as Ctrl-C)\n");
  239. #else
  240. ConOut(" <Ctrl-F><Enter> Force a break into debuggee (same as Ctrl-C)\n");
  241. #endif
  242. #ifdef KERNEL
  243. ConOut(" <Ctrl-K><Enter> Toggle Initial Breakpoint\n");
  244. #endif
  245. ConOut(" <Ctrl-P><Enter> Debug Current debugger\n");
  246. #ifdef KERNEL
  247. ConOut(" <Ctrl-R><Enter> Resynchronize target and host\n");
  248. #endif
  249. ConOut(" <Ctrl-V><Enter> Toggle Verbose mode\n");
  250. ConOut(" <Ctrl-W><Enter> Print version information\n");
  251. }
  252. PSTR
  253. GetArg(void)
  254. {
  255. if (g_Argc == 0)
  256. {
  257. Usage();
  258. ErrorExit("Missing argument for %s\n", g_CurArg);
  259. }
  260. g_Argc--;
  261. g_CurArg = *g_Argv;
  262. g_Argv++;
  263. // Move forward in the command string to skip over
  264. // the argument just consumed from argv. This is complicated
  265. // by quoting that may be present in the command string
  266. // that was filtered by the CRT.
  267. while (*g_CmdStr == ' ' || *g_CmdStr == '\t')
  268. {
  269. g_CmdStr++;
  270. }
  271. g_PrevCmdStr = g_CmdStr;
  272. int NumSlash;
  273. BOOL InQuote = FALSE;
  274. for (;;)
  275. {
  276. // Rules: 2N backslashes + " ==> N backslashes and begin/end quote
  277. // 2N+1 backslashes + " ==> N backslashes + literal "
  278. // N backslashes ==> N backslashes
  279. NumSlash = 0;
  280. while (*g_CmdStr == '\\')
  281. {
  282. // Count number of backslashes for use below
  283. ++g_CmdStr;
  284. ++NumSlash;
  285. }
  286. if (*g_CmdStr == '"')
  287. {
  288. // Of 2N backslashes before, start/end quote, otherwise
  289. // copy literally
  290. if (NumSlash % 2 == 0)
  291. {
  292. if (InQuote)
  293. {
  294. if (g_CmdStr[1] == '"')
  295. {
  296. // Double quote inside quoted string
  297. g_CmdStr++;
  298. }
  299. }
  300. InQuote = !InQuote;
  301. }
  302. }
  303. // If at end of arg, break loop
  304. if (*g_CmdStr == 0 ||
  305. (!InQuote && (*g_CmdStr == ' ' || *g_CmdStr == '\t')))
  306. {
  307. break;
  308. }
  309. ++g_CmdStr;
  310. }
  311. return g_CurArg;
  312. }
  313. void
  314. ParseCommandLine(int Argc, PCHAR* Argv, PSTR CmdStr)
  315. {
  316. PSTR Arg;
  317. BOOL ShowUsage = FALSE;
  318. ULONG OutMask;
  319. ULONG SystemErrorBreak;
  320. ULONG SystemErrorOutput;
  321. BOOL CheckMoreArgs = FALSE;
  322. g_Argc = Argc;
  323. g_Argv = Argv;
  324. g_CmdStr = CmdStr;
  325. // Skip program name.
  326. GetArg();
  327. // Check for remote arguments. They must
  328. // be the first arguments if present at all.
  329. if (g_Argc > 0)
  330. {
  331. if (!_strcmpi(*g_Argv, "-remote"))
  332. {
  333. GetArg();
  334. g_RemoteOptions = GetArg();
  335. g_RemoteClient = TRUE;
  336. ConnectEngine(g_RemoteOptions);
  337. }
  338. else if (!_strcmpi(*g_Argv, "-server"))
  339. {
  340. GetArg();
  341. g_RemoteOptions = GetArg();
  342. }
  343. }
  344. if (g_DbgClient == NULL)
  345. {
  346. // We didn't connect to a remote session so create
  347. // a new local session.
  348. CreateEngine(g_RemoteOptions);
  349. }
  350. if (!g_RemoteClient)
  351. {
  352. // Establish defaults.
  353. #ifdef KERNEL
  354. g_DbgControl->SetEngineOptions(0);
  355. #else
  356. g_DbgControl->SetEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK |
  357. DEBUG_ENGOPT_FINAL_BREAK);
  358. #endif
  359. g_DbgSymbols->SetSymbolOptions(SYMOPT_CASE_INSENSITIVE |
  360. SYMOPT_UNDNAME |
  361. SYMOPT_NO_CPP |
  362. SYMOPT_OMAP_FIND_NEAREST |
  363. SYMOPT_DEFERRED_LOADS);
  364. // Process the ini file for the base settings.
  365. ReadIniFile(&g_CreateFlags);
  366. }
  367. g_DbgClient->GetOutputMask(&OutMask);
  368. // Now process command line arguments.
  369. while (g_Argc > 0)
  370. {
  371. if (!CheckMoreArgs || !Arg[1])
  372. {
  373. Arg = GetArg();
  374. if (Arg[0] != '-' && Arg[0] != '/')
  375. {
  376. // Put argument back.
  377. g_Argv--;
  378. g_Argc++;
  379. g_CmdStr = g_PrevCmdStr;
  380. break;
  381. }
  382. // -remote and -server must be the first
  383. // arguments. Check for them later to
  384. // give a specific error message.
  385. if (!_strcmpi(Arg, "-remote") ||
  386. !_strcmpi(Arg, "-server"))
  387. {
  388. ConOut("%s: %s must be the first argument\n",
  389. g_DebuggerName, Arg);
  390. ShowUsage = TRUE;
  391. break;
  392. }
  393. }
  394. CheckMoreArgs = FALSE;
  395. Arg++;
  396. switch(tolower(Arg[0]))
  397. {
  398. case '?':
  399. ShowUsage = TRUE;
  400. break;
  401. case 'a':
  402. ULONG64 Handle;
  403. g_DbgControl->AddExtension(Arg + 1, DEBUG_EXTENSION_AT_ENGINE,
  404. &Handle);
  405. break;
  406. case 'c':
  407. if (!_stricmp(Arg, "clines"))
  408. {
  409. g_HistoryLines = atoi(GetArg());
  410. }
  411. else
  412. {
  413. g_InitialCommand = GetArg();
  414. }
  415. break;
  416. case 'f':
  417. if (!_stricmp(Arg, "failinc"))
  418. {
  419. g_DbgControl->
  420. AddEngineOptions(DEBUG_ENGOPT_FAIL_INCOMPLETE_INFORMATION);
  421. g_DbgSymbols->
  422. AddSymbolOptions(SYMOPT_EXACT_SYMBOLS);
  423. }
  424. else
  425. {
  426. goto BadSwitch;
  427. }
  428. break;
  429. case 'g':
  430. if (Arg[0] == 'g')
  431. {
  432. g_DbgControl->RemoveEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK);
  433. }
  434. else
  435. {
  436. g_DbgControl->RemoveEngineOptions(DEBUG_ENGOPT_FINAL_BREAK);
  437. }
  438. CheckMoreArgs = TRUE;
  439. break;
  440. case 'i':
  441. g_DbgSymbols->SetImagePath(GetArg());
  442. break;
  443. case 'l':
  444. if (_stricmp(Arg, "lines") == 0)
  445. {
  446. g_DbgSymbols->AddSymbolOptions(SYMOPT_LOAD_LINES);
  447. break;
  448. }
  449. else
  450. {
  451. goto BadSwitch;
  452. }
  453. case 'm':
  454. if (_stricmp(Arg, "myob") == 0)
  455. {
  456. g_DbgControl->
  457. AddEngineOptions(DEBUG_ENGOPT_IGNORE_DBGHELP_VERSION);
  458. break;
  459. }
  460. #ifdef KERNEL
  461. else if (Arg[1] == 0 && !g_RemoteClient)
  462. {
  463. g_ConnectOptions = "com:modem";
  464. }
  465. #endif
  466. else
  467. {
  468. goto BadSwitch;
  469. }
  470. case 'n':
  471. if (_strnicmp (Arg, "netsyms", 7) == 0)
  472. {
  473. //
  474. // undocumented
  475. // netsyms:{yes|no} allow or disallow loading symbols from a network path
  476. //
  477. Arg += 8; // skip over ':' as well.
  478. if (_stricmp (Arg, "no") == 0)
  479. {
  480. g_DbgControl->
  481. RemoveEngineOptions(DEBUG_ENGOPT_ALLOW_NETWORK_PATHS);
  482. g_DbgControl->
  483. AddEngineOptions(DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS);
  484. }
  485. else if (_stricmp (Arg, "yes") == 0)
  486. {
  487. g_DbgControl->RemoveEngineOptions
  488. (DEBUG_ENGOPT_DISALLOW_NETWORK_PATHS);
  489. g_DbgControl->
  490. AddEngineOptions(DEBUG_ENGOPT_ALLOW_NETWORK_PATHS);
  491. }
  492. break;
  493. }
  494. else if (g_RemoteOptions != NULL && !g_RemoteClient &&
  495. !_stricmp(Arg, "noio"))
  496. {
  497. g_IoRequested = IO_NONE;
  498. }
  499. else if (!_stricmp(Arg, "noshell"))
  500. {
  501. g_DbgControl->
  502. AddEngineOptions(DEBUG_ENGOPT_DISALLOW_SHELL_COMMANDS);
  503. }
  504. else if (Arg[1] == 0)
  505. {
  506. g_DbgSymbols->AddSymbolOptions(SYMOPT_DEBUG);
  507. break;
  508. }
  509. else
  510. {
  511. goto BadSwitch;
  512. }
  513. break;
  514. case 'q':
  515. if (Arg[0] != 'Q' || Arg[1] != 'R')
  516. {
  517. goto BadSwitch;
  518. }
  519. Arg = GetArg();
  520. ConOut("Servers on %s:\n", Arg);
  521. if (g_DbgClient->OutputServers(DEBUG_OUTCTL_ALL_CLIENTS, Arg,
  522. DEBUG_SERVERS_ALL) != S_OK)
  523. {
  524. ConOut("Unable to query %s\n", Arg);
  525. }
  526. ExitDebugger(0);
  527. case 's':
  528. if (!_stricmp(Arg, "srcpath"))
  529. {
  530. g_DbgSymbols->SetSourcePath(GetArg());
  531. }
  532. else if (!_stricmp(Arg, "ses"))
  533. {
  534. g_DbgSymbols->AddSymbolOptions(SYMOPT_EXACT_SYMBOLS);
  535. }
  536. else if (!_stricmp(Arg, "sfce"))
  537. {
  538. g_DbgSymbols->AddSymbolOptions(SYMOPT_FAIL_CRITICAL_ERRORS);
  539. }
  540. else if (!_stricmp(Arg, "sicv"))
  541. {
  542. g_DbgSymbols->AddSymbolOptions(SYMOPT_IGNORE_CVREC);
  543. }
  544. else if (!_stricmp(Arg, "snul"))
  545. {
  546. g_DbgSymbols->AddSymbolOptions(SYMOPT_NO_UNQUALIFIED_LOADS);
  547. }
  548. else
  549. {
  550. g_DbgSymbols->RemoveSymbolOptions(SYMOPT_DEFERRED_LOADS);
  551. CheckMoreArgs = TRUE;
  552. }
  553. break;
  554. case 'v':
  555. OutMask |= DEBUG_OUTPUT_VERBOSE;
  556. g_DbgClient->SetOutputMask(OutMask);
  557. g_DbgControl->SetLogMask(OutMask);
  558. CheckMoreArgs = TRUE;
  559. break;
  560. case 'y':
  561. g_DbgSymbols->SetSymbolPath(GetArg());
  562. break;
  563. case 'z':
  564. if (g_RemoteClient)
  565. {
  566. goto BadSwitch;
  567. }
  568. if (Arg[1] == 'p')
  569. {
  570. g_DumpPageFile = GetArg();
  571. }
  572. else if (Arg[1])
  573. {
  574. goto BadSwitch;
  575. }
  576. else
  577. {
  578. g_DumpFile = GetArg();
  579. }
  580. break;
  581. #ifndef KERNEL
  582. case '2':
  583. if (g_RemoteClient)
  584. {
  585. goto BadSwitch;
  586. }
  587. g_CreateFlags |= CREATE_NEW_CONSOLE;
  588. break;
  589. case '-':
  590. if (g_RemoteClient)
  591. {
  592. goto BadSwitch;
  593. }
  594. // '--' is the equivalent of -G -g -o -p -1 -netsyms:no -d -pd
  595. if (g_PidToDebug || g_ProcNameToDebug != NULL)
  596. {
  597. ErrorExit("%s: attach process redefined\n", g_DebuggerName);
  598. }
  599. g_CreateFlags |= DEBUG_PROCESS;
  600. g_CreateFlags &= ~DEBUG_ONLY_THIS_PROCESS;
  601. g_DbgSymbols->AddSymbolOptions(SYMOPT_DEFERRED_LOADS);
  602. g_DbgControl->RemoveEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK |
  603. DEBUG_ENGOPT_FINAL_BREAK);
  604. g_IoRequested = IO_DEBUG;
  605. g_PidToDebug = CSRSS_PROCESS_ID;
  606. g_ProcNameToDebug = NULL;
  607. g_DetachOnExitImplied = TRUE;
  608. break;
  609. case 'd':
  610. if (g_RemoteOptions != NULL)
  611. {
  612. ErrorExit("%s: Cannot use -d with debugger remoting\n",
  613. g_DebuggerName);
  614. }
  615. g_IoRequested = IO_DEBUG;
  616. CheckMoreArgs = TRUE;
  617. break;
  618. case 'e':
  619. //
  620. // undocumented
  621. //
  622. if (g_RemoteClient)
  623. {
  624. goto BadSwitch;
  625. }
  626. if (g_EventToSignal)
  627. {
  628. ErrorExit("%s: Event to signal redefined\n", g_DebuggerName);
  629. }
  630. // event to signal takes decimal argument
  631. Arg = GetArg();
  632. sscanf(Arg, "%I64d", &g_EventToSignal);
  633. if (!g_EventToSignal)
  634. {
  635. ErrorExit("%s: bad EventToSignal '%s'\n",
  636. g_DebuggerName, Arg);
  637. }
  638. g_DbgControl->SetNotifyEventHandle(g_EventToSignal);
  639. break;
  640. case 'h':
  641. if (Arg[1] == 'd')
  642. {
  643. g_CreateFlags |= DEBUG_CREATE_PROCESS_NO_DEBUG_HEAP;
  644. }
  645. else
  646. {
  647. goto BadSwitch;
  648. }
  649. break;
  650. case 'o':
  651. if (g_RemoteClient)
  652. {
  653. goto BadSwitch;
  654. }
  655. g_CreateFlags |= DEBUG_PROCESS;
  656. g_CreateFlags &= ~DEBUG_ONLY_THIS_PROCESS;
  657. CheckMoreArgs = TRUE;
  658. break;
  659. case 'p':
  660. if (g_RemoteClient)
  661. {
  662. goto BadSwitch;
  663. }
  664. if (!_stricmp(Arg, "premote"))
  665. {
  666. g_ProcessServer = GetArg();
  667. break;
  668. }
  669. else if (Arg[1] == 'd')
  670. {
  671. g_DetachOnExitRequired = TRUE;
  672. break;
  673. }
  674. else if (Arg[1] == 'e')
  675. {
  676. g_AttachProcessFlags = DEBUG_ATTACH_EXISTING;
  677. break;
  678. }
  679. else if (Arg[1] == 't')
  680. {
  681. g_DbgControl->SetInterruptTimeout(atoi(GetArg()));
  682. break;
  683. }
  684. else if (Arg[1] == 'v')
  685. {
  686. g_AttachProcessFlags = DEBUG_ATTACH_NONINVASIVE;
  687. break;
  688. }
  689. if (g_PidToDebug || g_ProcNameToDebug != NULL)
  690. {
  691. ErrorExit("%s: attach process redefined\n", g_DebuggerName);
  692. }
  693. if (Arg[1] == 'n')
  694. {
  695. // Process name.
  696. g_ProcNameToDebug = GetArg();
  697. g_PidToDebug = 0;
  698. }
  699. else
  700. {
  701. // pid debug takes decimal argument
  702. g_ProcNameToDebug = NULL;
  703. Arg = GetArg();
  704. if (Arg[0] == '-' && Arg[1] == '1' && Arg[2] == 0)
  705. {
  706. g_IoRequested = IO_DEBUG;
  707. g_PidToDebug = CSRSS_PROCESS_ID;
  708. }
  709. else
  710. {
  711. PSTR End;
  712. if (Arg[0] == '0' &&
  713. (Arg[1] == 'x' || Arg[1] == 'X'))
  714. {
  715. g_PidToDebug = strtoul(Arg, &End, 0);
  716. }
  717. else
  718. {
  719. g_PidToDebug = strtoul(Arg, &End, 10);
  720. }
  721. }
  722. if (!g_PidToDebug)
  723. {
  724. ErrorExit("%s: bad pid '%s'\n", g_DebuggerName, Arg);
  725. }
  726. }
  727. break;
  728. case 'r':
  729. if (!_stricmp(Arg, "robp"))
  730. {
  731. g_DbgControl->
  732. AddEngineOptions(DEBUG_ENGOPT_ALLOW_READ_ONLY_BREAKPOINTS);
  733. break;
  734. }
  735. else if (Arg[1] != 0)
  736. {
  737. goto BadSwitch;
  738. }
  739. // Rip flags takes single-char decimal argument
  740. Arg = GetArg();
  741. SystemErrorBreak = strtoul(Arg, &Arg, 10);
  742. if (SystemErrorBreak > 3)
  743. {
  744. ErrorExit("%s: bad Rip level '%ld'\n",
  745. g_DebuggerName, SystemErrorBreak);
  746. SystemErrorBreak = 0;
  747. }
  748. else
  749. {
  750. SystemErrorOutput = SystemErrorBreak;
  751. }
  752. g_DbgControl->SetSystemErrorControl(SystemErrorOutput,
  753. SystemErrorBreak);
  754. break;
  755. case 't':
  756. // Rip flags takes single-char decimal argument
  757. Arg = GetArg();
  758. SystemErrorOutput = strtoul(Arg, &Arg, 10);
  759. if (SystemErrorOutput > 3)
  760. {
  761. ErrorExit("%s: bad Rip level '%ld'\n",
  762. g_DebuggerName, SystemErrorOutput);
  763. SystemErrorOutput = 0;
  764. }
  765. g_DbgControl->SetSystemErrorControl(SystemErrorOutput,
  766. SystemErrorBreak);
  767. break;
  768. case 'x':
  769. if (Arg[1] == 0)
  770. {
  771. g_DbgControl->Execute(DEBUG_OUTCTL_IGNORE, "sxd av",
  772. DEBUG_EXECUTE_NOT_LOGGED);
  773. }
  774. else
  775. {
  776. // Turn "-x. arg" into "sx. arg" and execute
  777. // it to update the engine state.
  778. ExecuteCmd("sx", Arg[1], ' ', GetArg());
  779. }
  780. break;
  781. case 'w':
  782. if (!_stricmp(Arg, "wake"))
  783. {
  784. ULONG Pid = strtoul(GetArg(), &Arg, 10);
  785. if (!SetPidEvent(Pid, OPEN_EXISTING))
  786. {
  787. ErrorExit("Process %d is not a sleeping debugger\n", Pid);
  788. }
  789. else
  790. {
  791. ExitDebugger(0);
  792. }
  793. }
  794. if (g_RemoteClient)
  795. {
  796. goto BadSwitch;
  797. }
  798. g_CreateFlags |= CREATE_SEPARATE_WOW_VDM;
  799. CheckMoreArgs = TRUE;
  800. break;
  801. #else // #ifndef KERNEL
  802. case 'b':
  803. g_DbgControl->AddEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK);
  804. if (g_RemoteClient)
  805. {
  806. // The engine may already be waiting so just ask
  807. // for a breakin immediately.
  808. g_DbgControl->SetInterrupt(DEBUG_INTERRUPT_ACTIVE);
  809. }
  810. CheckMoreArgs = TRUE;
  811. break;
  812. case 'd':
  813. g_DbgControl->AddEngineOptions(DEBUG_ENGOPT_INITIAL_MODULE_BREAK);
  814. CheckMoreArgs = TRUE;
  815. break;
  816. case 'k':
  817. if (tolower(Arg[1]) == 'l')
  818. {
  819. g_AttachKernelFlags = DEBUG_ATTACH_LOCAL_KERNEL;
  820. }
  821. else if (tolower(Arg[1]) == 'x')
  822. {
  823. g_AttachKernelFlags = DEBUG_ATTACH_EXDI_DRIVER;
  824. g_ConnectOptions = GetArg();
  825. }
  826. else
  827. {
  828. g_ConnectOptions = GetArg();
  829. }
  830. break;
  831. case 'p':
  832. goto BadSwitch;
  833. case 'r':
  834. OutMask ^= DEBUG_OUTPUT_PROMPT_REGISTERS;
  835. g_DbgClient->SetOutputMask(OutMask);
  836. g_DbgControl->SetLogMask(OutMask);
  837. CheckMoreArgs = TRUE;
  838. break;
  839. case 'w':
  840. if (!_stricmp(Arg, "wake"))
  841. {
  842. ULONG Pid = strtoul(GetArg(), &Arg, 10);
  843. if (!SetPidEvent(Pid, OPEN_EXISTING))
  844. {
  845. ErrorExit("Process %d is not a sleeping debugger\n", Pid);
  846. }
  847. else
  848. {
  849. ExitDebugger(0);
  850. }
  851. }
  852. goto BadSwitch;
  853. case 'x':
  854. g_DbgControl->AddEngineOptions(DEBUG_ENGOPT_INITIAL_BREAK);
  855. g_InitialCommand = "eb nt!NtGlobalFlag 9;g";
  856. CheckMoreArgs = TRUE;
  857. break;
  858. #endif // #ifndef KERNEL
  859. default:
  860. BadSwitch:
  861. ConOut("%s: Invalid switch '%c'\n", g_DebuggerName, Arg[0]);
  862. ShowUsage = TRUE;
  863. break;
  864. }
  865. }
  866. #ifndef KERNEL
  867. if (g_RemoteClient)
  868. {
  869. if (g_Argc > 0)
  870. {
  871. ShowUsage = TRUE;
  872. }
  873. }
  874. else if (g_Argc > 0)
  875. {
  876. // Assume remaining arguments are a process execution
  877. // command line.
  878. g_CommandLine = g_CmdStr;
  879. }
  880. else if ((g_PidToDebug == 0) && (g_ProcNameToDebug == NULL) &&
  881. (g_DumpFile == NULL))
  882. {
  883. // User-mode debuggers require a dump file,
  884. // process attachment or created process.
  885. ShowUsage = TRUE;
  886. }
  887. #else
  888. if (g_Argc > 0)
  889. {
  890. // Kernel debuggers can't start user-mode processes.
  891. ShowUsage = TRUE;
  892. }
  893. #endif
  894. if (ShowUsage)
  895. {
  896. Usage();
  897. ErrorExit(NULL);
  898. }
  899. }
  900. int
  901. __cdecl
  902. main (
  903. int Argc,
  904. PCHAR* Argv
  905. )
  906. {
  907. HRESULT Hr;
  908. MakeHelpFileName("debugger.chm");
  909. ParseCommandLine(Argc, Argv, GetCommandLine());
  910. InitializeIo(g_InitialInputFile);
  911. #ifndef KERNEL
  912. if (g_DumpFile == NULL)
  913. {
  914. // Increase the priority for live debugging so
  915. // that the debugger is responsive for break-in.
  916. SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
  917. }
  918. #endif
  919. g_IoMode = g_IoRequested;
  920. switch(g_IoMode)
  921. {
  922. case IO_DEBUG:
  923. if (g_DbgClient2 != NULL)
  924. {
  925. if (g_DbgClient2->IsKernelDebuggerEnabled() != S_OK)
  926. {
  927. Usage();
  928. ErrorExit(NULL);
  929. }
  930. }
  931. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
  932. break;
  933. case IO_CONSOLE:
  934. CreateConsole();
  935. break;
  936. }
  937. // XXX drewb - Win9x doesn't support named pipes so
  938. // the separate input thread currently can't be used.
  939. // This makes remoting work very poorly and so should
  940. // be fixed by creating a simple internal pipe implementation.
  941. if (g_PlatformId == VER_PLATFORM_WIN32_NT)
  942. {
  943. if (g_IoMode != IO_NONE)
  944. {
  945. // Don't bother creating a separate thread for non-remoted
  946. // ntsd and cdb. This avoids problems with .remote
  947. // and multiple threads reading the console.
  948. #ifndef KERNEL
  949. if (g_RemoteOptions != NULL)
  950. #endif
  951. {
  952. CreateInputThread();
  953. }
  954. }
  955. }
  956. else if (g_RemoteOptions != NULL)
  957. {
  958. ErrorExit("Remoting is not currently supported on Win9x\n");
  959. }
  960. if (!g_RemoteClient)
  961. {
  962. if (g_RemoteOptions)
  963. {
  964. ConOut("Server started with '%s'\n", g_RemoteOptions);
  965. }
  966. InitializeSession();
  967. }
  968. else
  969. {
  970. ConOut("Connected to server with '%s'\n", g_RemoteOptions);
  971. // Use a heuristic of 45 characters per line.
  972. g_DbgClient->ConnectSession(DEBUG_CONNECT_SESSION_DEFAULT,
  973. g_HistoryLines * 45);
  974. }
  975. ULONG Code = S_OK;
  976. if (MainLoop())
  977. {
  978. // The session ended so return the exit code of the
  979. // last process that exited.
  980. Code = g_LastProcessExitCode;
  981. }
  982. ExitDebugger(Code);
  983. return Code;
  984. }