VirtualBox

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

Last change on this file since 93654 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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