VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGConsole.cpp@ 86651

Last change on this file since 86651 was 86327, checked in by vboxsync, 4 years ago

Debugger: Allow for different I/O providers instead of only TCP

So far TCP was the only option to communicate remotely with the internal debugger, the other option
was to use the console from the GUI directly. This commit reworks basic I/O to allow for different
providers where TCP is just one option. The second one being introduced is an IPC provider using a local
socket or named pipe depending on the platform. This allows for Windows kernel debugging over a pipe
using the KD stub in VirtualBox and WinDbg running on the host (not tested yet).

Furthermore this commit allows multiple stubs to be listening for connections at the same time, so
one can have a GDB stub listening on one TCP port and the native VBox debugger listening on another one
or even using a different I/O provider. Only one session can be active at a time though, because sharing
debugger states is impossible. To configure this the following CFGM keys need to be set for each listener:

"DBGC/<Some unique ID>/Provider" "tcp|ipc"
"DBGC/<Some unique ID>/StubType" "native|gdb|kd"
"DBGC/<Some unique ID>/Address" "<ip>|<local named pipe or socket path>"
"DBGC/<Some unique ID>/Port" "<port>" (for TCP only)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.5 KB
Line 
1/* $Id: DBGConsole.cpp 86327 2020-09-28 16:20:50Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/** @page pg_dbgc DBGC - The Debug Console
20 *
21 * The debugger console is an early attempt to make some interactive
22 * debugging facilities for the VirtualBox VMM. It was initially only
23 * accessible thru a telnet session in debug builds. Later it was hastily built
24 * into the VBoxDbg module with a very simple Qt wrapper around it.
25 *
26 * The current state is that it's by default shipped with all standard
27 * VirtualBox builds. The GUI component is by default accessible in all
28 * non-release builds, while release builds require extra data, environment or
29 * command line options to make it visible.
30 *
31 * Now, even if we ship it with all standard builds we would like it to remain
32 * an optional feature that can be omitted when building VirtualBox. Therefore,
33 * all external code interfacing DBGC need to be enclosed in
34 * \#ifdef VBOX_WITH_DEBUGGER blocks. This is mandatory for components that
35 * register external commands.
36 *
37 *
38 * @section sec_dbgc_op Operation
39 *
40 * The console will process commands in a manner similar to the OS/2 and Windows
41 * kernel debuggers. This means ';' is a command separator and that when
42 * possible we'll use the same command names as these two uses. As an
43 * alternative we intent to provide a set of gdb-like commands as well and let
44 * the user decide which should take precedence.
45 *
46 *
47 * @subsection sec_dbg_op_numbers Numbers
48 *
49 * Numbers are hexadecimal unless specified with a prefix indicating
50 * elsewise. Prefixes:
51 * - '0x' - hexadecimal.
52 * - '0n' - decimal
53 * - '0t' - octal.
54 * - '0y' - binary.
55 *
56 * Some of the prefixes are a bit uncommon, the reason for this that the
57 * typical binary prefix '0b' can also be a hexadecimal value since no prefix or
58 * suffix is required for such values. Ditto for '0n' and '0' for decimal and
59 * octal.
60 *
61 * The '`' can be used in the numeric value to separate parts as the user
62 * wishes. Generally, though the debugger may use it in output as thousand
63 * separator in decimal numbers and 32-bit separator in hex numbers.
64 *
65 * For historical reasons, a 'h' suffix is suffered on hex numbers. Unlike most
66 * assemblers, a leading 0 before a-f is not required with the 'h' suffix.
67 *
68 * The prefix '0i' can be used instead of '0n', as it was the early decimal
69 * prefix employed by DBGC. It's being deprecated and may be removed later.
70 *
71 *
72 * @subsection sec_dbg_op_strings Strings and Symbols
73 *
74 * The debugger will try to guess, convert or promote what the type of an
75 * argument to a command, function or operator based on the input description of
76 * the receiver. If the user wants to make it clear to the debugger that
77 * something is a string, put it inside double quotes. Symbols should use
78 * single quotes, though we're current still a bit flexible on this point.
79 *
80 * If you need to put a quote character inside the quoted text, you escape it by
81 * repating it once: echo "printf(""hello world"");"
82 *
83 *
84 * @subsection sec_dbg_op_address Addressing modes
85 *
86 * - Default is flat. For compatibility '%' also means flat.
87 * - Segmented addresses are specified selector:offset.
88 * - Physical addresses are specified using '%%'.
89 * - The default target for the addressing is the guest context, the '#'
90 * will override this and set it to the host.
91 * Note that several operations won't work on host addresses.
92 *
93 * The '%', '%%' and '#' prefixes is implemented as unary operators, while ':'
94 * is a binary operator. Operator precedence takes care of evaluation order.
95 *
96 *
97 * @subsection sec_dbg_op_c_operators C/C++ Operators
98 *
99 * Most unary and binary arithmetic, comparison, logical and bitwise C/C++
100 * operators are supported by the debugger, with the same precedence rules of
101 * course. There is one notable change made due to the unary '%' and '%%'
102 * operators, and that is that the modulo (remainder) operator is called 'mod'
103 * instead of '%'. This saves a lot of trouble separating argument.
104 *
105 * There are no assignment operators. Instead some simple global variable space
106 * is provided thru the 'set' and 'unset' commands and the unary '$' operator.
107 *
108 *
109 * @subsection sec_dbg_op_registers Registers
110 *
111 * All registers and their sub-fields exposed by the DBGF API are accessible via
112 * the '\@' operator. A few CPU register are accessible directly (as symbols)
113 * without using the '\@' operator. Hypervisor registers are accessible by
114 * prefixing the register name with a dot ('.').
115 *
116 *
117 * @subsection sec_dbg_op_commands Commands
118 *
119 * Commands names are case sensitive. By convention they are lower cased, starts
120 * with a letter but may contain digits and underscores afterwards. Operators
121 * are not allowed in the name (not even part of it), as we would risk
122 * misunderstanding it otherwise.
123 *
124 * Commands returns a status code.
125 *
126 * The '.' prefix indicates the set of external commands. External commands are
127 * command registered by VMM components.
128 *
129 *
130 * @subsection sec_dbg_op_functions Functions
131 *
132 * Functions are similar to commands, but return a variable and can only be used
133 * as part of an expression making up the argument of a command, function,
134 * operator or language statement (if we get around to implement that).
135 *
136 *
137 * @section sec_dbgc_logging Logging
138 *
139 * The idea is to be able to pass thru debug and release logs to the console
140 * if the user so wishes. This feature requires some kind of hook into the
141 * logger instance and while this was sketched it hasn't yet been implemented
142 * (dbgcProcessLog and DBGC::fLog).
143 *
144 * This feature has not materialized and probably never will.
145 *
146 *
147 * @section sec_dbgc_linking Linking and API
148 *
149 * The DBGC code is linked into the VBoxVMM module.
150 *
151 * IMachineDebugger may one day be extended with a DBGC interface so we can work
152 * with DBGC remotely without requiring TCP. Some questions about callbacks
153 * (for output) and security (you may wish to restrict users from debugging a
154 * VM) needs to be answered first though.
155 */
156
157
158/*********************************************************************************************************************************
159* Header Files *
160*********************************************************************************************************************************/
161#define LOG_GROUP LOG_GROUP_DBGC
162#include <VBox/dbg.h>
163#include <VBox/vmm/cfgm.h>
164#include <VBox/vmm/dbgf.h>
165#include <VBox/vmm/vmapi.h> /* VMR3GetVM() */
166#include <VBox/vmm/hm.h> /* HMR3IsEnabled */
167#include <VBox/vmm/nem.h> /* NEMR3IsEnabled */
168#include <VBox/err.h>
169#include <VBox/log.h>
170
171#include <iprt/asm.h>
172#include <iprt/assert.h>
173#include <iprt/file.h>
174#include <iprt/mem.h>
175#include <iprt/path.h>
176#include <iprt/string.h>
177
178#include "DBGCInternal.h"
179#include "DBGPlugIns.h"
180
181
182/*********************************************************************************************************************************
183* Internal Functions *
184*********************************************************************************************************************************/
185static int dbgcProcessLog(PDBGC pDbgc);
186
187
188/**
189 * Resolves a symbol (or tries to do so at least).
190 *
191 * @returns 0 on success.
192 * @returns VBox status on failure.
193 * @param pDbgc The debug console instance.
194 * @param pszSymbol The symbol name.
195 * @param enmType The result type. Specifying DBGCVAR_TYPE_GC_FAR may
196 * cause failure, avoid it.
197 * @param pResult Where to store the result.
198 */
199int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
200{
201 int rc;
202
203 /*
204 * Builtin?
205 */
206 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
207 if (pSymDesc)
208 {
209 if (!pSymDesc->pfnGet)
210 return VERR_DBGC_PARSE_WRITEONLY_SYMBOL;
211 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
212 }
213
214 /*
215 * A typical register? (Guest only)
216 */
217 static const char s_szSixLetterRegisters[] =
218 "rflags;eflags;"
219 ;
220 static const char s_szThreeLetterRegisters[] =
221 "eax;rax;" "r10;" "r8d;r8w;r8b;" "cr0;" "dr0;"
222 "ebx;rbx;" "r11;" "r9d;r9w;r8b;" "dr1;"
223 "ecx;rcx;" "r12;" "cr2;" "dr2;"
224 "edx;rdx;" "r13;" "cr3;" "dr3;"
225 "edi;rdi;dil;" "r14;" "cr4;" "dr4;"
226 "esi;rsi;sil;" "r15;" "cr8;"
227 "ebp;rbp;"
228 "esp;rsp;" "dr6;"
229 "rip;eip;" "dr7;"
230 "efl;"
231 ;
232 static const char s_szTwoLetterRegisters[] =
233 "ax;al;ah;" "r8;"
234 "bx;bl;bh;" "r9;"
235 "cx;cl;ch;" "cs;"
236 "dx;dl;dh;" "ds;"
237 "di;" "es;"
238 "si;" "fs;"
239 "bp;" "gs;"
240 "sp;" "ss;"
241 "ip;"
242 ;
243 const char *pszRegSym = *pszSymbol == '.' ? pszSymbol + 1 : pszSymbol;
244 size_t const cchRegSym = strlen(pszRegSym);
245 if ( (cchRegSym == 2 && strstr(s_szTwoLetterRegisters, pszRegSym))
246 || (cchRegSym == 3 && strstr(s_szThreeLetterRegisters, pszRegSym))
247 || (cchRegSym == 6 && strstr(s_szSixLetterRegisters, pszRegSym)))
248 {
249 if (!strchr(pszSymbol, ';'))
250 {
251 DBGCVAR Var;
252 DBGCVAR_INIT_SYMBOL(&Var, pszSymbol);
253 rc = dbgcOpRegister(pDbgc, &Var, DBGCVAR_CAT_ANY, pResult);
254 if (RT_SUCCESS(rc))
255 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
256 }
257 }
258
259 /*
260 * Ask PDM.
261 */
262 /** @todo resolve symbols using PDM. */
263
264 /*
265 * Ask the debug info manager.
266 */
267 RTDBGSYMBOL Symbol;
268 rc = DBGFR3AsSymbolByName(pDbgc->pUVM, pDbgc->hDbgAs, pszSymbol, &Symbol, NULL);
269 if (RT_SUCCESS(rc))
270 {
271 /*
272 * Default return is a flat gc address.
273 */
274 DBGCVAR_INIT_GC_FLAT(pResult, Symbol.Value);
275 if (Symbol.cb)
276 DBGCVAR_SET_RANGE(pResult, DBGCVAR_RANGE_BYTES, Symbol.cb);
277
278 switch (enmType)
279 {
280 /* nothing to do. */
281 case DBGCVAR_TYPE_GC_FLAT:
282 case DBGCVAR_TYPE_ANY:
283 return VINF_SUCCESS;
284
285 /* impossible at the moment. */
286 case DBGCVAR_TYPE_GC_FAR:
287 return VERR_DBGC_PARSE_CONVERSION_FAILED;
288
289 /* simply make it numeric. */
290 case DBGCVAR_TYPE_NUMBER:
291 pResult->enmType = DBGCVAR_TYPE_NUMBER;
292 pResult->u.u64Number = Symbol.Value;
293 return VINF_SUCCESS;
294
295 /* cast it. */
296 case DBGCVAR_TYPE_GC_PHYS:
297 case DBGCVAR_TYPE_HC_FLAT:
298 case DBGCVAR_TYPE_HC_PHYS:
299 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
300
301 default:
302 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
303 return VERR_INVALID_PARAMETER;
304 }
305 }
306
307 return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
308}
309
310
311/**
312 * Process all commands currently in the buffer.
313 *
314 * @returns VBox status code. Any error indicates the termination of the console session.
315 * @param pDbgc Debugger console instance data.
316 * @param fNoExecute Indicates that no commands should actually be executed.
317 */
318static int dbgcProcessCommands(PDBGC pDbgc, bool fNoExecute)
319{
320 /** @todo Replace this with a sh/ksh/csh/rexx like toplevel language that
321 * allows doing function, loops, if, cases, and such. */
322 int rc = VINF_SUCCESS;
323 while (pDbgc->cInputLines)
324 {
325 /*
326 * Empty the log buffer if we're hooking the log.
327 */
328 if (pDbgc->fLog)
329 {
330 rc = dbgcProcessLog(pDbgc);
331 if (RT_FAILURE(rc))
332 break;
333 }
334
335 if (pDbgc->iRead == pDbgc->iWrite)
336 {
337 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));
338 pDbgc->cInputLines = 0;
339 return 0;
340 }
341
342 /*
343 * Copy the command to the parse buffer.
344 */
345 char ch;
346 char *psz = &pDbgc->achInput[pDbgc->iRead];
347 char *pszTrg = &pDbgc->achScratch[0];
348 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
349 {
350 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
351 psz = &pDbgc->achInput[0];
352
353 if (psz == &pDbgc->achInput[pDbgc->iWrite])
354 {
355 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
356 pDbgc->cInputLines = 0;
357 return 0;
358 }
359
360 pszTrg++;
361 }
362 *pszTrg = '\0';
363
364 /*
365 * Advance the buffer.
366 */
367 pDbgc->iRead = psz - &pDbgc->achInput[0];
368 if (ch == '\n')
369 pDbgc->cInputLines--;
370
371 /*
372 * Parse and execute this command.
373 */
374 pDbgc->pszScratch = pszTrg + 1;
375 pDbgc->iArg = 0;
376 rc = dbgcEvalCommand(pDbgc, &pDbgc->achScratch[0], pszTrg - &pDbgc->achScratch[0] - 1, fNoExecute);
377 if ( rc == VERR_DBGC_QUIT
378 || rc == VWRN_DBGC_CMD_PENDING)
379 break;
380 rc = VINF_SUCCESS; /* ignore other statuses */
381 }
382
383 return rc;
384}
385
386
387/**
388 * Handle input buffer overflow.
389 *
390 * Will read any available input looking for a '\n' to reset the buffer on.
391 *
392 * @returns VBox status code.
393 * @param pDbgc Debugger console instance data.
394 */
395static int dbgcInputOverflow(PDBGC pDbgc)
396{
397 /*
398 * Assert overflow status and reset the input buffer.
399 */
400 if (!pDbgc->fInputOverflow)
401 {
402 pDbgc->fInputOverflow = true;
403 pDbgc->iRead = pDbgc->iWrite = 0;
404 pDbgc->cInputLines = 0;
405 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
406 }
407
408 /*
409 * Eat input till no more or there is a '\n'.
410 * When finding a '\n' we'll continue normal processing.
411 */
412 while (pDbgc->pIo->pfnInput(pDbgc->pIo, 0))
413 {
414 size_t cbRead;
415 int rc = pDbgc->pIo->pfnRead(pDbgc->pIo, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
416 if (RT_FAILURE(rc))
417 return rc;
418 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
419 if (psz)
420 {
421 pDbgc->fInputOverflow = false;
422 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
423 pDbgc->iWrite = (unsigned)cbRead;
424 pDbgc->cInputLines = 0;
425 break;
426 }
427 }
428
429 return 0;
430}
431
432
433/**
434 * Read input and do some preprocessing.
435 *
436 * @returns VBox status code.
437 * In addition to the iWrite and achInput, cInputLines is maintained.
438 * In case of an input overflow the fInputOverflow flag will be set.
439 * @param pDbgc Debugger console instance data.
440 */
441static int dbgcInputRead(PDBGC pDbgc)
442{
443 /*
444 * We have ready input.
445 * Read it till we don't have any or we have a full input buffer.
446 */
447 int rc = 0;
448 do
449 {
450 /*
451 * More available buffer space?
452 */
453 size_t cbLeft;
454 if (pDbgc->iWrite > pDbgc->iRead)
455 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
456 else
457 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
458 if (!cbLeft)
459 {
460 /* overflow? */
461 if (!pDbgc->cInputLines)
462 rc = dbgcInputOverflow(pDbgc);
463 break;
464 }
465
466 /*
467 * Read one char and interpret it.
468 */
469 char achRead[128];
470 size_t cbRead;
471 rc = pDbgc->pIo->pfnRead(pDbgc->pIo, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
472 if (RT_FAILURE(rc))
473 return rc;
474 char *psz = &achRead[0];
475 while (cbRead-- > 0)
476 {
477 char ch = *psz++;
478 switch (ch)
479 {
480 /*
481 * Ignore.
482 */
483 case '\0':
484 case '\r':
485 case '\a':
486 break;
487
488 /*
489 * Backspace.
490 */
491 case '\b':
492 Log2(("DBGC: backspace\n"));
493 if (pDbgc->iRead != pDbgc->iWrite)
494 {
495 unsigned iWriteUndo = pDbgc->iWrite;
496 if (pDbgc->iWrite)
497 pDbgc->iWrite--;
498 else
499 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
500
501 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
502 pDbgc->iWrite = iWriteUndo;
503 }
504 break;
505
506 /*
507 * Add char to buffer.
508 */
509 case '\t':
510 case '\n':
511 case ';':
512 switch (ch)
513 {
514 case '\t': ch = ' '; break;
515 case '\n': pDbgc->cInputLines++; break;
516 }
517 RT_FALL_THRU();
518 default:
519 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
520 pDbgc->achInput[pDbgc->iWrite] = ch;
521 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
522 pDbgc->iWrite = 0;
523 break;
524 }
525 }
526
527 /* Terminate it to make it easier to read in the debugger. */
528 pDbgc->achInput[pDbgc->iWrite] = '\0';
529 } while (pDbgc->pIo->pfnInput(pDbgc->pIo, 0));
530
531 return rc;
532}
533
534
535/**
536 * Reads input, parses it and executes commands on '\n'.
537 *
538 * @returns VBox status code.
539 * @param pDbgc Debugger console instance data.
540 * @param fNoExecute Indicates that no commands should actually be executed.
541 */
542int dbgcProcessInput(PDBGC pDbgc, bool fNoExecute)
543{
544 /*
545 * We know there's input ready, so let's read it first.
546 */
547 int rc = dbgcInputRead(pDbgc);
548 if (RT_FAILURE(rc))
549 return rc;
550
551 /*
552 * Now execute any ready commands.
553 */
554 if (pDbgc->cInputLines)
555 {
556 pDbgc->pIo->pfnSetReady(pDbgc->pIo, false);
557 pDbgc->fReady = false;
558 rc = dbgcProcessCommands(pDbgc, fNoExecute);
559 if (RT_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
560 pDbgc->fReady = true;
561
562 if ( RT_SUCCESS(rc)
563 && pDbgc->iRead == pDbgc->iWrite
564 && pDbgc->fReady)
565 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
566
567 if ( RT_SUCCESS(rc)
568 && pDbgc->fReady)
569 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
570 }
571 /*
572 * else - we have incomplete line, so leave it in the buffer and
573 * wait for more input.
574 *
575 * Windows telnet client is in "character at a time" mode by
576 * default and putty sends eol as a separate packet that will be
577 * most likely read separately from the command line it
578 * terminates.
579 */
580
581 return rc;
582}
583
584
585/**
586 * Gets the event context identifier string.
587 * @returns Read only string.
588 * @param enmCtx The context.
589 */
590DECLHIDDEN(const char *) dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
591{
592 switch (enmCtx)
593 {
594 case DBGFEVENTCTX_RAW: return "raw";
595 case DBGFEVENTCTX_REM: return "rem";
596 case DBGFEVENTCTX_HM: return "hwaccl";
597 case DBGFEVENTCTX_HYPER: return "hyper";
598 case DBGFEVENTCTX_OTHER: return "other";
599
600 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
601 default:
602 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
603 return "!Unknown Event Ctx!";
604 }
605}
606
607
608/**
609 * Looks up a generic debug event.
610 *
611 * @returns Pointer to DBGCSXEVT structure if found, otherwise NULL.
612 * @param enmType The possibly generic event to find the descriptor for.
613 */
614DECLHIDDEN(PCDBGCSXEVT) dbgcEventLookup(DBGFEVENTTYPE enmType)
615{
616 uint32_t i = g_cDbgcSxEvents;
617 while (i-- > 0)
618 if (g_aDbgcSxEvents[i].enmType == enmType)
619 return &g_aDbgcSxEvents[i];
620 return NULL;
621}
622
623
624/**
625 * Processes debugger events.
626 *
627 * @returns VBox status code.
628 * @param pDbgc DBGC Instance data.
629 * @param pEvent Pointer to event data.
630 */
631static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
632{
633 /*
634 * Flush log first.
635 */
636 if (pDbgc->fLog)
637 {
638 int rc = dbgcProcessLog(pDbgc);
639 if (RT_FAILURE(rc))
640 return rc;
641 }
642
643 /*
644 * Process the event.
645 */
646 pDbgc->pszScratch = &pDbgc->achInput[0];
647 pDbgc->iArg = 0;
648 bool fPrintPrompt = true;
649 int rc = VINF_SUCCESS;
650 VMCPUID const idCpuSaved = pDbgc->idCpu;
651 switch (pEvent->enmType)
652 {
653 /*
654 * The first part is events we have initiated with commands.
655 */
656 case DBGFEVENT_HALT_DONE:
657 {
658 /** @todo add option to suppress this on CPUs that aren't selected (like
659 * fRegTerse). */
660 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: CPU %u has halted! (%s)\n",
661 pEvent->idCpu, pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
662 if (RT_SUCCESS(rc))
663 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
664 break;
665 }
666
667
668 /*
669 * The second part is events which can occur at any time.
670 */
671 case DBGFEVENT_FATAL_ERROR:
672 {
673 pDbgc->idCpu = pEvent->idCpu;
674 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event/%u: Fatal error! (%s)\n",
675 pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
676 if (RT_SUCCESS(rc))
677 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
678 break;
679 }
680
681 case DBGFEVENT_BREAKPOINT:
682 case DBGFEVENT_BREAKPOINT_IO:
683 case DBGFEVENT_BREAKPOINT_MMIO:
684 case DBGFEVENT_BREAKPOINT_HYPER:
685 {
686 pDbgc->idCpu = pEvent->idCpu;
687 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
688 switch (rc)
689 {
690 case VERR_DBGC_BP_NOT_FOUND:
691 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Unknown breakpoint %u! (%s)\n",
692 pEvent->idCpu, pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
693 break;
694
695 case VINF_DBGC_BP_NO_COMMAND:
696 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Breakpoint %u! (%s)\n",
697 pEvent->idCpu, pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
698 break;
699
700 case VINF_BUFFER_OVERFLOW:
701 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Breakpoint %u! Command too long to execute! (%s)\n",
702 pEvent->idCpu, pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
703 break;
704
705 default:
706 break;
707 }
708 if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM, pEvent->idCpu))
709 {
710 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
711
712 /* Set the resume flag to ignore the breakpoint when resuming execution. */
713 if ( RT_SUCCESS(rc)
714 && pEvent->enmType == DBGFEVENT_BREAKPOINT)
715 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r eflags.rf = 1");
716 }
717 else
718 pDbgc->idCpu = idCpuSaved;
719 break;
720 }
721
722 case DBGFEVENT_STEPPED:
723 case DBGFEVENT_STEPPED_HYPER:
724 {
725 if (!pDbgc->cMultiStepsLeft || pEvent->idCpu != idCpuSaved)
726 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Single step! (%s)\n",
727 pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
728 else
729 pDbgc->cMultiStepsLeft -= 1;
730 if (RT_SUCCESS(rc))
731 {
732 if (pDbgc->fStepTraceRegs)
733 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
734 else
735 {
736 char szCmd[80];
737 if (DBGFR3CpuIsIn64BitCode(pDbgc->pUVM, pDbgc->idCpu))
738 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "u %016VR{rip} L 0");
739 else if (DBGFR3CpuIsInV86Code(pDbgc->pUVM, pDbgc->idCpu))
740 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "uv86 %04VR{cs}:%08VR{eip} L 0");
741 else
742 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "u %04VR{cs}:%08VR{eip} L 0");
743 if (RT_SUCCESS(rc))
744 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "%s", szCmd);
745 }
746 }
747
748 /* If multi-stepping, take the next step: */
749 if (pDbgc->cMultiStepsLeft > 0 && pEvent->idCpu != idCpuSaved)
750 {
751 int rc2 = DBGFR3StepEx(pDbgc->pUVM, pDbgc->idCpu, DBGF_STEP_F_INTO, NULL, NULL, 0, pDbgc->uMultiStepStrideLength);
752 if (RT_SUCCESS(rc2))
753 fPrintPrompt = false;
754 else
755 DBGCCmdHlpFailRc(&pDbgc->CmdHlp, pDbgc->pMultiStepCmd, rc2, "DBGFR3StepEx(,,DBGF_STEP_F_INTO,) failed");
756 }
757 else
758 pDbgc->idCpu = pEvent->idCpu;
759 break;
760 }
761
762 case DBGFEVENT_ASSERTION_HYPER:
763 {
764 pDbgc->idCpu = pEvent->idCpu;
765 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
766 "\ndbgf event/%u: Hypervisor Assertion! (%s)\n"
767 "%s"
768 "%s"
769 "\n",
770 pEvent->idCpu,
771 dbgcGetEventCtx(pEvent->enmCtx),
772 pEvent->u.Assert.pszMsg1,
773 pEvent->u.Assert.pszMsg2);
774 if (RT_SUCCESS(rc))
775 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
776 break;
777 }
778
779 case DBGFEVENT_DEV_STOP:
780 {
781 pDbgc->idCpu = pEvent->idCpu;
782 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
783 "\n"
784 "dbgf event/%u: DBGFSTOP (%s)\n"
785 "File: %s\n"
786 "Line: %d\n"
787 "Function: %s\n",
788 pEvent->idCpu,
789 dbgcGetEventCtx(pEvent->enmCtx),
790 pEvent->u.Src.pszFile,
791 pEvent->u.Src.uLine,
792 pEvent->u.Src.pszFunction);
793 if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
794 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
795 "Message: %s\n",
796 pEvent->u.Src.pszMessage);
797 if (RT_SUCCESS(rc))
798 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
799 break;
800 }
801
802
803 case DBGFEVENT_INVALID_COMMAND:
804 {
805 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
806 break;
807 }
808
809 case DBGFEVENT_POWERING_OFF:
810 {
811 pDbgc->fReady = false;
812 pDbgc->pIo->pfnSetReady(pDbgc->pIo, false);
813 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is powering off!\n");
814 fPrintPrompt = false;
815 rc = VERR_GENERAL_FAILURE;
816 break;
817 }
818
819
820 default:
821 {
822 /*
823 * Probably a generic event. Look it up to find its name.
824 */
825 PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType);
826 if (pEvtDesc)
827 {
828 if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt)
829 {
830 Assert(pEvtDesc->pszDesc);
831 Assert(pEvent->u.Generic.cArgs == 1);
832 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s no %#llx! (%s)\n",
833 pEvent->idCpu, pEvtDesc->pszDesc, pEvent->u.Generic.auArgs[0], pEvtDesc->pszName);
834 }
835 else if (pEvtDesc->fFlags & DBGCSXEVT_F_BUGCHECK)
836 {
837 Assert(pEvent->u.Generic.cArgs >= 5);
838 char szDetails[512];
839 DBGFR3FormatBugCheck(pDbgc->pUVM, szDetails, sizeof(szDetails), pEvent->u.Generic.auArgs[0],
840 pEvent->u.Generic.auArgs[1], pEvent->u.Generic.auArgs[2],
841 pEvent->u.Generic.auArgs[3], pEvent->u.Generic.auArgs[4]);
842 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s %s%s!\n%s", pEvent->idCpu,
843 pEvtDesc->pszName, pEvtDesc->pszDesc ? "- " : "",
844 pEvtDesc->pszDesc ? pEvtDesc->pszDesc : "", szDetails);
845 }
846 else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG)
847 || pEvent->u.Generic.cArgs > 1
848 || ( pEvent->u.Generic.cArgs == 1
849 && pEvent->u.Generic.auArgs[0] != 0))
850 {
851 if (pEvtDesc->pszDesc)
852 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s - %s!",
853 pEvent->idCpu, pEvtDesc->pszName, pEvtDesc->pszDesc);
854 else
855 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s!",
856 pEvent->idCpu, pEvtDesc->pszName);
857 if (pEvent->u.Generic.cArgs <= 1)
858 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]);
859 else
860 {
861 for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++)
862 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]);
863 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
864 }
865 }
866 else
867 {
868 if (pEvtDesc->pszDesc)
869 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s - %s!\n",
870 pEvent->idCpu, pEvtDesc->pszName, pEvtDesc->pszDesc);
871 else
872 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s!\n",
873 pEvent->idCpu, pEvtDesc->pszName);
874 }
875 }
876 else
877 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d on CPU %u!\n",
878 pEvent->enmType, pEvent->idCpu);
879 break;
880 }
881 }
882
883 /*
884 * Prompt, anyone?
885 */
886 if (fPrintPrompt && RT_SUCCESS(rc))
887 {
888 /** @todo add CPU indicator to the prompt if an SMP VM? */
889 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
890 pDbgc->fReady = true;
891 if (RT_SUCCESS(rc))
892 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
893 pDbgc->cMultiStepsLeft = 0;
894 }
895
896 return rc;
897}
898
899
900/**
901 * Prints any log lines from the log buffer.
902 *
903 * The caller must not call function this unless pDbgc->fLog is set.
904 *
905 * @returns VBox status code. (output related)
906 * @param pDbgc Debugger console instance data.
907 */
908static int dbgcProcessLog(PDBGC pDbgc)
909{
910 /** @todo */
911 NOREF(pDbgc);
912 return 0;
913}
914
915/** @callback_method_impl{FNRTDBGCFGLOG} */
916static DECLCALLBACK(void) dbgcDbgCfgLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
917{
918 /** @todo Add symbol noise setting. */
919 NOREF(hDbgCfg); NOREF(iLevel);
920 PDBGC pDbgc = (PDBGC)pvUser;
921 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s", pszMsg);
922}
923
924
925/**
926 * Run the debugger console.
927 *
928 * @returns VBox status code.
929 * @param pDbgc Pointer to the debugger console instance data.
930 */
931int dbgcRun(PDBGC pDbgc)
932{
933 /*
934 * We're ready for commands now.
935 */
936 pDbgc->fReady = true;
937 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
938
939 /*
940 * Main Debugger Loop.
941 *
942 * This loop will either block on waiting for input or on waiting on
943 * debug events. If we're forwarding the log we cannot wait for long
944 * before we must flush the log.
945 */
946 int rc;
947 for (;;)
948 {
949 rc = VERR_SEM_OUT_OF_TURN;
950 if (pDbgc->pUVM)
951 rc = DBGFR3QueryWaitable(pDbgc->pUVM);
952
953 if (RT_SUCCESS(rc))
954 {
955 /*
956 * Wait for a debug event.
957 */
958 DBGFEVENT Event;
959 rc = DBGFR3EventWait(pDbgc->pUVM, pDbgc->fLog ? 1 : 32, &Event);
960 if (RT_SUCCESS(rc))
961 {
962 rc = dbgcProcessEvent(pDbgc, &Event);
963 if (RT_FAILURE(rc))
964 break;
965 }
966 else if (rc != VERR_TIMEOUT)
967 break;
968
969 /*
970 * Check for input.
971 */
972 if (pDbgc->pIo->pfnInput(pDbgc->pIo, 0))
973 {
974 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
975 if (RT_FAILURE(rc))
976 break;
977 }
978 }
979 else if (rc == VERR_SEM_OUT_OF_TURN)
980 {
981 /*
982 * Wait for input. If Logging is enabled we'll only wait very briefly.
983 */
984 if (pDbgc->pIo->pfnInput(pDbgc->pIo, pDbgc->fLog ? 1 : 1000))
985 {
986 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
987 if (RT_FAILURE(rc))
988 break;
989 }
990 }
991 else
992 break;
993
994 /*
995 * Forward log output.
996 */
997 if (pDbgc->fLog)
998 {
999 rc = dbgcProcessLog(pDbgc);
1000 if (RT_FAILURE(rc))
1001 break;
1002 }
1003 }
1004
1005 return rc;
1006}
1007
1008
1009/**
1010 * Run the init scripts, if present.
1011 *
1012 * @param pDbgc The console instance.
1013 */
1014static void dbgcRunInitScripts(PDBGC pDbgc)
1015{
1016 /*
1017 * Do the global one, if it exists.
1018 */
1019 if ( pDbgc->pszGlobalInitScript
1020 && *pDbgc->pszGlobalInitScript != '\0'
1021 && RTFileExists(pDbgc->pszGlobalInitScript))
1022 dbgcEvalScript(pDbgc, pDbgc->pszGlobalInitScript, true /*fAnnounce*/);
1023
1024 /*
1025 * Then do the local one, if it exists.
1026 */
1027 if ( pDbgc->pszLocalInitScript
1028 && *pDbgc->pszLocalInitScript != '\0'
1029 && RTFileExists(pDbgc->pszLocalInitScript))
1030 dbgcEvalScript(pDbgc, pDbgc->pszLocalInitScript, true /*fAnnounce*/);
1031}
1032
1033
1034/**
1035 * Reads the CFGM configuration of the DBGC.
1036 *
1037 * Popuplates the PDBGC::pszHistoryFile, PDBGC::pszGlobalInitScript and
1038 * PDBGC::pszLocalInitScript members.
1039 *
1040 * @returns VBox status code.
1041 * @param pDbgc The console instance.
1042 * @param pUVM The user mode VM handle.
1043 */
1044static int dbgcReadConfig(PDBGC pDbgc, PUVM pUVM)
1045{
1046 /*
1047 * Get and validate the configuration node.
1048 */
1049 PCFGMNODE pNode = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
1050 int rc = CFGMR3ValidateConfig(pNode, "/DBGC/",
1051 "Enabled|"
1052 "HistoryFile|"
1053 "LocalInitScript|"
1054 "GlobalInitScript|",
1055 "*", "DBGC", 0);
1056 AssertRCReturn(rc, rc);
1057
1058 /*
1059 * Query the values.
1060 */
1061 char szHomeDefault[RTPATH_MAX];
1062 rc = RTPathUserHome(szHomeDefault, sizeof(szHomeDefault) - 32);
1063 AssertLogRelRCReturn(rc, rc);
1064 size_t cchHome = strlen(szHomeDefault);
1065
1066 /** @cfgm{/DBGC/HistoryFile, string, ${HOME}/.vboxdbgc-history}
1067 * The command history file of the VBox debugger. */
1068 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-history");
1069 AssertLogRelRCReturn(rc, rc);
1070
1071 char szPath[RTPATH_MAX];
1072 rc = CFGMR3QueryStringDef(pNode, "HistoryFile", szPath, sizeof(szPath), szHomeDefault);
1073 AssertLogRelRCReturn(rc, rc);
1074
1075 pDbgc->pszHistoryFile = RTStrDup(szPath);
1076 AssertReturn(pDbgc->pszHistoryFile, VERR_NO_STR_MEMORY);
1077
1078 /** @cfgm{/DBGC/GlobalInitFile, string, ${HOME}/.vboxdbgc-init}
1079 * The global init script of the VBox debugger. */
1080 szHomeDefault[cchHome] = '\0';
1081 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-init");
1082 AssertLogRelRCReturn(rc, rc);
1083
1084 rc = CFGMR3QueryStringDef(pNode, "GlobalInitScript", szPath, sizeof(szPath), szHomeDefault);
1085 AssertLogRelRCReturn(rc, rc);
1086
1087 pDbgc->pszGlobalInitScript = RTStrDup(szPath);
1088 AssertReturn(pDbgc->pszGlobalInitScript, VERR_NO_STR_MEMORY);
1089
1090 /** @cfgm{/DBGC/LocalInitFile, string, none}
1091 * The VM local init script of the VBox debugger. */
1092 rc = CFGMR3QueryString(pNode, "LocalInitScript", szPath, sizeof(szPath));
1093 if (RT_SUCCESS(rc))
1094 {
1095 pDbgc->pszLocalInitScript = RTStrDup(szPath);
1096 AssertReturn(pDbgc->pszLocalInitScript, VERR_NO_STR_MEMORY);
1097 }
1098 else
1099 {
1100 AssertLogRelReturn(rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT, rc);
1101 pDbgc->pszLocalInitScript = NULL;
1102 }
1103
1104 return VINF_SUCCESS;
1105}
1106
1107
1108/**
1109 * @copydoc DBGC::pfnOutput
1110 */
1111static DECLCALLBACK(int) dbgcOutputNative(void *pvUser, const char *pachChars, size_t cbChars)
1112{
1113 PDBGC pDbgc = (PDBGC)pvUser;
1114 return pDbgc->pIo->pfnWrite(pDbgc->pIo, pachChars, cbChars, NULL /*pcbWritten*/);
1115}
1116
1117
1118/**
1119 * Creates a a new instance.
1120 *
1121 * @returns VBox status code.
1122 * @param ppDbgc Where to store the pointer to the instance data.
1123 * @param pIo Pointer to the I/O callback table.
1124 * @param fFlags The flags.
1125 */
1126int dbgcCreate(PDBGC *ppDbgc, PCDBGCIO pIo, unsigned fFlags)
1127{
1128 /*
1129 * Validate input.
1130 */
1131 AssertPtrReturn(pIo, VERR_INVALID_POINTER);
1132 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
1133
1134 /*
1135 * Allocate and initialize.
1136 */
1137 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
1138 if (!pDbgc)
1139 return VERR_NO_MEMORY;
1140
1141 dbgcInitCmdHlp(pDbgc);
1142 pDbgc->pIo = pIo;
1143 pDbgc->pfnOutput = dbgcOutputNative;
1144 pDbgc->pvOutputUser = pDbgc;
1145 pDbgc->pVM = NULL;
1146 pDbgc->pUVM = NULL;
1147 pDbgc->idCpu = 0;
1148 pDbgc->hDbgAs = DBGF_AS_GLOBAL;
1149 pDbgc->pszEmulation = "CodeView/WinDbg";
1150 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
1151 pDbgc->cEmulationCmds = g_cCmdsCodeView;
1152 pDbgc->paEmulationFuncs = &g_aFuncsCodeView[0];
1153 pDbgc->cEmulationFuncs = g_cFuncsCodeView;
1154 //pDbgc->fLog = false;
1155 pDbgc->fRegTerse = true;
1156 pDbgc->fStepTraceRegs = true;
1157 //pDbgc->cPagingHierarchyDumps = 0;
1158 //pDbgc->DisasmPos = {0};
1159 //pDbgc->SourcePos = {0};
1160 //pDbgc->DumpPos = {0};
1161 pDbgc->pLastPos = &pDbgc->DisasmPos;
1162 //pDbgc->cbDumpElement = 0;
1163 //pDbgc->cVars = 0;
1164 //pDbgc->paVars = NULL;
1165 //pDbgc->pPlugInHead = NULL;
1166 //pDbgc->pFirstBp = NULL;
1167 //pDbgc->abSearch = {0};
1168 //pDbgc->cbSearch = 0;
1169 pDbgc->cbSearchUnit = 1;
1170 pDbgc->cMaxSearchHits = 1;
1171 //pDbgc->SearchAddr = {0};
1172 //pDbgc->cbSearchRange = 0;
1173
1174 //pDbgc->uInputZero = 0;
1175 //pDbgc->iRead = 0;
1176 //pDbgc->iWrite = 0;
1177 //pDbgc->cInputLines = 0;
1178 //pDbgc->fInputOverflow = false;
1179 pDbgc->fReady = true;
1180 pDbgc->pszScratch = &pDbgc->achScratch[0];
1181 //pDbgc->iArg = 0;
1182 //pDbgc->rcOutput = 0;
1183 //pDbgc->rcCmd = 0;
1184
1185 //pDbgc->pszHistoryFile = NULL;
1186 //pDbgc->pszGlobalInitScript = NULL;
1187 //pDbgc->pszLocalInitScript = NULL;
1188
1189 dbgcEvalInit();
1190
1191 *ppDbgc = pDbgc;
1192 return VINF_SUCCESS;
1193}
1194
1195/**
1196 * Destroys a DBGC instance created by dbgcCreate.
1197 *
1198 * @param pDbgc Pointer to the debugger console instance data.
1199 */
1200void dbgcDestroy(PDBGC pDbgc)
1201{
1202 AssertPtr(pDbgc);
1203
1204 /* Disable log hook. */
1205 if (pDbgc->fLog)
1206 {
1207
1208 }
1209
1210 /* Detach from the VM. */
1211 if (pDbgc->pUVM)
1212 DBGFR3Detach(pDbgc->pUVM);
1213
1214 /* Free config strings. */
1215 RTStrFree(pDbgc->pszGlobalInitScript);
1216 pDbgc->pszGlobalInitScript = NULL;
1217 RTStrFree(pDbgc->pszLocalInitScript);
1218 pDbgc->pszLocalInitScript = NULL;
1219 RTStrFree(pDbgc->pszHistoryFile);
1220 pDbgc->pszHistoryFile = NULL;
1221
1222 /* Finally, free the instance memory. */
1223 RTMemFree(pDbgc);
1224}
1225
1226
1227/**
1228 * Make a console instance.
1229 *
1230 * This will not return until either an 'exit' command is issued or a error code
1231 * indicating connection loss is encountered.
1232 *
1233 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
1234 * @returns The VBox status code causing the console termination.
1235 *
1236 * @param pUVM The user mode VM handle.
1237 * @param pIo Pointer to the I/O callback structure. This must contain
1238 * a full set of function pointers to service the console.
1239 * @param fFlags Reserved, must be zero.
1240 * @remarks A forced termination of the console is easiest done by forcing the
1241 * callbacks to return fatal failures.
1242 */
1243DBGDECL(int) DBGCCreate(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags)
1244{
1245 /*
1246 * Validate input.
1247 */
1248 AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
1249 PVM pVM = NULL;
1250 if (pUVM)
1251 {
1252 pVM = VMR3GetVM(pUVM);
1253 AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
1254 }
1255
1256 /*
1257 * Allocate and initialize instance data
1258 */
1259 PDBGC pDbgc;
1260 int rc = dbgcCreate(&pDbgc, pIo, fFlags);
1261 if (RT_FAILURE(rc))
1262 return rc;
1263 if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM))
1264 pDbgc->hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
1265
1266 /*
1267 * Print welcome message.
1268 */
1269 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1270 "Welcome to the VirtualBox Debugger!\n");
1271
1272 /*
1273 * Attach to the specified VM.
1274 */
1275 if (RT_SUCCESS(rc) && pUVM)
1276 {
1277 rc = dbgcReadConfig(pDbgc, pUVM);
1278 if (RT_SUCCESS(rc))
1279 {
1280 rc = DBGFR3Attach(pUVM);
1281 if (RT_SUCCESS(rc))
1282 {
1283 pDbgc->pVM = pVM;
1284 pDbgc->pUVM = pUVM;
1285 pDbgc->idCpu = 0;
1286 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1287 "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
1288 , pDbgc->pVM, pDbgc->idCpu);
1289 }
1290 else
1291 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
1292 }
1293 else
1294 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "Error reading configuration\n");
1295 }
1296
1297 /*
1298 * Load plugins.
1299 */
1300 if (RT_SUCCESS(rc))
1301 {
1302 if (pVM)
1303 DBGFR3PlugInLoadAll(pDbgc->pUVM);
1304 dbgcEventInit(pDbgc);
1305 dbgcRunInitScripts(pDbgc);
1306
1307 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
1308 if (RT_SUCCESS(rc))
1309 {
1310 /*
1311 * Set debug config log callback.
1312 */
1313 RTDBGCFG hDbgCfg = DBGFR3AsGetConfig(pUVM);
1314 if ( hDbgCfg != NIL_RTDBGCFG
1315 && RTDbgCfgRetain(hDbgCfg) != UINT32_MAX)
1316 {
1317 int rc2 = RTDbgCfgSetLogCallback(hDbgCfg, dbgcDbgCfgLogCallback, pDbgc);
1318 if (RT_FAILURE(rc2))
1319 {
1320 hDbgCfg = NIL_RTDBGCFG;
1321 RTDbgCfgRelease(hDbgCfg);
1322 }
1323 }
1324 else
1325 hDbgCfg = NIL_RTDBGCFG;
1326
1327
1328 /*
1329 * Run the debugger main loop.
1330 */
1331 rc = dbgcRun(pDbgc);
1332
1333
1334 /*
1335 * Remove debug config log callback.
1336 */
1337 if (hDbgCfg != NIL_RTDBGCFG)
1338 {
1339 RTDbgCfgSetLogCallback(hDbgCfg, NULL, NULL);
1340 RTDbgCfgRelease(hDbgCfg);
1341 }
1342 }
1343 dbgcEventTerm(pDbgc);
1344 }
1345 else
1346 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nDBGCCreate error: %Rrc\n", rc);
1347
1348
1349 /*
1350 * Cleanup console debugger session.
1351 */
1352 dbgcDestroy(pDbgc);
1353 return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
1354}
1355
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette