[31510] | 1 | /* $Id: DBGConsole.cpp 103490 2024-02-21 12:33:44Z 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] | 195 | static 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 | */
|
---|
| 209 | int 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 | */
|
---|
| 328 | static 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 | */
|
---|
| 418 | static 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 | */
|
---|
| 464 | static 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 | */
|
---|
| 565 | int 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] | 613 | DECLHIDDEN(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] | 637 | DECLHIDDEN(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 | */
|
---|
| 654 | static 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);
|
---|
[73348] | 880 | if (pEvent->u.Generic.cArgs <= 1)
|
---|
[73352] | 881 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]);
|
---|
[73348] | 882 | else
|
---|
| 883 | {
|
---|
| 884 | for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++)
|
---|
| 885 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]);
|
---|
| 886 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
|
---|
| 887 | }
|
---|
[59246] | 888 | }
|
---|
| 889 | else
|
---|
| 890 | {
|
---|
| 891 | if (pEvtDesc->pszDesc)
|
---|
[86098] | 892 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s - %s!\n",
|
---|
| 893 | pEvent->idCpu, pEvtDesc->pszName, pEvtDesc->pszDesc);
|
---|
[59246] | 894 | else
|
---|
[86098] | 895 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s!\n",
|
---|
| 896 | pEvent->idCpu, pEvtDesc->pszName);
|
---|
[59246] | 897 | }
|
---|
| 898 | }
|
---|
| 899 | else
|
---|
[86098] | 900 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d on CPU %u!\n",
|
---|
| 901 | pEvent->enmType, pEvent->idCpu);
|
---|
[31510] | 902 | break;
|
---|
| 903 | }
|
---|
| 904 | }
|
---|
| 905 |
|
---|
| 906 | /*
|
---|
| 907 | * Prompt, anyone?
|
---|
| 908 | */
|
---|
| 909 | if (fPrintPrompt && RT_SUCCESS(rc))
|
---|
| 910 | {
|
---|
[86098] | 911 | /** @todo add CPU indicator to the prompt if an SMP VM? */
|
---|
[31510] | 912 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
|
---|
| 913 | pDbgc->fReady = true;
|
---|
| 914 | if (RT_SUCCESS(rc))
|
---|
[86327] | 915 | pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
|
---|
[83088] | 916 | pDbgc->cMultiStepsLeft = 0;
|
---|
[31510] | 917 | }
|
---|
| 918 |
|
---|
| 919 | return rc;
|
---|
| 920 | }
|
---|
| 921 |
|
---|
| 922 |
|
---|
| 923 | /**
|
---|
| 924 | * Prints any log lines from the log buffer.
|
---|
| 925 | *
|
---|
| 926 | * The caller must not call function this unless pDbgc->fLog is set.
|
---|
| 927 | *
|
---|
[58170] | 928 | * @returns VBox status code. (output related)
|
---|
[31510] | 929 | * @param pDbgc Debugger console instance data.
|
---|
| 930 | */
|
---|
| 931 | static int dbgcProcessLog(PDBGC pDbgc)
|
---|
| 932 | {
|
---|
| 933 | /** @todo */
|
---|
| 934 | NOREF(pDbgc);
|
---|
| 935 | return 0;
|
---|
| 936 | }
|
---|
| 937 |
|
---|
[46074] | 938 | /** @callback_method_impl{FNRTDBGCFGLOG} */
|
---|
| 939 | static DECLCALLBACK(void) dbgcDbgCfgLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
|
---|
| 940 | {
|
---|
| 941 | /** @todo Add symbol noise setting. */
|
---|
| 942 | NOREF(hDbgCfg); NOREF(iLevel);
|
---|
| 943 | PDBGC pDbgc = (PDBGC)pvUser;
|
---|
| 944 | pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s", pszMsg);
|
---|
| 945 | }
|
---|
[31510] | 946 |
|
---|
[46074] | 947 |
|
---|
[31510] | 948 | /**
|
---|
| 949 | * Run the debugger console.
|
---|
| 950 | *
|
---|
[58170] | 951 | * @returns VBox status code.
|
---|
[31510] | 952 | * @param pDbgc Pointer to the debugger console instance data.
|
---|
| 953 | */
|
---|
| 954 | int dbgcRun(PDBGC pDbgc)
|
---|
| 955 | {
|
---|
| 956 | /*
|
---|
| 957 | * We're ready for commands now.
|
---|
| 958 | */
|
---|
| 959 | pDbgc->fReady = true;
|
---|
[86327] | 960 | pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
|
---|
[31510] | 961 |
|
---|
| 962 | /*
|
---|
| 963 | * Main Debugger Loop.
|
---|
| 964 | *
|
---|
| 965 | * This loop will either block on waiting for input or on waiting on
|
---|
| 966 | * debug events. If we're forwarding the log we cannot wait for long
|
---|
| 967 | * before we must flush the log.
|
---|
| 968 | */
|
---|
[45006] | 969 | int rc;
|
---|
[31510] | 970 | for (;;)
|
---|
| 971 | {
|
---|
[45006] | 972 | rc = VERR_SEM_OUT_OF_TURN;
|
---|
| 973 | if (pDbgc->pUVM)
|
---|
| 974 | rc = DBGFR3QueryWaitable(pDbgc->pUVM);
|
---|
| 975 |
|
---|
| 976 | if (RT_SUCCESS(rc))
|
---|
[31510] | 977 | {
|
---|
| 978 | /*
|
---|
| 979 | * Wait for a debug event.
|
---|
| 980 | */
|
---|
[86098] | 981 | DBGFEVENT Event;
|
---|
| 982 | rc = DBGFR3EventWait(pDbgc->pUVM, pDbgc->fLog ? 1 : 32, &Event);
|
---|
[31510] | 983 | if (RT_SUCCESS(rc))
|
---|
| 984 | {
|
---|
[86098] | 985 | rc = dbgcProcessEvent(pDbgc, &Event);
|
---|
[31510] | 986 | if (RT_FAILURE(rc))
|
---|
| 987 | break;
|
---|
| 988 | }
|
---|
| 989 | else if (rc != VERR_TIMEOUT)
|
---|
| 990 | break;
|
---|
| 991 |
|
---|
| 992 | /*
|
---|
| 993 | * Check for input.
|
---|
| 994 | */
|
---|
[86327] | 995 | if (pDbgc->pIo->pfnInput(pDbgc->pIo, 0))
|
---|
[31510] | 996 | {
|
---|
| 997 | rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
|
---|
| 998 | if (RT_FAILURE(rc))
|
---|
| 999 | break;
|
---|
| 1000 | }
|
---|
| 1001 | }
|
---|
[45006] | 1002 | else if (rc == VERR_SEM_OUT_OF_TURN)
|
---|
[31510] | 1003 | {
|
---|
| 1004 | /*
|
---|
| 1005 | * Wait for input. If Logging is enabled we'll only wait very briefly.
|
---|
| 1006 | */
|
---|
[86327] | 1007 | if (pDbgc->pIo->pfnInput(pDbgc->pIo, pDbgc->fLog ? 1 : 1000))
|
---|
[31510] | 1008 | {
|
---|
| 1009 | rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
|
---|
| 1010 | if (RT_FAILURE(rc))
|
---|
| 1011 | break;
|
---|
| 1012 | }
|
---|
| 1013 | }
|
---|
[45006] | 1014 | else
|
---|
| 1015 | break;
|
---|
[31510] | 1016 |
|
---|
| 1017 | /*
|
---|
| 1018 | * Forward log output.
|
---|
| 1019 | */
|
---|
| 1020 | if (pDbgc->fLog)
|
---|
| 1021 | {
|
---|
| 1022 | rc = dbgcProcessLog(pDbgc);
|
---|
| 1023 | if (RT_FAILURE(rc))
|
---|
| 1024 | break;
|
---|
| 1025 | }
|
---|
| 1026 | }
|
---|
| 1027 |
|
---|
| 1028 | return rc;
|
---|
| 1029 | }
|
---|
| 1030 |
|
---|
| 1031 |
|
---|
| 1032 | /**
|
---|
[59229] | 1033 | * Run the init scripts, if present.
|
---|
| 1034 | *
|
---|
| 1035 | * @param pDbgc The console instance.
|
---|
| 1036 | */
|
---|
| 1037 | static void dbgcRunInitScripts(PDBGC pDbgc)
|
---|
| 1038 | {
|
---|
| 1039 | /*
|
---|
| 1040 | * Do the global one, if it exists.
|
---|
| 1041 | */
|
---|
| 1042 | if ( pDbgc->pszGlobalInitScript
|
---|
| 1043 | && *pDbgc->pszGlobalInitScript != '\0'
|
---|
| 1044 | && RTFileExists(pDbgc->pszGlobalInitScript))
|
---|
| 1045 | dbgcEvalScript(pDbgc, pDbgc->pszGlobalInitScript, true /*fAnnounce*/);
|
---|
| 1046 |
|
---|
| 1047 | /*
|
---|
| 1048 | * Then do the local one, if it exists.
|
---|
| 1049 | */
|
---|
| 1050 | if ( pDbgc->pszLocalInitScript
|
---|
| 1051 | && *pDbgc->pszLocalInitScript != '\0'
|
---|
| 1052 | && RTFileExists(pDbgc->pszLocalInitScript))
|
---|
| 1053 | dbgcEvalScript(pDbgc, pDbgc->pszLocalInitScript, true /*fAnnounce*/);
|
---|
| 1054 | }
|
---|
| 1055 |
|
---|
| 1056 |
|
---|
| 1057 | /**
|
---|
| 1058 | * Reads the CFGM configuration of the DBGC.
|
---|
| 1059 | *
|
---|
| 1060 | * Popuplates the PDBGC::pszHistoryFile, PDBGC::pszGlobalInitScript and
|
---|
| 1061 | * PDBGC::pszLocalInitScript members.
|
---|
| 1062 | *
|
---|
| 1063 | * @returns VBox status code.
|
---|
| 1064 | * @param pDbgc The console instance.
|
---|
| 1065 | * @param pUVM The user mode VM handle.
|
---|
| 1066 | */
|
---|
| 1067 | static int dbgcReadConfig(PDBGC pDbgc, PUVM pUVM)
|
---|
| 1068 | {
|
---|
| 1069 | /*
|
---|
| 1070 | * Get and validate the configuration node.
|
---|
| 1071 | */
|
---|
| 1072 | PCFGMNODE pNode = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
|
---|
| 1073 | int rc = CFGMR3ValidateConfig(pNode, "/DBGC/",
|
---|
[60835] | 1074 | "Enabled|"
|
---|
[59229] | 1075 | "HistoryFile|"
|
---|
| 1076 | "LocalInitScript|"
|
---|
[86327] | 1077 | "GlobalInitScript|",
|
---|
| 1078 | "*", "DBGC", 0);
|
---|
[59229] | 1079 | AssertRCReturn(rc, rc);
|
---|
| 1080 |
|
---|
| 1081 | /*
|
---|
| 1082 | * Query the values.
|
---|
| 1083 | */
|
---|
| 1084 | char szHomeDefault[RTPATH_MAX];
|
---|
| 1085 | rc = RTPathUserHome(szHomeDefault, sizeof(szHomeDefault) - 32);
|
---|
| 1086 | AssertLogRelRCReturn(rc, rc);
|
---|
| 1087 | size_t cchHome = strlen(szHomeDefault);
|
---|
| 1088 |
|
---|
| 1089 | /** @cfgm{/DBGC/HistoryFile, string, ${HOME}/.vboxdbgc-history}
|
---|
| 1090 | * The command history file of the VBox debugger. */
|
---|
| 1091 | rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-history");
|
---|
| 1092 | AssertLogRelRCReturn(rc, rc);
|
---|
| 1093 |
|
---|
| 1094 | char szPath[RTPATH_MAX];
|
---|
| 1095 | rc = CFGMR3QueryStringDef(pNode, "HistoryFile", szPath, sizeof(szPath), szHomeDefault);
|
---|
| 1096 | AssertLogRelRCReturn(rc, rc);
|
---|
| 1097 |
|
---|
| 1098 | pDbgc->pszHistoryFile = RTStrDup(szPath);
|
---|
| 1099 | AssertReturn(pDbgc->pszHistoryFile, VERR_NO_STR_MEMORY);
|
---|
| 1100 |
|
---|
| 1101 | /** @cfgm{/DBGC/GlobalInitFile, string, ${HOME}/.vboxdbgc-init}
|
---|
| 1102 | * The global init script of the VBox debugger. */
|
---|
| 1103 | szHomeDefault[cchHome] = '\0';
|
---|
| 1104 | rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-init");
|
---|
| 1105 | AssertLogRelRCReturn(rc, rc);
|
---|
| 1106 |
|
---|
| 1107 | rc = CFGMR3QueryStringDef(pNode, "GlobalInitScript", szPath, sizeof(szPath), szHomeDefault);
|
---|
| 1108 | AssertLogRelRCReturn(rc, rc);
|
---|
| 1109 |
|
---|
| 1110 | pDbgc->pszGlobalInitScript = RTStrDup(szPath);
|
---|
| 1111 | AssertReturn(pDbgc->pszGlobalInitScript, VERR_NO_STR_MEMORY);
|
---|
| 1112 |
|
---|
| 1113 | /** @cfgm{/DBGC/LocalInitFile, string, none}
|
---|
| 1114 | * The VM local init script of the VBox debugger. */
|
---|
| 1115 | rc = CFGMR3QueryString(pNode, "LocalInitScript", szPath, sizeof(szPath));
|
---|
| 1116 | if (RT_SUCCESS(rc))
|
---|
| 1117 | {
|
---|
| 1118 | pDbgc->pszLocalInitScript = RTStrDup(szPath);
|
---|
| 1119 | AssertReturn(pDbgc->pszLocalInitScript, VERR_NO_STR_MEMORY);
|
---|
| 1120 | }
|
---|
| 1121 | else
|
---|
| 1122 | {
|
---|
| 1123 | AssertLogRelReturn(rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT, rc);
|
---|
| 1124 | pDbgc->pszLocalInitScript = NULL;
|
---|
| 1125 | }
|
---|
| 1126 |
|
---|
| 1127 | return VINF_SUCCESS;
|
---|
| 1128 | }
|
---|
| 1129 |
|
---|
| 1130 |
|
---|
[84653] | 1131 | /**
|
---|
[84660] | 1132 | * @copydoc DBGC::pfnOutput
|
---|
[84653] | 1133 | */
|
---|
| 1134 | static DECLCALLBACK(int) dbgcOutputNative(void *pvUser, const char *pachChars, size_t cbChars)
|
---|
| 1135 | {
|
---|
| 1136 | PDBGC pDbgc = (PDBGC)pvUser;
|
---|
[86327] | 1137 | return pDbgc->pIo->pfnWrite(pDbgc->pIo, pachChars, cbChars, NULL /*pcbWritten*/);
|
---|
[84653] | 1138 | }
|
---|
[59229] | 1139 |
|
---|
[84653] | 1140 |
|
---|
[59229] | 1141 | /**
|
---|
[31510] | 1142 | * Creates a a new instance.
|
---|
| 1143 | *
|
---|
| 1144 | * @returns VBox status code.
|
---|
| 1145 | * @param ppDbgc Where to store the pointer to the instance data.
|
---|
[86327] | 1146 | * @param pIo Pointer to the I/O callback table.
|
---|
[31510] | 1147 | * @param fFlags The flags.
|
---|
| 1148 | */
|
---|
[86327] | 1149 | int dbgcCreate(PDBGC *ppDbgc, PCDBGCIO pIo, unsigned fFlags)
|
---|
[31510] | 1150 | {
|
---|
| 1151 | /*
|
---|
| 1152 | * Validate input.
|
---|
| 1153 | */
|
---|
[86327] | 1154 | AssertPtrReturn(pIo, VERR_INVALID_POINTER);
|
---|
[31510] | 1155 | AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
|
---|
| 1156 |
|
---|
| 1157 | /*
|
---|
| 1158 | * Allocate and initialize.
|
---|
| 1159 | */
|
---|
| 1160 | PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
|
---|
| 1161 | if (!pDbgc)
|
---|
| 1162 | return VERR_NO_MEMORY;
|
---|
| 1163 |
|
---|
| 1164 | dbgcInitCmdHlp(pDbgc);
|
---|
[86327] | 1165 | pDbgc->pIo = pIo;
|
---|
[84653] | 1166 | pDbgc->pfnOutput = dbgcOutputNative;
|
---|
| 1167 | pDbgc->pvOutputUser = pDbgc;
|
---|
[31510] | 1168 | pDbgc->pVM = NULL;
|
---|
[44399] | 1169 | pDbgc->pUVM = NULL;
|
---|
[35629] | 1170 | pDbgc->idCpu = 0;
|
---|
[31510] | 1171 | pDbgc->hDbgAs = DBGF_AS_GLOBAL;
|
---|
| 1172 | pDbgc->pszEmulation = "CodeView/WinDbg";
|
---|
| 1173 | pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
|
---|
| 1174 | pDbgc->cEmulationCmds = g_cCmdsCodeView;
|
---|
[41561] | 1175 | pDbgc->paEmulationFuncs = &g_aFuncsCodeView[0];
|
---|
| 1176 | pDbgc->cEmulationFuncs = g_cFuncsCodeView;
|
---|
[31510] | 1177 | //pDbgc->fLog = false;
|
---|
| 1178 | pDbgc->fRegTerse = true;
|
---|
[64721] | 1179 | pDbgc->fStepTraceRegs = true;
|
---|
[31987] | 1180 | //pDbgc->cPagingHierarchyDumps = 0;
|
---|
[31510] | 1181 | //pDbgc->DisasmPos = {0};
|
---|
| 1182 | //pDbgc->SourcePos = {0};
|
---|
| 1183 | //pDbgc->DumpPos = {0};
|
---|
[31966] | 1184 | pDbgc->pLastPos = &pDbgc->DisasmPos;
|
---|
[31510] | 1185 | //pDbgc->cbDumpElement = 0;
|
---|
| 1186 | //pDbgc->cVars = 0;
|
---|
| 1187 | //pDbgc->paVars = NULL;
|
---|
| 1188 | //pDbgc->pPlugInHead = NULL;
|
---|
| 1189 | //pDbgc->pFirstBp = NULL;
|
---|
[87788] | 1190 | RTListInit(&pDbgc->LstTraceFlowMods);
|
---|
[31510] | 1191 | //pDbgc->abSearch = {0};
|
---|
| 1192 | //pDbgc->cbSearch = 0;
|
---|
| 1193 | pDbgc->cbSearchUnit = 1;
|
---|
| 1194 | pDbgc->cMaxSearchHits = 1;
|
---|
| 1195 | //pDbgc->SearchAddr = {0};
|
---|
| 1196 | //pDbgc->cbSearchRange = 0;
|
---|
| 1197 |
|
---|
| 1198 | //pDbgc->uInputZero = 0;
|
---|
| 1199 | //pDbgc->iRead = 0;
|
---|
| 1200 | //pDbgc->iWrite = 0;
|
---|
| 1201 | //pDbgc->cInputLines = 0;
|
---|
| 1202 | //pDbgc->fInputOverflow = false;
|
---|
| 1203 | pDbgc->fReady = true;
|
---|
| 1204 | pDbgc->pszScratch = &pDbgc->achScratch[0];
|
---|
| 1205 | //pDbgc->iArg = 0;
|
---|
| 1206 | //pDbgc->rcOutput = 0;
|
---|
| 1207 | //pDbgc->rcCmd = 0;
|
---|
| 1208 |
|
---|
[59229] | 1209 | //pDbgc->pszHistoryFile = NULL;
|
---|
| 1210 | //pDbgc->pszGlobalInitScript = NULL;
|
---|
| 1211 | //pDbgc->pszLocalInitScript = NULL;
|
---|
| 1212 |
|
---|
[35628] | 1213 | dbgcEvalInit();
|
---|
[31510] | 1214 |
|
---|
| 1215 | *ppDbgc = pDbgc;
|
---|
| 1216 | return VINF_SUCCESS;
|
---|
| 1217 | }
|
---|
| 1218 |
|
---|
| 1219 | /**
|
---|
| 1220 | * Destroys a DBGC instance created by dbgcCreate.
|
---|
| 1221 | *
|
---|
| 1222 | * @param pDbgc Pointer to the debugger console instance data.
|
---|
| 1223 | */
|
---|
| 1224 | void dbgcDestroy(PDBGC pDbgc)
|
---|
| 1225 | {
|
---|
| 1226 | AssertPtr(pDbgc);
|
---|
| 1227 |
|
---|
| 1228 | /* Disable log hook. */
|
---|
| 1229 | if (pDbgc->fLog)
|
---|
| 1230 | {
|
---|
| 1231 |
|
---|
| 1232 | }
|
---|
| 1233 |
|
---|
| 1234 | /* Detach from the VM. */
|
---|
[44399] | 1235 | if (pDbgc->pUVM)
|
---|
| 1236 | DBGFR3Detach(pDbgc->pUVM);
|
---|
[31510] | 1237 |
|
---|
[59229] | 1238 | /* Free config strings. */
|
---|
| 1239 | RTStrFree(pDbgc->pszGlobalInitScript);
|
---|
| 1240 | pDbgc->pszGlobalInitScript = NULL;
|
---|
| 1241 | RTStrFree(pDbgc->pszLocalInitScript);
|
---|
| 1242 | pDbgc->pszLocalInitScript = NULL;
|
---|
| 1243 | RTStrFree(pDbgc->pszHistoryFile);
|
---|
| 1244 | pDbgc->pszHistoryFile = NULL;
|
---|
| 1245 |
|
---|
| 1246 | /* Finally, free the instance memory. */
|
---|
[31510] | 1247 | RTMemFree(pDbgc);
|
---|
| 1248 | }
|
---|
| 1249 |
|
---|
| 1250 |
|
---|
| 1251 | /**
|
---|
| 1252 | * Make a console instance.
|
---|
| 1253 | *
|
---|
| 1254 | * This will not return until either an 'exit' command is issued or a error code
|
---|
| 1255 | * indicating connection loss is encountered.
|
---|
| 1256 | *
|
---|
| 1257 | * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
|
---|
| 1258 | * @returns The VBox status code causing the console termination.
|
---|
| 1259 | *
|
---|
[44399] | 1260 | * @param pUVM The user mode VM handle.
|
---|
[86327] | 1261 | * @param pIo Pointer to the I/O callback structure. This must contain
|
---|
[31510] | 1262 | * a full set of function pointers to service the console.
|
---|
| 1263 | * @param fFlags Reserved, must be zero.
|
---|
[59229] | 1264 | * @remarks A forced termination of the console is easiest done by forcing the
|
---|
[31510] | 1265 | * callbacks to return fatal failures.
|
---|
| 1266 | */
|
---|
[86327] | 1267 | DBGDECL(int) DBGCCreate(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags)
|
---|
[31510] | 1268 | {
|
---|
| 1269 | /*
|
---|
| 1270 | * Validate input.
|
---|
| 1271 | */
|
---|
[44399] | 1272 | AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
|
---|
| 1273 | PVM pVM = NULL;
|
---|
| 1274 | if (pUVM)
|
---|
| 1275 | {
|
---|
| 1276 | pVM = VMR3GetVM(pUVM);
|
---|
| 1277 | AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
|
---|
| 1278 | }
|
---|
[31510] | 1279 |
|
---|
| 1280 | /*
|
---|
| 1281 | * Allocate and initialize instance data
|
---|
| 1282 | */
|
---|
| 1283 | PDBGC pDbgc;
|
---|
[86327] | 1284 | int rc = dbgcCreate(&pDbgc, pIo, fFlags);
|
---|
[31510] | 1285 | if (RT_FAILURE(rc))
|
---|
| 1286 | return rc;
|
---|
[72268] | 1287 | if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM))
|
---|
[46137] | 1288 | pDbgc->hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
|
---|
[31510] | 1289 |
|
---|
| 1290 | /*
|
---|
| 1291 | * Print welcome message.
|
---|
| 1292 | */
|
---|
| 1293 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
|
---|
| 1294 | "Welcome to the VirtualBox Debugger!\n");
|
---|
| 1295 |
|
---|
| 1296 | /*
|
---|
| 1297 | * Attach to the specified VM.
|
---|
| 1298 | */
|
---|
[44399] | 1299 | if (RT_SUCCESS(rc) && pUVM)
|
---|
[31510] | 1300 | {
|
---|
[59229] | 1301 | rc = dbgcReadConfig(pDbgc, pUVM);
|
---|
[31510] | 1302 | if (RT_SUCCESS(rc))
|
---|
| 1303 | {
|
---|
[59229] | 1304 | rc = DBGFR3Attach(pUVM);
|
---|
| 1305 | if (RT_SUCCESS(rc))
|
---|
| 1306 | {
|
---|
| 1307 | pDbgc->pVM = pVM;
|
---|
| 1308 | pDbgc->pUVM = pUVM;
|
---|
| 1309 | pDbgc->idCpu = 0;
|
---|
| 1310 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
|
---|
| 1311 | "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
|
---|
| 1312 | , pDbgc->pVM, pDbgc->idCpu);
|
---|
| 1313 | }
|
---|
| 1314 | else
|
---|
| 1315 | rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
|
---|
[31510] | 1316 | }
|
---|
| 1317 | else
|
---|
[59229] | 1318 | rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "Error reading configuration\n");
|
---|
[31510] | 1319 | }
|
---|
| 1320 |
|
---|
| 1321 | /*
|
---|
| 1322 | * Load plugins.
|
---|
| 1323 | */
|
---|
| 1324 | if (RT_SUCCESS(rc))
|
---|
| 1325 | {
|
---|
| 1326 | if (pVM)
|
---|
[55881] | 1327 | DBGFR3PlugInLoadAll(pDbgc->pUVM);
|
---|
[59072] | 1328 | dbgcEventInit(pDbgc);
|
---|
[59229] | 1329 | dbgcRunInitScripts(pDbgc);
|
---|
[59072] | 1330 |
|
---|
[31510] | 1331 | rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
|
---|
[46074] | 1332 | if (RT_SUCCESS(rc))
|
---|
| 1333 | {
|
---|
| 1334 | /*
|
---|
| 1335 | * Set debug config log callback.
|
---|
| 1336 | */
|
---|
| 1337 | RTDBGCFG hDbgCfg = DBGFR3AsGetConfig(pUVM);
|
---|
| 1338 | if ( hDbgCfg != NIL_RTDBGCFG
|
---|
| 1339 | && RTDbgCfgRetain(hDbgCfg) != UINT32_MAX)
|
---|
| 1340 | {
|
---|
| 1341 | int rc2 = RTDbgCfgSetLogCallback(hDbgCfg, dbgcDbgCfgLogCallback, pDbgc);
|
---|
| 1342 | if (RT_FAILURE(rc2))
|
---|
| 1343 | {
|
---|
| 1344 | hDbgCfg = NIL_RTDBGCFG;
|
---|
| 1345 | RTDbgCfgRelease(hDbgCfg);
|
---|
| 1346 | }
|
---|
| 1347 | }
|
---|
| 1348 | else
|
---|
| 1349 | hDbgCfg = NIL_RTDBGCFG;
|
---|
| 1350 |
|
---|
| 1351 |
|
---|
| 1352 | /*
|
---|
| 1353 | * Run the debugger main loop.
|
---|
| 1354 | */
|
---|
| 1355 | rc = dbgcRun(pDbgc);
|
---|
| 1356 |
|
---|
| 1357 |
|
---|
| 1358 | /*
|
---|
| 1359 | * Remove debug config log callback.
|
---|
| 1360 | */
|
---|
| 1361 | if (hDbgCfg != NIL_RTDBGCFG)
|
---|
| 1362 | {
|
---|
| 1363 | RTDbgCfgSetLogCallback(hDbgCfg, NULL, NULL);
|
---|
| 1364 | RTDbgCfgRelease(hDbgCfg);
|
---|
| 1365 | }
|
---|
| 1366 | }
|
---|
[59072] | 1367 | dbgcEventTerm(pDbgc);
|
---|
[31510] | 1368 | }
|
---|
| 1369 | else
|
---|
| 1370 | pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nDBGCCreate error: %Rrc\n", rc);
|
---|
| 1371 |
|
---|
| 1372 |
|
---|
| 1373 | /*
|
---|
| 1374 | * Cleanup console debugger session.
|
---|
| 1375 | */
|
---|
| 1376 | dbgcDestroy(pDbgc);
|
---|
| 1377 | return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
|
---|
| 1378 | }
|
---|
| 1379 |
|
---|