VirtualBox

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

Last change on this file since 59743 was 59246, checked in by vboxsync, 9 years ago

DBGC: Generic event reporting.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.1 KB
Line 
1/* $Id: DBGConsole.cpp 59246 2016-01-04 01:58:15Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console.
4 */
5
6/*
7 * Copyright (C) 2006-2015 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/err.h>
168#include <VBox/log.h>
169
170#include <iprt/asm.h>
171#include <iprt/assert.h>
172#include <iprt/file.h>
173#include <iprt/mem.h>
174#include <iprt/path.h>
175#include <iprt/string.h>
176
177#include "DBGCInternal.h"
178#include "DBGPlugIns.h"
179
180
181/*********************************************************************************************************************************
182* Internal Functions *
183*********************************************************************************************************************************/
184static int dbgcProcessLog(PDBGC pDbgc);
185
186
187/**
188 * Resolves a symbol (or tries to do so at least).
189 *
190 * @returns 0 on success.
191 * @returns VBox status on failure.
192 * @param pDbgc The debug console instance.
193 * @param pszSymbol The symbol name.
194 * @param enmType The result type. Specifying DBGCVAR_TYPE_GC_FAR may
195 * cause failure, avoid it.
196 * @param pResult Where to store the result.
197 */
198int dbgcSymbolGet(PDBGC pDbgc, const char *pszSymbol, DBGCVARTYPE enmType, PDBGCVAR pResult)
199{
200 int rc;
201
202 /*
203 * Builtin?
204 */
205 PCDBGCSYM pSymDesc = dbgcLookupRegisterSymbol(pDbgc, pszSymbol);
206 if (pSymDesc)
207 {
208 if (!pSymDesc->pfnGet)
209 return VERR_DBGC_PARSE_WRITEONLY_SYMBOL;
210 return pSymDesc->pfnGet(pSymDesc, &pDbgc->CmdHlp, enmType, pResult);
211 }
212
213 /*
214 * A typical register? (Guest only)
215 */
216 static const char s_szSixLetterRegisters[] =
217 "rflags;eflags;"
218 ;
219 static const char s_szThreeLetterRegisters[] =
220 "eax;rax;" "r10;" "r8d;r8w;r8b;" "cr0;" "dr0;"
221 "ebx;rbx;" "r11;" "r9d;r9w;r8b;" "dr1;"
222 "ecx;rcx;" "r12;" "cr2;" "dr2;"
223 "edx;rdx;" "r13;" "cr3;" "dr3;"
224 "edi;rdi;dil;" "r14;" "cr4;" "dr4;"
225 "esi;rsi;sil;" "r15;" "cr8;"
226 "ebp;rbp;"
227 "esp;rsp;" "dr6;"
228 "rip;eip;" "dr7;"
229 "efl;"
230 ;
231 static const char s_szTwoLetterRegisters[] =
232 "ax;al;ah;" "r8;"
233 "bx;bl;bh;" "r9;"
234 "cx;cl;ch;" "cs;"
235 "dx;dl;dh;" "ds;"
236 "di;" "es;"
237 "si;" "fs;"
238 "bp;" "gs;"
239 "sp;" "ss;"
240 "ip;"
241 ;
242 const char *pszRegSym = *pszSymbol == '.' ? pszSymbol + 1 : pszSymbol;
243 size_t const cchRegSym = strlen(pszRegSym);
244 if ( (cchRegSym == 2 && strstr(s_szTwoLetterRegisters, pszRegSym))
245 || (cchRegSym == 3 && strstr(s_szThreeLetterRegisters, pszRegSym))
246 || (cchRegSym == 6 && strstr(s_szSixLetterRegisters, pszRegSym)))
247 {
248 if (!strchr(pszSymbol, ';'))
249 {
250 DBGCVAR Var;
251 DBGCVAR_INIT_SYMBOL(&Var, pszSymbol);
252 rc = dbgcOpRegister(pDbgc, &Var, DBGCVAR_CAT_ANY, pResult);
253 if (RT_SUCCESS(rc))
254 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
255 }
256 }
257
258 /*
259 * Ask PDM.
260 */
261 /** @todo resolve symbols using PDM. */
262
263 /*
264 * Ask the debug info manager.
265 */
266 RTDBGSYMBOL Symbol;
267 rc = DBGFR3AsSymbolByName(pDbgc->pUVM, pDbgc->hDbgAs, pszSymbol, &Symbol, NULL);
268 if (RT_SUCCESS(rc))
269 {
270 /*
271 * Default return is a flat gc address.
272 */
273 DBGCVAR_INIT_GC_FLAT(pResult, Symbol.Value);
274 if (Symbol.cb)
275 DBGCVAR_SET_RANGE(pResult, DBGCVAR_RANGE_BYTES, Symbol.cb);
276
277 switch (enmType)
278 {
279 /* nothing to do. */
280 case DBGCVAR_TYPE_GC_FLAT:
281 case DBGCVAR_TYPE_ANY:
282 return VINF_SUCCESS;
283
284 /* impossible at the moment. */
285 case DBGCVAR_TYPE_GC_FAR:
286 return VERR_DBGC_PARSE_CONVERSION_FAILED;
287
288 /* simply make it numeric. */
289 case DBGCVAR_TYPE_NUMBER:
290 pResult->enmType = DBGCVAR_TYPE_NUMBER;
291 pResult->u.u64Number = Symbol.Value;
292 return VINF_SUCCESS;
293
294 /* cast it. */
295 case DBGCVAR_TYPE_GC_PHYS:
296 case DBGCVAR_TYPE_HC_FLAT:
297 case DBGCVAR_TYPE_HC_PHYS:
298 return DBGCCmdHlpConvert(&pDbgc->CmdHlp, pResult, enmType, false /*fConvSyms*/, pResult);
299
300 default:
301 AssertMsgFailed(("Internal error enmType=%d\n", enmType));
302 return VERR_INVALID_PARAMETER;
303 }
304 }
305
306 return VERR_DBGC_PARSE_NOT_IMPLEMENTED;
307}
308
309
310/**
311 * Process all commands currently in the buffer.
312 *
313 * @returns VBox status code. Any error indicates the termination of the console session.
314 * @param pDbgc Debugger console instance data.
315 * @param fNoExecute Indicates that no commands should actually be executed.
316 */
317static int dbgcProcessCommands(PDBGC pDbgc, bool fNoExecute)
318{
319 /** @todo Replace this with a sh/ksh/csh/rexx like toplevel language that
320 * allows doing function, loops, if, cases, and such. */
321 int rc = VINF_SUCCESS;
322 while (pDbgc->cInputLines)
323 {
324 /*
325 * Empty the log buffer if we're hooking the log.
326 */
327 if (pDbgc->fLog)
328 {
329 rc = dbgcProcessLog(pDbgc);
330 if (RT_FAILURE(rc))
331 break;
332 }
333
334 if (pDbgc->iRead == pDbgc->iWrite)
335 {
336 AssertMsgFailed(("The input buffer is empty while cInputLines=%d!\n", pDbgc->cInputLines));
337 pDbgc->cInputLines = 0;
338 return 0;
339 }
340
341 /*
342 * Copy the command to the parse buffer.
343 */
344 char ch;
345 char *psz = &pDbgc->achInput[pDbgc->iRead];
346 char *pszTrg = &pDbgc->achScratch[0];
347 while ((*pszTrg = ch = *psz++) != ';' && ch != '\n' )
348 {
349 if (psz == &pDbgc->achInput[sizeof(pDbgc->achInput)])
350 psz = &pDbgc->achInput[0];
351
352 if (psz == &pDbgc->achInput[pDbgc->iWrite])
353 {
354 AssertMsgFailed(("The buffer contains no commands while cInputLines=%d!\n", pDbgc->cInputLines));
355 pDbgc->cInputLines = 0;
356 return 0;
357 }
358
359 pszTrg++;
360 }
361 *pszTrg = '\0';
362
363 /*
364 * Advance the buffer.
365 */
366 pDbgc->iRead = psz - &pDbgc->achInput[0];
367 if (ch == '\n')
368 pDbgc->cInputLines--;
369
370 /*
371 * Parse and execute this command.
372 */
373 pDbgc->pszScratch = pszTrg + 1;
374 pDbgc->iArg = 0;
375 rc = dbgcEvalCommand(pDbgc, &pDbgc->achScratch[0], pszTrg - &pDbgc->achScratch[0] - 1, fNoExecute);
376 if ( rc == VERR_DBGC_QUIT
377 || rc == VWRN_DBGC_CMD_PENDING)
378 break;
379 rc = VINF_SUCCESS; /* ignore other statuses */
380 }
381
382 return rc;
383}
384
385
386/**
387 * Handle input buffer overflow.
388 *
389 * Will read any available input looking for a '\n' to reset the buffer on.
390 *
391 * @returns VBox status code.
392 * @param pDbgc Debugger console instance data.
393 */
394static int dbgcInputOverflow(PDBGC pDbgc)
395{
396 /*
397 * Assert overflow status and reset the input buffer.
398 */
399 if (!pDbgc->fInputOverflow)
400 {
401 pDbgc->fInputOverflow = true;
402 pDbgc->iRead = pDbgc->iWrite = 0;
403 pDbgc->cInputLines = 0;
404 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "Input overflow!!\n");
405 }
406
407 /*
408 * Eat input till no more or there is a '\n'.
409 * When finding a '\n' we'll continue normal processing.
410 */
411 while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))
412 {
413 size_t cbRead;
414 int rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &pDbgc->achInput[0], sizeof(pDbgc->achInput) - 1, &cbRead);
415 if (RT_FAILURE(rc))
416 return rc;
417 char *psz = (char *)memchr(&pDbgc->achInput[0], '\n', cbRead);
418 if (psz)
419 {
420 pDbgc->fInputOverflow = false;
421 pDbgc->iRead = psz - &pDbgc->achInput[0] + 1;
422 pDbgc->iWrite = (unsigned)cbRead;
423 pDbgc->cInputLines = 0;
424 break;
425 }
426 }
427
428 return 0;
429}
430
431
432/**
433 * Read input and do some preprocessing.
434 *
435 * @returns VBox status code.
436 * In addition to the iWrite and achInput, cInputLines is maintained.
437 * In case of an input overflow the fInputOverflow flag will be set.
438 * @param pDbgc Debugger console instance data.
439 */
440static int dbgcInputRead(PDBGC pDbgc)
441{
442 /*
443 * We have ready input.
444 * Read it till we don't have any or we have a full input buffer.
445 */
446 int rc = 0;
447 do
448 {
449 /*
450 * More available buffer space?
451 */
452 size_t cbLeft;
453 if (pDbgc->iWrite > pDbgc->iRead)
454 cbLeft = sizeof(pDbgc->achInput) - pDbgc->iWrite - (pDbgc->iRead == 0);
455 else
456 cbLeft = pDbgc->iRead - pDbgc->iWrite - 1;
457 if (!cbLeft)
458 {
459 /* overflow? */
460 if (!pDbgc->cInputLines)
461 rc = dbgcInputOverflow(pDbgc);
462 break;
463 }
464
465 /*
466 * Read one char and interpret it.
467 */
468 char achRead[128];
469 size_t cbRead;
470 rc = pDbgc->pBack->pfnRead(pDbgc->pBack, &achRead[0], RT_MIN(cbLeft, sizeof(achRead)), &cbRead);
471 if (RT_FAILURE(rc))
472 return rc;
473 char *psz = &achRead[0];
474 while (cbRead-- > 0)
475 {
476 char ch = *psz++;
477 switch (ch)
478 {
479 /*
480 * Ignore.
481 */
482 case '\0':
483 case '\r':
484 case '\a':
485 break;
486
487 /*
488 * Backspace.
489 */
490 case '\b':
491 Log2(("DBGC: backspace\n"));
492 if (pDbgc->iRead != pDbgc->iWrite)
493 {
494 unsigned iWriteUndo = pDbgc->iWrite;
495 if (pDbgc->iWrite)
496 pDbgc->iWrite--;
497 else
498 pDbgc->iWrite = sizeof(pDbgc->achInput) - 1;
499
500 if (pDbgc->achInput[pDbgc->iWrite] == '\n')
501 pDbgc->iWrite = iWriteUndo;
502 }
503 break;
504
505 /*
506 * Add char to buffer.
507 */
508 case '\t':
509 case '\n':
510 case ';':
511 switch (ch)
512 {
513 case '\t': ch = ' '; break;
514 case '\n': pDbgc->cInputLines++; break;
515 }
516 default:
517 Log2(("DBGC: ch=%02x\n", (unsigned char)ch));
518 pDbgc->achInput[pDbgc->iWrite] = ch;
519 if (++pDbgc->iWrite >= sizeof(pDbgc->achInput))
520 pDbgc->iWrite = 0;
521 break;
522 }
523 }
524
525 /* Terminate it to make it easier to read in the debugger. */
526 pDbgc->achInput[pDbgc->iWrite] = '\0';
527 } while (pDbgc->pBack->pfnInput(pDbgc->pBack, 0));
528
529 return rc;
530}
531
532
533/**
534 * Reads input, parses it and executes commands on '\n'.
535 *
536 * @returns VBox status code.
537 * @param pDbgc Debugger console instance data.
538 * @param fNoExecute Indicates that no commands should actually be executed.
539 */
540int dbgcProcessInput(PDBGC pDbgc, bool fNoExecute)
541{
542 /*
543 * We know there's input ready, so let's read it first.
544 */
545 int rc = dbgcInputRead(pDbgc);
546 if (RT_FAILURE(rc))
547 return rc;
548
549 /*
550 * Now execute any ready commands.
551 */
552 if (pDbgc->cInputLines)
553 {
554 pDbgc->pBack->pfnSetReady(pDbgc->pBack, false);
555 pDbgc->fReady = false;
556 rc = dbgcProcessCommands(pDbgc, fNoExecute);
557 if (RT_SUCCESS(rc) && rc != VWRN_DBGC_CMD_PENDING)
558 pDbgc->fReady = true;
559
560 if ( RT_SUCCESS(rc)
561 && pDbgc->iRead == pDbgc->iWrite
562 && pDbgc->fReady)
563 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
564
565 if ( RT_SUCCESS(rc)
566 && pDbgc->fReady)
567 pDbgc->pBack->pfnSetReady(pDbgc->pBack, true);
568 }
569 /*
570 * else - we have incomplete line, so leave it in the buffer and
571 * wait for more input.
572 *
573 * Windows telnet client is in "character at a time" mode by
574 * default and putty sends eol as a separate packet that will be
575 * most likely read separately from the command line it
576 * terminates.
577 */
578
579 return rc;
580}
581
582
583/**
584 * Gets the event context identifier string.
585 * @returns Read only string.
586 * @param enmCtx The context.
587 */
588static const char *dbgcGetEventCtx(DBGFEVENTCTX enmCtx)
589{
590 switch (enmCtx)
591 {
592 case DBGFEVENTCTX_RAW: return "raw";
593 case DBGFEVENTCTX_REM: return "rem";
594 case DBGFEVENTCTX_HM: return "hwaccl";
595 case DBGFEVENTCTX_HYPER: return "hyper";
596 case DBGFEVENTCTX_OTHER: return "other";
597
598 case DBGFEVENTCTX_INVALID: return "!Invalid Event Ctx!";
599 default:
600 AssertMsgFailed(("enmCtx=%d\n", enmCtx));
601 return "!Unknown Event Ctx!";
602 }
603}
604
605
606/**
607 * Looks up a generic debug event.
608 *
609 * @returns Pointer to DBGCSXEVT structure if found, otherwise NULL.
610 * @param enmType The possibly generic event to find the descriptor for.
611 */
612static PCDBGCSXEVT dbgcEventLookup(DBGFEVENTTYPE enmType)
613{
614 uint32_t i = g_cDbgcSxEvents;
615 while (i-- > 0)
616 if (g_aDbgcSxEvents[i].enmType == enmType)
617 return &g_aDbgcSxEvents[i];
618 return NULL;
619}
620
621
622/**
623 * Processes debugger events.
624 *
625 * @returns VBox status code.
626 * @param pDbgc DBGC Instance data.
627 * @param pEvent Pointer to event data.
628 */
629static int dbgcProcessEvent(PDBGC pDbgc, PCDBGFEVENT pEvent)
630{
631 /*
632 * Flush log first.
633 */
634 if (pDbgc->fLog)
635 {
636 int rc = dbgcProcessLog(pDbgc);
637 if (RT_FAILURE(rc))
638 return rc;
639 }
640
641 /*
642 * Process the event.
643 */
644 pDbgc->pszScratch = &pDbgc->achInput[0];
645 pDbgc->iArg = 0;
646 bool fPrintPrompt = true;
647 int rc = VINF_SUCCESS;
648 switch (pEvent->enmType)
649 {
650 /*
651 * The first part is events we have initiated with commands.
652 */
653 case DBGFEVENT_HALT_DONE:
654 {
655 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: VM %p is halted! (%s)\n",
656 pDbgc->pVM, dbgcGetEventCtx(pEvent->enmCtx));
657 pDbgc->fRegCtxGuest = true; /* we're always in guest context when halted. */
658 if (RT_SUCCESS(rc))
659 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
660 break;
661 }
662
663
664 /*
665 * The second part is events which can occur at any time.
666 */
667 case DBGFEVENT_FATAL_ERROR:
668 {
669 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
670 dbgcGetEventCtx(pEvent->enmCtx));
671 pDbgc->fRegCtxGuest = false; /* fatal errors are always in hypervisor. */
672 if (RT_SUCCESS(rc))
673 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
674 break;
675 }
676
677 case DBGFEVENT_BREAKPOINT:
678 case DBGFEVENT_BREAKPOINT_IO:
679 case DBGFEVENT_BREAKPOINT_MMIO:
680 case DBGFEVENT_BREAKPOINT_HYPER:
681 {
682 bool fRegCtxGuest = pDbgc->fRegCtxGuest;
683 pDbgc->fRegCtxGuest = pEvent->enmType != DBGFEVENT_BREAKPOINT_HYPER;
684
685 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
686 switch (rc)
687 {
688 case VERR_DBGC_BP_NOT_FOUND:
689 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
690 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
691 break;
692
693 case VINF_DBGC_BP_NO_COMMAND:
694 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
695 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
696 break;
697
698 case VINF_BUFFER_OVERFLOW:
699 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
700 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
701 break;
702
703 default:
704 break;
705 }
706 if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM))
707 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
708 else
709 pDbgc->fRegCtxGuest = fRegCtxGuest;
710 break;
711 }
712
713 case DBGFEVENT_STEPPED:
714 case DBGFEVENT_STEPPED_HYPER:
715 {
716 pDbgc->fRegCtxGuest = pEvent->enmType == DBGFEVENT_STEPPED;
717
718 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Single step! (%s)\n", dbgcGetEventCtx(pEvent->enmCtx));
719 if (RT_SUCCESS(rc))
720 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
721 break;
722 }
723
724 case DBGFEVENT_ASSERTION_HYPER:
725 {
726 pDbgc->fRegCtxGuest = false;
727
728 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
729 "\ndbgf event: Hypervisor Assertion! (%s)\n"
730 "%s"
731 "%s"
732 "\n",
733 dbgcGetEventCtx(pEvent->enmCtx),
734 pEvent->u.Assert.pszMsg1,
735 pEvent->u.Assert.pszMsg2);
736 if (RT_SUCCESS(rc))
737 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
738 break;
739 }
740
741 case DBGFEVENT_DEV_STOP:
742 {
743 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
744 "\n"
745 "dbgf event: DBGFSTOP (%s)\n"
746 "File: %s\n"
747 "Line: %d\n"
748 "Function: %s\n",
749 dbgcGetEventCtx(pEvent->enmCtx),
750 pEvent->u.Src.pszFile,
751 pEvent->u.Src.uLine,
752 pEvent->u.Src.pszFunction);
753 if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
754 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
755 "Message: %s\n",
756 pEvent->u.Src.pszMessage);
757 if (RT_SUCCESS(rc))
758 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
759 break;
760 }
761
762
763 case DBGFEVENT_INVALID_COMMAND:
764 {
765 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
766 break;
767 }
768
769 case DBGFEVENT_POWERING_OFF:
770 {
771 pDbgc->fReady = false;
772 pDbgc->pBack->pfnSetReady(pDbgc->pBack, false);
773 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nVM is powering off!\n");
774 fPrintPrompt = false;
775 rc = VERR_GENERAL_FAILURE;
776 break;
777 }
778
779
780 default:
781 {
782 /*
783 * Probably a generic event. Look it up to find its name.
784 */
785 PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType);
786 if (pEvtDesc)
787 {
788 if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt)
789 {
790 Assert(pEvtDesc->pszDesc);
791 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s no %#llx! (%s)\n",
792 pEvtDesc->pszDesc, pEvent->u.Generic.uArg, pEvtDesc->pszName);
793 }
794 else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG)
795 || pEvent->u.Generic.uArg != 0)
796 {
797 if (pEvtDesc->pszDesc)
798 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s! arg=%#llx\n",
799 pEvtDesc->pszName, pEvtDesc->pszDesc, pEvent->u.Generic.uArg);
800 else
801 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s! arg=%#llx\n",
802 pEvtDesc->pszName, pEvent->u.Generic.uArg);
803 }
804 else
805 {
806 if (pEvtDesc->pszDesc)
807 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!\n",
808 pEvtDesc->pszName, pEvtDesc->pszDesc);
809 else
810 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!\n", pEvtDesc->pszName);
811 }
812 }
813 else
814 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
815 break;
816 }
817 }
818
819 /*
820 * Prompt, anyone?
821 */
822 if (fPrintPrompt && RT_SUCCESS(rc))
823 {
824 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
825 pDbgc->fReady = true;
826 if (RT_SUCCESS(rc))
827 pDbgc->pBack->pfnSetReady(pDbgc->pBack, true);
828 }
829
830 return rc;
831}
832
833
834/**
835 * Prints any log lines from the log buffer.
836 *
837 * The caller must not call function this unless pDbgc->fLog is set.
838 *
839 * @returns VBox status code. (output related)
840 * @param pDbgc Debugger console instance data.
841 */
842static int dbgcProcessLog(PDBGC pDbgc)
843{
844 /** @todo */
845 NOREF(pDbgc);
846 return 0;
847}
848
849/** @callback_method_impl{FNRTDBGCFGLOG} */
850static DECLCALLBACK(void) dbgcDbgCfgLogCallback(RTDBGCFG hDbgCfg, uint32_t iLevel, const char *pszMsg, void *pvUser)
851{
852 /** @todo Add symbol noise setting. */
853 NOREF(hDbgCfg); NOREF(iLevel);
854 PDBGC pDbgc = (PDBGC)pvUser;
855 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "%s", pszMsg);
856}
857
858
859/**
860 * Run the debugger console.
861 *
862 * @returns VBox status code.
863 * @param pDbgc Pointer to the debugger console instance data.
864 */
865int dbgcRun(PDBGC pDbgc)
866{
867 /*
868 * We're ready for commands now.
869 */
870 pDbgc->fReady = true;
871 pDbgc->pBack->pfnSetReady(pDbgc->pBack, true);
872
873 /*
874 * Main Debugger Loop.
875 *
876 * This loop will either block on waiting for input or on waiting on
877 * debug events. If we're forwarding the log we cannot wait for long
878 * before we must flush the log.
879 */
880 int rc;
881 for (;;)
882 {
883 rc = VERR_SEM_OUT_OF_TURN;
884 if (pDbgc->pUVM)
885 rc = DBGFR3QueryWaitable(pDbgc->pUVM);
886
887 if (RT_SUCCESS(rc))
888 {
889 /*
890 * Wait for a debug event.
891 */
892 PCDBGFEVENT pEvent;
893 rc = DBGFR3EventWait(pDbgc->pUVM, pDbgc->fLog ? 1 : 32, &pEvent);
894 if (RT_SUCCESS(rc))
895 {
896 rc = dbgcProcessEvent(pDbgc, pEvent);
897 if (RT_FAILURE(rc))
898 break;
899 }
900 else if (rc != VERR_TIMEOUT)
901 break;
902
903 /*
904 * Check for input.
905 */
906 if (pDbgc->pBack->pfnInput(pDbgc->pBack, 0))
907 {
908 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
909 if (RT_FAILURE(rc))
910 break;
911 }
912 }
913 else if (rc == VERR_SEM_OUT_OF_TURN)
914 {
915 /*
916 * Wait for input. If Logging is enabled we'll only wait very briefly.
917 */
918 if (pDbgc->pBack->pfnInput(pDbgc->pBack, pDbgc->fLog ? 1 : 1000))
919 {
920 rc = dbgcProcessInput(pDbgc, false /* fNoExecute */);
921 if (RT_FAILURE(rc))
922 break;
923 }
924 }
925 else
926 break;
927
928 /*
929 * Forward log output.
930 */
931 if (pDbgc->fLog)
932 {
933 rc = dbgcProcessLog(pDbgc);
934 if (RT_FAILURE(rc))
935 break;
936 }
937 }
938
939 return rc;
940}
941
942
943/**
944 * Run the init scripts, if present.
945 *
946 * @param pDbgc The console instance.
947 */
948static void dbgcRunInitScripts(PDBGC pDbgc)
949{
950 /*
951 * Do the global one, if it exists.
952 */
953 if ( pDbgc->pszGlobalInitScript
954 && *pDbgc->pszGlobalInitScript != '\0'
955 && RTFileExists(pDbgc->pszGlobalInitScript))
956 dbgcEvalScript(pDbgc, pDbgc->pszGlobalInitScript, true /*fAnnounce*/);
957
958 /*
959 * Then do the local one, if it exists.
960 */
961 if ( pDbgc->pszLocalInitScript
962 && *pDbgc->pszLocalInitScript != '\0'
963 && RTFileExists(pDbgc->pszLocalInitScript))
964 dbgcEvalScript(pDbgc, pDbgc->pszLocalInitScript, true /*fAnnounce*/);
965}
966
967
968/**
969 * Reads the CFGM configuration of the DBGC.
970 *
971 * Popuplates the PDBGC::pszHistoryFile, PDBGC::pszGlobalInitScript and
972 * PDBGC::pszLocalInitScript members.
973 *
974 * @returns VBox status code.
975 * @param pDbgc The console instance.
976 * @param pUVM The user mode VM handle.
977 */
978static int dbgcReadConfig(PDBGC pDbgc, PUVM pUVM)
979{
980 /*
981 * Get and validate the configuration node.
982 */
983 PCFGMNODE pNode = CFGMR3GetChild(CFGMR3GetRootU(pUVM), "DBGC");
984 int rc = CFGMR3ValidateConfig(pNode, "/DBGC/",
985 "HistoryFile|"
986 "LocalInitScript|"
987 "GlobalInitScript",
988 "", "DBGC", 0);
989 AssertRCReturn(rc, rc);
990
991 /*
992 * Query the values.
993 */
994 char szHomeDefault[RTPATH_MAX];
995 rc = RTPathUserHome(szHomeDefault, sizeof(szHomeDefault) - 32);
996 AssertLogRelRCReturn(rc, rc);
997 size_t cchHome = strlen(szHomeDefault);
998
999 /** @cfgm{/DBGC/HistoryFile, string, ${HOME}/.vboxdbgc-history}
1000 * The command history file of the VBox debugger. */
1001 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-history");
1002 AssertLogRelRCReturn(rc, rc);
1003
1004 char szPath[RTPATH_MAX];
1005 rc = CFGMR3QueryStringDef(pNode, "HistoryFile", szPath, sizeof(szPath), szHomeDefault);
1006 AssertLogRelRCReturn(rc, rc);
1007
1008 pDbgc->pszHistoryFile = RTStrDup(szPath);
1009 AssertReturn(pDbgc->pszHistoryFile, VERR_NO_STR_MEMORY);
1010
1011 /** @cfgm{/DBGC/GlobalInitFile, string, ${HOME}/.vboxdbgc-init}
1012 * The global init script of the VBox debugger. */
1013 szHomeDefault[cchHome] = '\0';
1014 rc = RTPathAppend(szHomeDefault, sizeof(szHomeDefault), ".vboxdbgc-init");
1015 AssertLogRelRCReturn(rc, rc);
1016
1017 rc = CFGMR3QueryStringDef(pNode, "GlobalInitScript", szPath, sizeof(szPath), szHomeDefault);
1018 AssertLogRelRCReturn(rc, rc);
1019
1020 pDbgc->pszGlobalInitScript = RTStrDup(szPath);
1021 AssertReturn(pDbgc->pszGlobalInitScript, VERR_NO_STR_MEMORY);
1022
1023 /** @cfgm{/DBGC/LocalInitFile, string, none}
1024 * The VM local init script of the VBox debugger. */
1025 rc = CFGMR3QueryString(pNode, "LocalInitScript", szPath, sizeof(szPath));
1026 if (RT_SUCCESS(rc))
1027 {
1028 pDbgc->pszLocalInitScript = RTStrDup(szPath);
1029 AssertReturn(pDbgc->pszLocalInitScript, VERR_NO_STR_MEMORY);
1030 }
1031 else
1032 {
1033 AssertLogRelReturn(rc == VERR_CFGM_VALUE_NOT_FOUND || rc == VERR_CFGM_NO_PARENT, rc);
1034 pDbgc->pszLocalInitScript = NULL;
1035 }
1036
1037 return VINF_SUCCESS;
1038}
1039
1040
1041
1042/**
1043 * Creates a a new instance.
1044 *
1045 * @returns VBox status code.
1046 * @param ppDbgc Where to store the pointer to the instance data.
1047 * @param pBack Pointer to the backend.
1048 * @param fFlags The flags.
1049 */
1050int dbgcCreate(PDBGC *ppDbgc, PDBGCBACK pBack, unsigned fFlags)
1051{
1052 /*
1053 * Validate input.
1054 */
1055 AssertPtrReturn(pBack, VERR_INVALID_POINTER);
1056 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
1057
1058 /*
1059 * Allocate and initialize.
1060 */
1061 PDBGC pDbgc = (PDBGC)RTMemAllocZ(sizeof(*pDbgc));
1062 if (!pDbgc)
1063 return VERR_NO_MEMORY;
1064
1065 dbgcInitCmdHlp(pDbgc);
1066 pDbgc->pBack = pBack;
1067 pDbgc->pVM = NULL;
1068 pDbgc->pUVM = NULL;
1069 pDbgc->idCpu = 0;
1070 pDbgc->hDbgAs = DBGF_AS_GLOBAL;
1071 pDbgc->pszEmulation = "CodeView/WinDbg";
1072 pDbgc->paEmulationCmds = &g_aCmdsCodeView[0];
1073 pDbgc->cEmulationCmds = g_cCmdsCodeView;
1074 pDbgc->paEmulationFuncs = &g_aFuncsCodeView[0];
1075 pDbgc->cEmulationFuncs = g_cFuncsCodeView;
1076 //pDbgc->fLog = false;
1077 pDbgc->fRegCtxGuest = true;
1078 pDbgc->fRegTerse = true;
1079 //pDbgc->cPagingHierarchyDumps = 0;
1080 //pDbgc->DisasmPos = {0};
1081 //pDbgc->SourcePos = {0};
1082 //pDbgc->DumpPos = {0};
1083 pDbgc->pLastPos = &pDbgc->DisasmPos;
1084 //pDbgc->cbDumpElement = 0;
1085 //pDbgc->cVars = 0;
1086 //pDbgc->paVars = NULL;
1087 //pDbgc->pPlugInHead = NULL;
1088 //pDbgc->pFirstBp = NULL;
1089 //pDbgc->abSearch = {0};
1090 //pDbgc->cbSearch = 0;
1091 pDbgc->cbSearchUnit = 1;
1092 pDbgc->cMaxSearchHits = 1;
1093 //pDbgc->SearchAddr = {0};
1094 //pDbgc->cbSearchRange = 0;
1095
1096 //pDbgc->uInputZero = 0;
1097 //pDbgc->iRead = 0;
1098 //pDbgc->iWrite = 0;
1099 //pDbgc->cInputLines = 0;
1100 //pDbgc->fInputOverflow = false;
1101 pDbgc->fReady = true;
1102 pDbgc->pszScratch = &pDbgc->achScratch[0];
1103 //pDbgc->iArg = 0;
1104 //pDbgc->rcOutput = 0;
1105 //pDbgc->rcCmd = 0;
1106
1107 //pDbgc->pszHistoryFile = NULL;
1108 //pDbgc->pszGlobalInitScript = NULL;
1109 //pDbgc->pszLocalInitScript = NULL;
1110
1111 dbgcEvalInit();
1112
1113 *ppDbgc = pDbgc;
1114 return VINF_SUCCESS;
1115}
1116
1117/**
1118 * Destroys a DBGC instance created by dbgcCreate.
1119 *
1120 * @param pDbgc Pointer to the debugger console instance data.
1121 */
1122void dbgcDestroy(PDBGC pDbgc)
1123{
1124 AssertPtr(pDbgc);
1125
1126 /* Disable log hook. */
1127 if (pDbgc->fLog)
1128 {
1129
1130 }
1131
1132 /* Detach from the VM. */
1133 if (pDbgc->pUVM)
1134 DBGFR3Detach(pDbgc->pUVM);
1135
1136 /* Free config strings. */
1137 RTStrFree(pDbgc->pszGlobalInitScript);
1138 pDbgc->pszGlobalInitScript = NULL;
1139 RTStrFree(pDbgc->pszLocalInitScript);
1140 pDbgc->pszLocalInitScript = NULL;
1141 RTStrFree(pDbgc->pszHistoryFile);
1142 pDbgc->pszHistoryFile = NULL;
1143
1144 /* Finally, free the instance memory. */
1145 RTMemFree(pDbgc);
1146}
1147
1148
1149/**
1150 * Make a console instance.
1151 *
1152 * This will not return until either an 'exit' command is issued or a error code
1153 * indicating connection loss is encountered.
1154 *
1155 * @returns VINF_SUCCESS if console termination caused by the 'exit' command.
1156 * @returns The VBox status code causing the console termination.
1157 *
1158 * @param pUVM The user mode VM handle.
1159 * @param pBack Pointer to the backend structure. This must contain
1160 * a full set of function pointers to service the console.
1161 * @param fFlags Reserved, must be zero.
1162 * @remarks A forced termination of the console is easiest done by forcing the
1163 * callbacks to return fatal failures.
1164 */
1165DBGDECL(int) DBGCCreate(PUVM pUVM, PDBGCBACK pBack, unsigned fFlags)
1166{
1167 /*
1168 * Validate input.
1169 */
1170 AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
1171 PVM pVM = NULL;
1172 if (pUVM)
1173 {
1174 pVM = VMR3GetVM(pUVM);
1175 AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
1176 }
1177
1178 /*
1179 * Allocate and initialize instance data
1180 */
1181 PDBGC pDbgc;
1182 int rc = dbgcCreate(&pDbgc, pBack, fFlags);
1183 if (RT_FAILURE(rc))
1184 return rc;
1185 if (!HMR3IsEnabled(pUVM))
1186 pDbgc->hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
1187
1188 /*
1189 * Print welcome message.
1190 */
1191 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1192 "Welcome to the VirtualBox Debugger!\n");
1193
1194 /*
1195 * Attach to the specified VM.
1196 */
1197 if (RT_SUCCESS(rc) && pUVM)
1198 {
1199 rc = dbgcReadConfig(pDbgc, pUVM);
1200 if (RT_SUCCESS(rc))
1201 {
1202 rc = DBGFR3Attach(pUVM);
1203 if (RT_SUCCESS(rc))
1204 {
1205 pDbgc->pVM = pVM;
1206 pDbgc->pUVM = pUVM;
1207 pDbgc->idCpu = 0;
1208 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
1209 "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
1210 , pDbgc->pVM, pDbgc->idCpu);
1211 }
1212 else
1213 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "When trying to attach to VM %p\n", pDbgc->pVM);
1214 }
1215 else
1216 rc = pDbgc->CmdHlp.pfnVBoxError(&pDbgc->CmdHlp, rc, "Error reading configuration\n");
1217 }
1218
1219 /*
1220 * Load plugins.
1221 */
1222 if (RT_SUCCESS(rc))
1223 {
1224 if (pVM)
1225 DBGFR3PlugInLoadAll(pDbgc->pUVM);
1226 dbgcEventInit(pDbgc);
1227 dbgcRunInitScripts(pDbgc);
1228
1229 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
1230 if (RT_SUCCESS(rc))
1231 {
1232 /*
1233 * Set debug config log callback.
1234 */
1235 RTDBGCFG hDbgCfg = DBGFR3AsGetConfig(pUVM);
1236 if ( hDbgCfg != NIL_RTDBGCFG
1237 && RTDbgCfgRetain(hDbgCfg) != UINT32_MAX)
1238 {
1239 int rc2 = RTDbgCfgSetLogCallback(hDbgCfg, dbgcDbgCfgLogCallback, pDbgc);
1240 if (RT_FAILURE(rc2))
1241 {
1242 hDbgCfg = NIL_RTDBGCFG;
1243 RTDbgCfgRelease(hDbgCfg);
1244 }
1245 }
1246 else
1247 hDbgCfg = NIL_RTDBGCFG;
1248
1249
1250 /*
1251 * Run the debugger main loop.
1252 */
1253 rc = dbgcRun(pDbgc);
1254
1255
1256 /*
1257 * Remove debug config log callback.
1258 */
1259 if (hDbgCfg != NIL_RTDBGCFG)
1260 {
1261 RTDbgCfgSetLogCallback(hDbgCfg, NULL, NULL);
1262 RTDbgCfgRelease(hDbgCfg);
1263 }
1264 }
1265 dbgcEventTerm(pDbgc);
1266 }
1267 else
1268 pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\nDBGCCreate error: %Rrc\n", rc);
1269
1270
1271 /*
1272 * Cleanup console debugger session.
1273 */
1274 dbgcDestroy(pDbgc);
1275 return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
1276}
1277
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