VirtualBox

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

Last change on this file since 97639 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 45.8 KB
Line 
1/* $Id: DBGConsole.cpp 96407 2022-08-22 17:43:14Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console.
4 */
5
6/*
7 * Copyright (C) 2006-2022 Oracle and/or its affiliates.
8 *
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
26 */
27
28
29/** @page pg_dbgc DBGC - The Debug Console
30 *
31 * The debugger console is an early attempt to make some interactive
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.
35 *
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.
40 *
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.
46 *
47 *
48 * @section sec_dbgc_op Operation
49 *
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.
55 *
56 *
57 * @subsection sec_dbg_op_numbers Numbers
58 *
59 * Numbers are hexadecimal unless specified with a prefix indicating
60 * elsewise. Prefixes:
61 * - '0x' - hexadecimal.
62 * - '0n' - decimal
63 * - '0t' - octal.
64 * - '0y' - binary.
65 *
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.
70 *
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.
74 *
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 *
94 * @subsection sec_dbg_op_address Addressing modes
95 *
96 * - Default is flat. For compatibility '%' also means flat.
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 ':'
104 * is a binary operator. Operator precedence takes care of evaluation order.
105 *
106 *
107 * @subsection sec_dbg_op_c_operators C/C++ Operators
108 *
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.
114 *
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.
117 *
118 *
119 * @subsection sec_dbg_op_registers Registers
120 *
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 ('.').
125 *
126 *
127 * @subsection sec_dbg_op_commands Commands
128 *
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.
133 *
134 * Commands returns a status code.
135 *
136 * The '.' prefix indicates the set of external commands. External commands are
137 * command registered by VMM components.
138 *
139 *
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 *
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 *
154 * This feature has not materialized and probably never will.
155 *
156 *
157 * @section sec_dbgc_linking Linking and API
158 *
159 * The DBGC code is linked into the VBoxVMM module.
160 *
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.
165 */
166
167
168/*********************************************************************************************************************************
169* Header Files *
170*********************************************************************************************************************************/
171#define LOG_GROUP LOG_GROUP_DBGC
172#include <VBox/dbg.h>
173#include <VBox/vmm/cfgm.h>
174#include <VBox/vmm/dbgf.h>
175#include <VBox/vmm/vmapi.h> /* VMR3GetVM() */
176#include <VBox/vmm/hm.h> /* HMR3IsEnabled */
177#include <VBox/vmm/nem.h> /* NEMR3IsEnabled */
178#include <VBox/err.h>
179#include <VBox/log.h>
180
181#include <iprt/asm.h>
182#include <iprt/assert.h>
183#include <iprt/file.h>
184#include <iprt/mem.h>
185#include <iprt/path.h>
186#include <iprt/string.h>
187
188#include "DBGCInternal.h"
189#include "DBGPlugIns.h"
190
191
192/*********************************************************************************************************************************
193* Internal Functions *
194*********************************************************************************************************************************/
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.
205 * @param enmType The result type. Specifying DBGCVAR_TYPE_GC_FAR may
206 * cause failure, avoid it.
207 * @param pResult Where to store the result.
208 */
209int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
210{
211 int rc;
212
213 /*
214 * Builtin?
215 */
216 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
217 if (pSymDesc)
218 {
219 if (!pSymDesc->pfnGet)
220 return VERR_DBGC_PARSE_WRITEONLY_SYMBOL;
221 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
222 }
223
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 ;
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)))
258 {
259 if (!strchr(pszSymbol, ';'))
260 {
261 DBGCVAR Var;
262 DBGCVAR_INIT_SYMBOL(&Var, pszSymbol);
263 rc = dbgcOpRegister(pDbgc, &Var, DBGCVAR_CAT_ANY, pResult);
264 if (RT_SUCCESS(rc))
265 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
266 }
267 }
268
269 /*
270 * Ask PDM.
271 */
272 /** @todo resolve symbols using PDM. */
273
274 /*
275 * Ask the debug info manager.
276 */
277 RTDBGSYMBOL Symbol;
278 rc = DBGFR3AsSymbolByName(pDbgc->pUVM, pDbgc->hDbgAs, pszSymbol, &Symbol, NULL);
279 if (RT_SUCCESS(rc))
280 {
281 /*
282 * Default return is a flat gc address.
283 */
284 DBGCVAR_INIT_GC_FLAT(pResult, Symbol.Value);
285 if (Symbol.cb)
286 DBGCVAR_SET_RANGE(pResult, DBGCVAR_RANGE_BYTES, Symbol.cb);
287
288 switch (enmType)
289 {
290 /* nothing to do. */
291 case DBGCVAR_TYPE_GC_FLAT:
292 case DBGCVAR_TYPE_ANY:
293 return VINF_SUCCESS;
294
295 /* impossible at the moment. */
296 case DBGCVAR_TYPE_GC_FAR:
297 return VERR_DBGC_PARSE_CONVERSION_FAILED;
298
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:
309 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
310
311 default:
312 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
313 return VERR_INVALID_PARAMETER;
314 }
315 }
316
317 return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
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{
330 /** @todo Replace this with a sh/ksh/csh/rexx like toplevel language that
331 * allows doing function, loops, if, cases, and such. */
332 int rc = VINF_SUCCESS;
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 */
355 char ch;
356 char *psz = &pDbgc->achInput[pDbgc->iRead];
357 char *pszTrg = &pDbgc->achScratch[0];
358 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
359 {
360 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
361 psz = &pDbgc->achInput[0];
362
363 if (psz == &pDbgc->achInput[pDbgc->iWrite])
364 {
365 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
366 pDbgc->cInputLines = 0;
367 return 0;
368 }
369
370 pszTrg++;
371 }
372 *pszTrg = '\0';
373
374 /*
375 * Advance the buffer.
376 */
377 pDbgc->iRead = psz - &pDbgc->achInput[0];
378 if (ch == '\n')
379 pDbgc->cInputLines--;
380
381 /*
382 * Parse and execute this command.
383 */
384 pDbgc->pszScratch = pszTrg + 1;
385 pDbgc->iArg = 0;
386 rc = dbgcEvalCommand(pDbgc, &pDbgc->achScratch[0], pszTrg - &pDbgc->achScratch[0] - 1, fNoExecute);
387 if ( rc == VERR_DBGC_QUIT
388 || rc == VWRN_DBGC_CMD_PENDING)
389 break;
390 rc = VINF_SUCCESS; /* ignore other statuses */
391 }
392
393 return rc;
394}
395
396
397/**
398 * Handle input buffer overflow.
399 *
400 * Will read any available input looking for a '\n' to reset the buffer on.
401 *
402 * @returns VBox status code.
403 * @param pDbgc Debugger console instance data.
404 */
405static int dbgcInputOverflow(PDBGC pDbgc)
406{
407 /*
408 * Assert overflow status and reset the input buffer.
409 */
410 if (!pDbgc->fInputOverflow)
411 {
412 pDbgc->fInputOverflow = true;
413 pDbgc->iRead = pDbgc->iWrite = 0;
414 pDbgc->cInputLines = 0;
415 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
416 }
417
418 /*
419 * Eat input till no more or there is a '\n'.
420 * When finding a '\n' we'll continue normal processing.
421 */
422 while (pDbgc->pIo->pfnInput(pDbgc->pIo, 0))
423 {
424 size_t cbRead;
425 int rc = pDbgc->pIo->pfnRead(pDbgc->pIo, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
426 if (RT_FAILURE(rc))
427 return rc;
428 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
429 if (psz)
430 {
431 pDbgc->fInputOverflow = false;
432 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
433 pDbgc->iWrite = (unsigned)cbRead;
434 pDbgc->cInputLines = 0;
435 break;
436 }
437 }
438
439 return 0;
440}
441
442
443/**
444 * Read input and do some preprocessing.
445 *
446 * @returns VBox status code.
447 * In addition to the iWrite and achInput, cInputLines is maintained.
448 * In case of an input overflow the fInputOverflow flag will be set.
449 * @param pDbgc Debugger console instance data.
450 */
451static int dbgcInputRead(PDBGC pDbgc)
452{
453 /*
454 * We have ready input.
455 * Read it till we don't have any or we have a full input buffer.
456 */
457 int rc = 0;
458 do
459 {
460 /*
461 * More available buffer space?
462 */
463 size_t cbLeft;
464 if (pDbgc->iWrite > pDbgc->iRead)
465 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
466 else
467 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
468 if (!cbLeft)
469 {
470 /* overflow? */
471 if (!pDbgc->cInputLines)
472 rc = dbgcInputOverflow(pDbgc);
473 break;
474 }
475
476 /*
477 * Read one char and interpret it.
478 */
479 char achRead[128];
480 size_t cbRead;
481 rc = pDbgc->pIo->pfnRead(pDbgc->pIo, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
482 if (RT_FAILURE(rc))
483 return rc;
484 char *psz = &achRead[0];
485 while (cbRead-- > 0)
486 {
487 char ch = *psz++;
488 switch (ch)
489 {
490 /*
491 * Ignore.
492 */
493 case '\0':
494 case '\r':
495 case '\a':
496 break;
497
498 /*
499 * Backspace.
500 */
501 case '\b':
502 Log2(("DBGC: backspace\n"));
503 if (pDbgc->iRead != pDbgc->iWrite)
504 {
505 unsigned iWriteUndo = pDbgc->iWrite;
506 if (pDbgc->iWrite)
507 pDbgc->iWrite--;
508 else
509 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
510
511 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
512 pDbgc->iWrite = iWriteUndo;
513 }
514 break;
515
516 /*
517 * Add char to buffer.
518 */
519 case '\t':
520 case '\n':
521 case ';':
522 switch (ch)
523 {
524 case '\t': ch = ' '; break;
525 case '\n': pDbgc->cInputLines++; break;
526 }
527 RT_FALL_THRU();
528 default:
529 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
530 pDbgc->achInput[pDbgc->iWrite] = ch;
531 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
532 pDbgc->iWrite = 0;
533 break;
534 }
535 }
536
537 /* Terminate it to make it easier to read in the debugger. */
538 pDbgc->achInput[pDbgc->iWrite] = '\0';
539 } while (pDbgc->pIo->pfnInput(pDbgc->pIo, 0));
540
541 return rc;
542}
543
544
545/**
546 * Reads input, parses it and executes commands on '\n'.
547 *
548 * @returns VBox status code.
549 * @param pDbgc Debugger console instance data.
550 * @param fNoExecute Indicates that no commands should actually be executed.
551 */
552int dbgcProcessInput(PDBGC pDbgc, bool fNoExecute)
553{
554 /*
555 * We know there's input ready, so let's read it first.
556 */
557 int rc = dbgcInputRead(pDbgc);
558 if (RT_FAILURE(rc))
559 return rc;
560
561 /*
562 * Now execute any ready commands.
563 */
564 if (pDbgc->cInputLines)
565 {
566 pDbgc->pIo->pfnSetReady(pDbgc->pIo, false);
567 pDbgc->fReady = false;
568 rc = dbgcProcessCommands(pDbgc, fNoExecute);
569 if (RT_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
570 pDbgc->fReady = true;
571
572 if ( RT_SUCCESS(rc)
573 && pDbgc->iRead == pDbgc->iWrite
574 && pDbgc->fReady)
575 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
576
577 if ( RT_SUCCESS(rc)
578 && pDbgc->fReady)
579 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
580 }
581 /*
582 * else - we have incomplete line, so leave it in the buffer and
583 * wait for more input.
584 *
585 * Windows telnet client is in "character at a time" mode by
586 * default and putty sends eol as a separate packet that will be
587 * most likely read separately from the command line it
588 * terminates.
589 */
590
591 return rc;
592}
593
594
595/**
596 * Gets the event context identifier string.
597 * @returns Read only string.
598 * @param enmCtx The context.
599 */
600DECLHIDDEN(const char *) dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
601{
602 switch (enmCtx)
603 {
604 case DBGFEVENTCTX_RAW: return "raw";
605 case DBGFEVENTCTX_REM: return "rem";
606 case DBGFEVENTCTX_HM: return "hwaccl";
607 case DBGFEVENTCTX_HYPER: return "hyper";
608 case DBGFEVENTCTX_OTHER: return "other";
609
610 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
611 default:
612 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
613 return "!Unknown Event Ctx!";
614 }
615}
616
617
618/**
619 * Looks up a generic debug event.
620 *
621 * @returns Pointer to DBGCSXEVT structure if found, otherwise NULL.
622 * @param enmType The possibly generic event to find the descriptor for.
623 */
624DECLHIDDEN(PCDBGCSXEVT) dbgcEventLookup(DBGFEVENTTYPE enmType)
625{
626 uint32_t i = g_cDbgcSxEvents;
627 while (i-- > 0)
628 if (g_aDbgcSxEvents[i].enmType == enmType)
629 return &g_aDbgcSxEvents[i];
630 return NULL;
631}
632
633
634/**
635 * Processes debugger events.
636 *
637 * @returns VBox status code.
638 * @param pDbgc DBGC Instance data.
639 * @param pEvent Pointer to event data.
640 */
641static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
642{
643 /*
644 * Flush log first.
645 */
646 if (pDbgc->fLog)
647 {
648 int rc = dbgcProcessLog(pDbgc);
649 if (RT_FAILURE(rc))
650 return rc;
651 }
652
653 /*
654 * Process the event.
655 */
656 pDbgc->pszScratch = &pDbgc->achInput[0];
657 pDbgc->iArg = 0;
658 bool fPrintPrompt = true;
659 int rc = VINF_SUCCESS;
660 VMCPUID const idCpuSaved = pDbgc->idCpu;
661 switch (pEvent->enmType)
662 {
663 /*
664 * The first part is events we have initiated with commands.
665 */
666 case DBGFEVENT_HALT_DONE:
667 {
668 /** @todo add option to suppress this on CPUs that aren't selected (like
669 * fRegTerse). */
670 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: CPU %u has halted! (%s)\n",
671 pEvent->idCpu, pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
672 if (RT_SUCCESS(rc))
673 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
674 break;
675 }
676
677
678 /*
679 * The second part is events which can occur at any time.
680 */
681 case DBGFEVENT_FATAL_ERROR:
682 {
683 pDbgc->idCpu = pEvent->idCpu;
684 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event/%u: Fatal error! (%s)\n",
685 pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
686 if (RT_SUCCESS(rc))
687 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
688 break;
689 }
690
691 case DBGFEVENT_BREAKPOINT:
692 case DBGFEVENT_BREAKPOINT_IO:
693 case DBGFEVENT_BREAKPOINT_MMIO:
694 case DBGFEVENT_BREAKPOINT_HYPER:
695 {
696 pDbgc->idCpu = pEvent->idCpu;
697 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.hBp);
698 switch (rc)
699 {
700 case VERR_DBGC_BP_NOT_FOUND:
701 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Unknown breakpoint %u! (%s)\n",
702 pEvent->idCpu, pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx));
703 break;
704
705 case VINF_DBGC_BP_NO_COMMAND:
706 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Breakpoint %u! (%s)\n",
707 pEvent->idCpu, pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx));
708 break;
709
710 case VINF_BUFFER_OVERFLOW:
711 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Breakpoint %u! Command too long to execute! (%s)\n",
712 pEvent->idCpu, pEvent->u.Bp.hBp, dbgcGetEventCtx(pEvent->enmCtx));
713 break;
714
715 default:
716 break;
717 }
718 if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM, pEvent->idCpu))
719 {
720 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
721
722 /* Set the resume flag to ignore the breakpoint when resuming execution. */
723 if ( RT_SUCCESS(rc)
724 && pEvent->enmType == DBGFEVENT_BREAKPOINT)
725 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r eflags.rf = 1");
726 }
727 else
728 pDbgc->idCpu = idCpuSaved;
729 break;
730 }
731
732 case DBGFEVENT_STEPPED:
733 case DBGFEVENT_STEPPED_HYPER:
734 {
735 if (!pDbgc->cMultiStepsLeft || pEvent->idCpu != idCpuSaved)
736 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: Single step! (%s)\n",
737 pEvent->idCpu, dbgcGetEventCtx(pEvent->enmCtx));
738 else
739 pDbgc->cMultiStepsLeft -= 1;
740 if (RT_SUCCESS(rc))
741 {
742 if (pDbgc->fStepTraceRegs)
743 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
744 else
745 {
746 char szCmd[80];
747 if (DBGFR3CpuIsIn64BitCode(pDbgc->pUVM, pDbgc->idCpu))
748 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "u %016VR{rip} L 0");
749 else if (DBGFR3CpuIsInV86Code(pDbgc->pUVM, pDbgc->idCpu))
750 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "uv86 %04VR{cs}:%08VR{eip} L 0");
751 else
752 rc = DBGFR3RegPrintf(pDbgc->pUVM, pDbgc->idCpu, szCmd, sizeof(szCmd), "u %04VR{cs}:%08VR{eip} L 0");
753 if (RT_SUCCESS(rc))
754 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "%s", szCmd);
755 }
756 }
757
758 /* If multi-stepping, take the next step: */
759 if (pDbgc->cMultiStepsLeft > 0 && pEvent->idCpu == idCpuSaved)
760 {
761 int rc2 = DBGFR3StepEx(pDbgc->pUVM, pDbgc->idCpu, DBGF_STEP_F_INTO, NULL, NULL, 0, pDbgc->uMultiStepStrideLength);
762 if (RT_SUCCESS(rc2))
763 fPrintPrompt = false;
764 else
765 DBGCCmdHlpFailRc(&pDbgc->CmdHlp, pDbgc->pMultiStepCmd, rc2, "DBGFR3StepEx(,,DBGF_STEP_F_INTO,) failed");
766 }
767 else
768 pDbgc->idCpu = pEvent->idCpu;
769 break;
770 }
771
772 case DBGFEVENT_ASSERTION_HYPER:
773 {
774 pDbgc->idCpu = pEvent->idCpu;
775 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
776 "\ndbgf event/%u: Hypervisor Assertion! (%s)\n"
777 "%s"
778 "%s"
779 "\n",
780 pEvent->idCpu,
781 dbgcGetEventCtx(pEvent->enmCtx),
782 pEvent->u.Assert.pszMsg1,
783 pEvent->u.Assert.pszMsg2);
784 if (RT_SUCCESS(rc))
785 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
786 break;
787 }
788
789 case DBGFEVENT_DEV_STOP:
790 {
791 pDbgc->idCpu = pEvent->idCpu;
792 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
793 "\n"
794 "dbgf event/%u: DBGFSTOP (%s)\n"
795 "File: %s\n"
796 "Line: %d\n"
797 "Function: %s\n",
798 pEvent->idCpu,
799 dbgcGetEventCtx(pEvent->enmCtx),
800 pEvent->u.Src.pszFile,
801 pEvent->u.Src.uLine,
802 pEvent->u.Src.pszFunction);
803 if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
804 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
805 "Message: %s\n",
806 pEvent->u.Src.pszMessage);
807 if (RT_SUCCESS(rc))
808 rc = DBGCCmdHlpRegPrintf(&pDbgc->CmdHlp, pEvent->idCpu, -1, pDbgc->fRegTerse);
809 break;
810 }
811
812
813 case DBGFEVENT_INVALID_COMMAND:
814 {
815 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
816 break;
817 }
818
819 case DBGFEVENT_POWERING_OFF:
820 {
821 pDbgc->fReady = false;
822 pDbgc->pIo->pfnSetReady(pDbgc->pIo, false);
823 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is powering off!\n");
824 fPrintPrompt = false;
825 rc = VERR_GENERAL_FAILURE;
826 break;
827 }
828
829
830 default:
831 {
832 /*
833 * Probably a generic event. Look it up to find its name.
834 */
835 PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType);
836 if (pEvtDesc)
837 {
838 if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt)
839 {
840 Assert(pEvtDesc->pszDesc);
841 Assert(pEvent->u.Generic.cArgs == 1);
842 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s no %#llx! (%s)\n",
843 pEvent->idCpu, pEvtDesc->pszDesc, pEvent->u.Generic.auArgs[0], pEvtDesc->pszName);
844 }
845 else if (pEvtDesc->fFlags & DBGCSXEVT_F_BUGCHECK)
846 {
847 Assert(pEvent->u.Generic.cArgs >= 5);
848 char szDetails[512];
849 DBGFR3FormatBugCheck(pDbgc->pUVM, szDetails, sizeof(szDetails), pEvent->u.Generic.auArgs[0],
850 pEvent->u.Generic.auArgs[1], pEvent->u.Generic.auArgs[2],
851 pEvent->u.Generic.auArgs[3], pEvent->u.Generic.auArgs[4]);
852 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s %s%s!\n%s", pEvent->idCpu,
853 pEvtDesc->pszName, pEvtDesc->pszDesc ? "- " : "",
854 pEvtDesc->pszDesc ? pEvtDesc->pszDesc : "", szDetails);
855 }
856 else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG)
857 || pEvent->u.Generic.cArgs > 1
858 || ( pEvent->u.Generic.cArgs == 1
859 && pEvent->u.Generic.auArgs[0] != 0))
860 {
861 if (pEvtDesc->pszDesc)
862 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s - %s!",
863 pEvent->idCpu, pEvtDesc->pszName, pEvtDesc->pszDesc);
864 else
865 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s!",
866 pEvent->idCpu, pEvtDesc->pszName);
867 if (pEvent->u.Generic.cArgs <= 1)
868 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]);
869 else
870 {
871 for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++)
872 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]);
873 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
874 }
875 }
876 else
877 {
878 if (pEvtDesc->pszDesc)
879 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s - %s!\n",
880 pEvent->idCpu, pEvtDesc->pszName, pEvtDesc->pszDesc);
881 else
882 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event/%u: %s!\n",
883 pEvent->idCpu, pEvtDesc->pszName);
884 }
885 }
886 else
887 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d on CPU %u!\n",
888 pEvent->enmType, pEvent->idCpu);
889 break;
890 }
891 }
892
893 /*
894 * Prompt, anyone?
895 */
896 if (fPrintPrompt && RT_SUCCESS(rc))
897 {
898 /** @todo add CPU indicator to the prompt if an SMP VM? */
899 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
900 pDbgc->fReady = true;
901 if (RT_SUCCESS(rc))
902 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
903 pDbgc->cMultiStepsLeft = 0;
904 }
905
906 return rc;
907}
908
909
910/**
911 * Prints any log lines from the log buffer.
912 *
913 * The caller must not call function this unless pDbgc->fLog is set.
914 *
915 * @returns VBox status code. (output related)
916 * @param pDbgc Debugger console instance data.
917 */
918static int dbgcProcessLog(PDBGC pDbgc)
919{
920 /** @todo */
921 NOREF(pDbgc);
922 return 0;
923}
924
925/** @callback_method_impl{FNRTDBGCFGLOG} */
926static DECLCALLBACK(void) dbgcDbgCfgLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
927{
928 /** @todo Add symbol noise setting. */
929 NOREF(hDbgCfg); NOREF(iLevel);
930 PDBGC pDbgc = (PDBGC)pvUser;
931 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s", pszMsg);
932}
933
934
935/**
936 * Run the debugger console.
937 *
938 * @returns VBox status code.
939 * @param pDbgc Pointer to the debugger console instance data.
940 */
941int dbgcRun(PDBGC pDbgc)
942{
943 /*
944 * We're ready for commands now.
945 */
946 pDbgc->fReady = true;
947 pDbgc->pIo->pfnSetReady(pDbgc->pIo, true);
948
949 /*
950 * Main Debugger Loop.
951 *
952 * This loop will either block on waiting for input or on waiting on
953 * debug events. If we're forwarding the log we cannot wait for long
954 * before we must flush the log.
955 */
956 int rc;
957 for (;;)
958 {
959 rc = VERR_SEM_OUT_OF_TURN;
960 if (pDbgc->pUVM)
961 rc = DBGFR3QueryWaitable(pDbgc->pUVM);
962
963 if (RT_SUCCESS(rc))
964 {
965 /*
966 * Wait for a debug event.
967 */
968 DBGFEVENT Event;
969 rc = DBGFR3EventWait(pDbgc->pUVM, pDbgc->fLog ? 1 : 32, &Event);
970 if (RT_SUCCESS(rc))
971 {
972 rc = dbgcProcessEvent(pDbgc, &Event);
973 if (RT_FAILURE(rc))
974 break;
975 }
976 else if (rc != VERR_TIMEOUT)
977 break;
978
979 /*
980 * Check for input.
981 */
982 if (pDbgc->pIo->pfnInput(pDbgc->pIo, 0))
983 {
984 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
985 if (RT_FAILURE(rc))
986 break;
987 }
988 }
989 else if (rc == VERR_SEM_OUT_OF_TURN)
990 {
991 /*
992 * Wait for input. If Logging is enabled we'll only wait very briefly.
993 */
994 if (pDbgc->pIo->pfnInput(pDbgc->pIo, pDbgc->fLog ? 1 : 1000))
995 {
996 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
997 if (RT_FAILURE(rc))
998 break;
999 }
1000 }
1001 else
1002 break;
1003
1004 /*
1005 * Forward log output.
1006 */
1007 if (pDbgc->fLog)
1008 {
1009 rc = dbgcProcessLog(pDbgc);
1010 if (RT_FAILURE(rc))
1011 break;
1012 }
1013 }
1014
1015 return rc;
1016}
1017
1018
1019/**
1020 * Run the init scripts, if present.
1021 *
1022 * @param pDbgc The console instance.
1023 */
1024static void dbgcRunInitScripts(PDBGC pDbgc)
1025{
1026 /*
1027 * Do the global one, if it exists.
1028 */
1029 if ( pDbgc->pszGlobalInitScript
1030 && *pDbgc->pszGlobalInitScript != '\0'
1031 && RTFileExists(pDbgc->pszGlobalInitScript))
1032 dbgcEvalScript(pDbgc, pDbgc->pszGlobalInitScript, true /*fAnnounce*/);
1033
1034 /*
1035 * Then do the local one, if it exists.
1036 */
1037 if ( pDbgc->pszLocalInitScript
1038 && *pDbgc->pszLocalInitScript != '\0'
1039 && RTFileExists(pDbgc->pszLocalInitScript))
1040 dbgcEvalScript(pDbgc, pDbgc->pszLocalInitScript, true /*fAnnounce*/);
1041}
1042
1043
1044/**
1045 * Reads the CFGM configuration of the DBGC.
1046 *
1047 * Popuplates the PDBGC::pszHistoryFile, PDBGC::pszGlobalInitScript and
1048 * PDBGC::pszLocalInitScript members.
1049 *
1050 * @returns VBox status code.
1051 * @param pDbgc The console instance.
1052 * @param pUVM The user mode VM handle.
1053 */
1054static int dbgcReadConfig(PDBGC pDbgc, PUVM pUVM)
1055{
1056 /*
1057 * Get and validate the configuration node.
1058 */
1059 PCFGMNODE pNode = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
1060 int rc = CFGMR3ValidateConfig(pNode, "/DBGC/",
1061 "Enabled|"
1062 "HistoryFile|"
1063 "LocalInitScript|"
1064 "GlobalInitScript|",
1065 "*", "DBGC", 0);
1066 AssertRCReturn(rc, rc);
1067
1068 /*
1069 * Query the values.
1070 */
1071 char szHomeDefault[RTPATH_MAX];
1072 rc = RTPathUserHome(szHomeDefault, sizeof(szHomeDefault) - 32);
1073 AssertLogRelRCReturn(rc, rc);
1074 size_t cchHome = strlen(szHomeDefault);
1075
1076 /** @cfgm{/DBGC/HistoryFile, string, ${HOME}/.vboxdbgc-history}
1077 * The command history file of the VBox debugger. */
1078 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-history");
1079 AssertLogRelRCReturn(rc, rc);
1080
1081 char szPath[RTPATH_MAX];
1082 rc = CFGMR3QueryStringDef(pNode, "HistoryFile", szPath, sizeof(szPath), szHomeDefault);
1083 AssertLogRelRCReturn(rc, rc);
1084
1085 pDbgc->pszHistoryFile = RTStrDup(szPath);
1086 AssertReturn(pDbgc->pszHistoryFile, VERR_NO_STR_MEMORY);
1087
1088 /** @cfgm{/DBGC/GlobalInitFile, string, ${HOME}/.vboxdbgc-init}
1089 * The global init script of the VBox debugger. */
1090 szHomeDefault[cchHome] = '\0';
1091 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-init");
1092 AssertLogRelRCReturn(rc, rc);
1093
1094 rc = CFGMR3QueryStringDef(pNode, "GlobalInitScript", szPath, sizeof(szPath), szHomeDefault);
1095 AssertLogRelRCReturn(rc, rc);
1096
1097 pDbgc->pszGlobalInitScript = RTStrDup(szPath);
1098 AssertReturn(pDbgc->pszGlobalInitScript, VERR_NO_STR_MEMORY);
1099
1100 /** @cfgm{/DBGC/LocalInitFile, string, none}
1101 * The VM local init script of the VBox debugger. */
1102 rc = CFGMR3QueryString(pNode, "LocalInitScript", szPath, sizeof(szPath));
1103 if (RT_SUCCESS(rc))
1104 {
1105 pDbgc->pszLocalInitScript = RTStrDup(szPath);
1106 AssertReturn(pDbgc->pszLocalInitScript, VERR_NO_STR_MEMORY);
1107 }
1108 else
1109 {
1110 AssertLogRelReturn(rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT, rc);
1111 pDbgc->pszLocalInitScript = NULL;
1112 }
1113
1114 return VINF_SUCCESS;
1115}
1116
1117
1118/**
1119 * @copydoc DBGC::pfnOutput
1120 */
1121static DECLCALLBACK(int) dbgcOutputNative(void *pvUser, const char *pachChars, size_t cbChars)
1122{
1123 PDBGC pDbgc = (PDBGC)pvUser;
1124 return pDbgc->pIo->pfnWrite(pDbgc->pIo, pachChars, cbChars, NULL /*pcbWritten*/);
1125}
1126
1127
1128/**
1129 * Creates a a new instance.
1130 *
1131 * @returns VBox status code.
1132 * @param ppDbgc Where to store the pointer to the instance data.
1133 * @param pIo Pointer to the I/O callback table.
1134 * @param fFlags The flags.
1135 */
1136int dbgcCreate(PDBGC *ppDbgc, PCDBGCIO pIo, unsigned fFlags)
1137{
1138 /*
1139 * Validate input.
1140 */
1141 AssertPtrReturn(pIo, VERR_INVALID_POINTER);
1142 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
1143
1144 /*
1145 * Allocate and initialize.
1146 */
1147 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
1148 if (!pDbgc)
1149 return VERR_NO_MEMORY;
1150
1151 dbgcInitCmdHlp(pDbgc);
1152 pDbgc->pIo = pIo;
1153 pDbgc->pfnOutput = dbgcOutputNative;
1154 pDbgc->pvOutputUser = pDbgc;
1155 pDbgc->pVM = NULL;
1156 pDbgc->pUVM = NULL;
1157 pDbgc->idCpu = 0;
1158 pDbgc->hDbgAs = DBGF_AS_GLOBAL;
1159 pDbgc->pszEmulation = "CodeView/WinDbg";
1160 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
1161 pDbgc->cEmulationCmds = g_cCmdsCodeView;
1162 pDbgc->paEmulationFuncs = &g_aFuncsCodeView[0];
1163 pDbgc->cEmulationFuncs = g_cFuncsCodeView;
1164 //pDbgc->fLog = false;
1165 pDbgc->fRegTerse = true;
1166 pDbgc->fStepTraceRegs = true;
1167 //pDbgc->cPagingHierarchyDumps = 0;
1168 //pDbgc->DisasmPos = {0};
1169 //pDbgc->SourcePos = {0};
1170 //pDbgc->DumpPos = {0};
1171 pDbgc->pLastPos = &pDbgc->DisasmPos;
1172 //pDbgc->cbDumpElement = 0;
1173 //pDbgc->cVars = 0;
1174 //pDbgc->paVars = NULL;
1175 //pDbgc->pPlugInHead = NULL;
1176 //pDbgc->pFirstBp = NULL;
1177 RTListInit(&pDbgc->LstTraceFlowMods);
1178 //pDbgc->abSearch = {0};
1179 //pDbgc->cbSearch = 0;
1180 pDbgc->cbSearchUnit = 1;
1181 pDbgc->cMaxSearchHits = 1;
1182 //pDbgc->SearchAddr = {0};
1183 //pDbgc->cbSearchRange = 0;
1184
1185 //pDbgc->uInputZero = 0;
1186 //pDbgc->iRead = 0;
1187 //pDbgc->iWrite = 0;
1188 //pDbgc->cInputLines = 0;
1189 //pDbgc->fInputOverflow = false;
1190 pDbgc->fReady = true;
1191 pDbgc->pszScratch = &pDbgc->achScratch[0];
1192 //pDbgc->iArg = 0;
1193 //pDbgc->rcOutput = 0;
1194 //pDbgc->rcCmd = 0;
1195
1196 //pDbgc->pszHistoryFile = NULL;
1197 //pDbgc->pszGlobalInitScript = NULL;
1198 //pDbgc->pszLocalInitScript = NULL;
1199
1200 dbgcEvalInit();
1201
1202 *ppDbgc = pDbgc;
1203 return VINF_SUCCESS;
1204}
1205
1206/**
1207 * Destroys a DBGC instance created by dbgcCreate.
1208 *
1209 * @param pDbgc Pointer to the debugger console instance data.
1210 */
1211void dbgcDestroy(PDBGC pDbgc)
1212{
1213 AssertPtr(pDbgc);
1214
1215 /* Disable log hook. */
1216 if (pDbgc->fLog)
1217 {
1218
1219 }
1220
1221 /* Detach from the VM. */
1222 if (pDbgc->pUVM)
1223 DBGFR3Detach(pDbgc->pUVM);
1224
1225 /* Free config strings. */
1226 RTStrFree(pDbgc->pszGlobalInitScript);
1227 pDbgc->pszGlobalInitScript = NULL;
1228 RTStrFree(pDbgc->pszLocalInitScript);
1229 pDbgc->pszLocalInitScript = NULL;
1230 RTStrFree(pDbgc->pszHistoryFile);
1231 pDbgc->pszHistoryFile = NULL;
1232
1233 /* Finally, free the instance memory. */
1234 RTMemFree(pDbgc);
1235}
1236
1237
1238/**
1239 * Make a console instance.
1240 *
1241 * This will not return until either an 'exit' command is issued or a error code
1242 * indicating connection loss is encountered.
1243 *
1244 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
1245 * @returns The VBox status code causing the console termination.
1246 *
1247 * @param pUVM The user mode VM handle.
1248 * @param pIo Pointer to the I/O callback structure. This must contain
1249 * a full set of function pointers to service the console.
1250 * @param fFlags Reserved, must be zero.
1251 * @remarks A forced termination of the console is easiest done by forcing the
1252 * callbacks to return fatal failures.
1253 */
1254DBGDECL(int) DBGCCreate(PUVM pUVM, PCDBGCIO pIo, unsigned fFlags)
1255{
1256 /*
1257 * Validate input.
1258 */
1259 AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
1260 PVM pVM = NULL;
1261 if (pUVM)
1262 {
1263 pVM = VMR3GetVM(pUVM);
1264 AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
1265 }
1266
1267 /*
1268 * Allocate and initialize instance data
1269 */
1270 PDBGC pDbgc;
1271 int rc = dbgcCreate(&pDbgc, pIo, fFlags);
1272 if (RT_FAILURE(rc))
1273 return rc;
1274 if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM))
1275 pDbgc->hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
1276
1277 /*
1278 * Print welcome message.
1279 */
1280 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1281 "Welcome to the VirtualBox Debugger!\n");
1282
1283 /*
1284 * Attach to the specified VM.
1285 */
1286 if (RT_SUCCESS(rc) && pUVM)
1287 {
1288 rc = dbgcReadConfig(pDbgc, pUVM);
1289 if (RT_SUCCESS(rc))
1290 {
1291 rc = DBGFR3Attach(pUVM);
1292 if (RT_SUCCESS(rc))
1293 {
1294 pDbgc->pVM = pVM;
1295 pDbgc->pUVM = pUVM;
1296 pDbgc->idCpu = 0;
1297 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1298 "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
1299 , pDbgc->pVM, pDbgc->idCpu);
1300 }
1301 else
1302 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
1303 }
1304 else
1305 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "Error reading configuration\n");
1306 }
1307
1308 /*
1309 * Load plugins.
1310 */
1311 if (RT_SUCCESS(rc))
1312 {
1313 if (pVM)
1314 DBGFR3PlugInLoadAll(pDbgc->pUVM);
1315 dbgcEventInit(pDbgc);
1316 dbgcRunInitScripts(pDbgc);
1317
1318 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
1319 if (RT_SUCCESS(rc))
1320 {
1321 /*
1322 * Set debug config log callback.
1323 */
1324 RTDBGCFG hDbgCfg = DBGFR3AsGetConfig(pUVM);
1325 if ( hDbgCfg != NIL_RTDBGCFG
1326 && RTDbgCfgRetain(hDbgCfg) != UINT32_MAX)
1327 {
1328 int rc2 = RTDbgCfgSetLogCallback(hDbgCfg, dbgcDbgCfgLogCallback, pDbgc);
1329 if (RT_FAILURE(rc2))
1330 {
1331 hDbgCfg = NIL_RTDBGCFG;
1332 RTDbgCfgRelease(hDbgCfg);
1333 }
1334 }
1335 else
1336 hDbgCfg = NIL_RTDBGCFG;
1337
1338
1339 /*
1340 * Run the debugger main loop.
1341 */
1342 rc = dbgcRun(pDbgc);
1343
1344
1345 /*
1346 * Remove debug config log callback.
1347 */
1348 if (hDbgCfg != NIL_RTDBGCFG)
1349 {
1350 RTDbgCfgSetLogCallback(hDbgCfg, NULL, NULL);
1351 RTDbgCfgRelease(hDbgCfg);
1352 }
1353 }
1354 dbgcEventTerm(pDbgc);
1355 }
1356 else
1357 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nDBGCCreate error: %Rrc\n", rc);
1358
1359
1360 /*
1361 * Cleanup console debugger session.
1362 */
1363 dbgcDestroy(pDbgc);
1364 return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
1365}
1366
Note: See TracBrowser for help on using the repository browser.

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