VirtualBox

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

Last change on this file was 104614, checked in by vboxsync, 4 months ago

Debugger/DBGC*: Error handling fixes, bugref:3409

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

© 2024 Oracle
ContactPrivacy/Do Not Sell My InfoTerms of Use