VirtualBox

source: vbox/trunk/src/VBox/VMM/DBGF.cpp@ 1

Last change on this file since 1 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 KB
Line 
1/** @file
2 *
3 * VMM DBGF - Debugger Facility.
4 */
5
6/*
7 * Copyright (C) 2006 InnoTek Systemberatung GmbH
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 as published by the Free Software Foundation,
13 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
14 * distribution. VirtualBox OSE is distributed in the hope that it will
15 * be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * If you received this file as part of a commercial VirtualBox
18 * distribution, then only the terms of your commercial VirtualBox
19 * license agreement apply instead of the previous paragraph.
20 */
21
22
23/** @page pg_dbgf DBGC - The Debugger Facility
24 *
25 * The purpose of the DBGC is to provide an interface for debuggers to manipulate
26 * the VMM without having to mess up the source code for each of them. The DBGF
27 * is always built in and will always work when a debugger attaches to the VM.
28 * The DBGF provides the basic debugger features, such as halting execution,
29 * handling breakpoints and single step execution.
30 *
31 * The interface is working in a manner similar to the win32, linux and os2
32 * debugger interfaces. It interface has an asynchronous nature. This comes from
33 * the fact that the VMM and the Debugger are running in different threads.
34 * They are refered to as the "emulation thread" and the "debugger thread",
35 * or as the "ping thread" and the "pong thread, respectivly. (The last set
36 * of names comes from the use of the Ping-Pong synchronization construct from
37 * the RTSem API.)
38 *
39 *
40 *
41 * @section sec_dbgf_scenario Debugger Scenario
42 *
43 * The debugger starts by attaching to the VM. For pratical reasons we limit the
44 * number of concurrently attached debuggers to 1 per VM. The action of attaching
45 * to the VM causes the VM to check and generate debug events.
46 *
47 * The debugger then will wait/poll for debug events and issue commands.
48 *
49 * The waiting and polling is done by the DBGFEventWait() function. It will wait
50 * for the emulation thread to send a ping, thus indicating that there is an
51 * event waiting to be processed.
52 *
53 * An event can be a respons to an command issued previously, the hitting of a
54 * breakpoint, or running into a bad/fatal VMM condition. The debugger now have
55 * the ping and must respond to the event at hand - the VMM is waiting. This
56 * usually means that the user of the debugger must do something, but it doesn't
57 * have to. The debugger is free to call any DBGF function (nearly at least)
58 * while processing the event.
59 *
60 * Typically the user will issue a request for the execution to be resumed, so
61 * the debugger calls DBGFResume() and goes back to waiting/polling for events.
62 *
63 * When the user eventually terminates the debugging session or selects another
64 * VM, the debugger detaches from the VM. This means that breakpoints are disabled
65 * and that the emulation thread no longer polls for debugger commands.
66 *
67 */
68
69
70
71/*******************************************************************************
72* Header Files *
73*******************************************************************************/
74#define LOG_GROUP LOG_GROUP_DBGF
75#include <VBox/dbgf.h>
76#include <VBox/selm.h>
77#include <VBox/rem.h>
78#include <VBox/em.h>
79#include "DBGFInternal.h"
80#include <VBox/vm.h>
81#include <VBox/err.h>
82
83#include <VBox/log.h>
84#include <iprt/semaphore.h>
85#include <iprt/thread.h>
86#include <iprt/asm.h>
87#include <iprt/time.h>
88#include <iprt/assert.h>
89#include <iprt/stream.h>
90
91#include <stdio.h>
92
93
94/*******************************************************************************
95* Internal Functions *
96*******************************************************************************/
97static int dbgfr3VMMWait(PVM pVM);
98static int dbgfr3VMMCmd(PVM pVM, DBGFCMD enmCmd, PDBGFCMDDATA pCmdData, bool *pfResumeExecution);
99
100
101/**
102 * Sets the VMM Debug Command variable.
103 *
104 * @returns Previous command.
105 * @param pVM VM Handle.
106 * @param enmCmd The command.
107 */
108DECLINLINE(DBGFCMD) dbgfr3SetCmd(PVM pVM, DBGFCMD enmCmd)
109{
110 if (enmCmd == DBGFCMD_NO_COMMAND)
111 {
112 Log2(("DBGF: Setting command to %d (DBGFCMD_NO_COMMAND)\n", enmCmd));
113 DBGFCMD rc = (DBGFCMD)ASMAtomicXchgU32((uint32_t volatile *)(void *)&pVM->dbgf.s.enmVMMCmd, enmCmd);
114 VM_FF_CLEAR(pVM, VM_FF_DBGF);
115 return rc;
116 }
117 else
118 {
119 Log2(("DBGF: Setting command to %d\n", enmCmd));
120 AssertMsg(pVM->dbgf.s.enmVMMCmd == DBGFCMD_NO_COMMAND, ("enmCmd=%d enmVMMCmd=%d\n", enmCmd, pVM->dbgf.s.enmVMMCmd));
121 DBGFCMD rc = (DBGFCMD)ASMAtomicXchgU32((uint32_t volatile *)(void *)&pVM->dbgf.s.enmVMMCmd, enmCmd);
122 VM_FF_SET(pVM, VM_FF_DBGF);
123 VMR3NotifyFF(pVM, false /* didn't notify REM */);
124 return rc;
125 }
126}
127
128
129/**
130 * Initializes the DBGF.
131 *
132 * @returns VBox status code.
133 * @param pVM VM handle.
134 */
135DBGFR3DECL(int) DBGFR3Init(PVM pVM)
136{
137 int rc = dbgfR3InfoInit(pVM);
138 if (VBOX_SUCCESS(rc))
139 rc = dbgfR3SymInit(pVM);
140 if (VBOX_SUCCESS(rc))
141 rc = dbgfR3BpInit(pVM);
142 return rc;
143}
144
145
146/**
147 * Termiantes and cleans up resources allocated by the DBGF.
148 *
149 * @returns VBox status code.
150 * @param pVM VM Handle.
151 */
152DBGFR3DECL(int) DBGFR3Term(PVM pVM)
153{
154 int rc;
155
156 /*
157 * Send a termination event to any attached debuggers.
158 */
159 /* wait for any pending events or whatever */
160 if ( pVM->dbgf.s.fAttached
161 && pVM->dbgf.s.PingPong.enmSpeaker == RTPINGPONGSPEAKER_PONG)
162 {
163 do rc = RTSemPingWait(&pVM->dbgf.s.PingPong, 5000);
164 while (pVM->dbgf.s.fAttached);
165 }
166
167 /* now, send the event */
168 if ( pVM->dbgf.s.fAttached
169 && pVM->dbgf.s.PingPong.enmSpeaker != RTPINGPONGSPEAKER_PONG)
170 {
171 pVM->dbgf.s.DbgEvent.enmType = DBGFEVENT_TERMINATING;
172 pVM->dbgf.s.DbgEvent.enmCtx = DBGFEVENTCTX_OTHER;
173 rc = RTSemPing(&pVM->dbgf.s.PingPong);
174 if (VBOX_SUCCESS(rc))
175 {
176 /*
177 * Waits for the debugger to detach.
178 */
179 while (pVM->dbgf.s.fAttached)
180 {
181 /*
182 * Wait.
183 */
184 rc = RTSemPingWait(&pVM->dbgf.s.PingPong, 1000);
185
186 /*
187 * Process the command.
188 */
189 DBGFCMDDATA CmdData = pVM->dbgf.s.VMMCmdData;
190 DBGFCMD enmCmd = dbgfr3SetCmd(pVM, DBGFCMD_NO_COMMAND);
191 if (enmCmd != DBGFCMD_NO_COMMAND)
192 {
193 bool fResumeExecution = false;
194 rc = dbgfr3VMMCmd(pVM, enmCmd, &CmdData, &fResumeExecution);
195 if (enmCmd == DBGFCMD_DETACH_DEBUGGER)
196 break;
197 }
198 else if (VBOX_FAILURE(rc))
199 break;
200 }
201 }
202 }
203
204 /*
205 * Terminate the other bits.
206 */
207 dbgfR3InfoTerm(pVM);
208 return VINF_SUCCESS;
209}
210
211
212/**
213 * Applies relocations to data and code managed by this
214 * component. This function will be called at init and
215 * whenever the VMM need to relocate it self inside the GC.
216 *
217 * @param pVM VM handle.
218 * @param offDelta Relocation delta relative to old location.
219 */
220DBGFR3DECL(void) DBGFR3Relocate(PVM pVM, RTGCINTPTR offDelta)
221{
222}
223
224
225/**
226 * Waits a little while for a debuggger to attach.
227 *
228 * @returns True is a debugger have attached.
229 * @param pVM VM handle.
230 * @param enmEvent Event.
231 */
232bool dbgfR3WaitForAttach(PVM pVM, DBGFEVENTTYPE enmEvent)
233{
234 /*
235 * First a message.
236 */
237 fprintf(stderr, "DBGF: No debugger attached, waiting 15 seconds for one to attach (event=%d)\n", enmEvent);
238 fflush(stderr);
239#ifdef DEBUG_sandervl
240 int cWait = 10;
241#else
242 int cWait = 150;
243#endif
244 while (cWait > 0)
245 {
246 RTThreadSleep(100);
247 if (pVM->dbgf.s.fAttached)
248 {
249 fprintf(stderr, "Attached!\n");
250 fflush(stderr);
251 return true;
252 }
253
254 /* next */
255 if (!(cWait % 10))
256 {
257 fprintf(stderr, "%d.", cWait / 10);
258 fflush(stderr);
259 }
260 cWait--;
261 }
262
263 fprintf(stderr, "Stopping the VM!\n");
264 fflush(stderr);
265 return false;
266}
267
268
269/**
270 * Forced action callback.
271 * The VMM will call this from it's main loop when VM_FF_DBGF is set.
272 *
273 * The function checks and executes pending commands from the debugger.
274 *
275 * @returns VINF_SUCCESS normally.
276 * @returns VERR_DBGF_RAISE_FATAL_ERROR to pretend a fatal error happend.
277 * @param pVM VM Handle.
278 */
279DBGFR3DECL(int) DBGFR3VMMForcedAction(PVM pVM)
280{
281 /*
282 * Clear the FF DBGF request flag.
283 */
284 Assert(pVM->fForcedActions & VM_FF_DBGF);
285 VM_FF_CLEAR(pVM, VM_FF_DBGF);
286
287 /*
288 * Commands?
289 */
290 int rc = VINF_SUCCESS;
291 if (pVM->dbgf.s.enmVMMCmd != DBGFCMD_NO_COMMAND)
292 {
293 /** @todo stupid GDT/LDT sync hack. go away! */
294 SELMR3UpdateFromCPUM(pVM);
295
296 /*
297 * Process the command.
298 */
299 bool fResumeExecution;
300 DBGFCMDDATA CmdData = pVM->dbgf.s.VMMCmdData;
301 DBGFCMD enmCmd = dbgfr3SetCmd(pVM, DBGFCMD_NO_COMMAND);
302 rc = dbgfr3VMMCmd(pVM, enmCmd, &CmdData, &fResumeExecution);
303 if (!fResumeExecution)
304 rc = dbgfr3VMMWait(pVM);
305 }
306 return rc;
307}
308
309
310/**
311 * Flag whether the event implies that we're stopped in the hypervisor code
312 * and have to block certain operations.
313 *
314 * @param pVM The VM handle.
315 * @param enmEvent The event.
316 */
317static void dbgfR3EventSetStoppedInHyperFlag(PVM pVM, DBGFEVENTTYPE enmEvent)
318{
319 switch (enmEvent)
320 {
321 case DBGFEVENT_STEPPED_HYPER:
322 case DBGFEVENT_ASSERTION_HYPER:
323 case DBGFEVENT_BREAKPOINT_HYPER:
324 pVM->dbgf.s.fStoppedInHyper = true;
325 break;
326 default:
327 pVM->dbgf.s.fStoppedInHyper = false;
328 break;
329 }
330}
331
332
333/**
334 * Try determin the event context.
335 *
336 * @returns debug event context.
337 * @param pVM The VM handle.
338 */
339static DBGFEVENTCTX dbgfR3FigureEventCtx(PVM pVM)
340{
341 switch (EMGetState(pVM))
342 {
343 case EMSTATE_RAW:
344 case EMSTATE_DEBUG_GUEST_RAW:
345 return DBGFEVENTCTX_RAW;
346
347 case EMSTATE_REM:
348 case EMSTATE_DEBUG_GUEST_REM:
349 return DBGFEVENTCTX_REM;
350
351 case EMSTATE_DEBUG_HYPER:
352 case EMSTATE_GURU_MEDITATION:
353 return DBGFEVENTCTX_HYPER;
354
355 default:
356 return DBGFEVENTCTX_OTHER;
357 }
358}
359
360/**
361 * The common event prologue code.
362 * It will set the 'stopped-in-hyper' flag, make sure someone's attach,
363 * and perhaps process any high priority pending actions (none yet).
364 *
365 * @returns VBox status.
366 * @param pVM The VM handle.
367 * @param enmEvent The event to be sent.
368 */
369static int dbgfR3EventPrologue(PVM pVM, DBGFEVENTTYPE enmEvent)
370{
371 /*
372 * Check if a debugger is attached.
373 */
374 if ( !pVM->dbgf.s.fAttached
375 && !dbgfR3WaitForAttach(pVM, enmEvent))
376 {
377 Log(("DBGFR3VMMEventSrc: enmEvent=%d - debugger not attached\n", enmEvent));
378 return VERR_DBGF_NOT_ATTACHED;
379 }
380
381 /*
382 * Sync back the state from the REM.
383 */
384 dbgfR3EventSetStoppedInHyperFlag(pVM, enmEvent);
385 if (!pVM->dbgf.s.fStoppedInHyper)
386 REMR3StateUpdate(pVM);
387
388 /*
389 * Look thru pending commands and finish those which make sense now.
390 */
391 /** @todo Process/purge pending commands. */
392 //int rc = DBGFR3VMMForcedAction(pVM);
393 return VINF_SUCCESS;
394}
395
396
397/**
398 * Sends the event in the event buffer.
399 *
400 * @returns VBox status code.
401 * @param pVM The VM handle.
402 */
403static int dbgfR3SendEvent(PVM pVM)
404{
405 int rc = RTSemPing(&pVM->dbgf.s.PingPong);
406 if (VBOX_SUCCESS(rc))
407 rc = dbgfr3VMMWait(pVM);
408
409 pVM->dbgf.s.fStoppedInHyper = false;
410 /** @todo sync VMM -> REM after exitting the debugger. everything may change while in the debugger! */
411 return rc;
412}
413
414
415/**
416 * Send a generic debugger event which takes no data.
417 *
418 * @returns VBox status.
419 * @param pVM The VM handle.
420 * @param enmEvent The event to send.
421 */
422DBGFR3DECL(int) DBGFR3Event(PVM pVM, DBGFEVENTTYPE enmEvent)
423{
424 int rc = dbgfR3EventPrologue(pVM, enmEvent);
425 if (VBOX_FAILURE(rc))
426 return rc;
427
428 /*
429 * Send the event and process the reply communication.
430 */
431 pVM->dbgf.s.DbgEvent.enmType = enmEvent;
432 pVM->dbgf.s.DbgEvent.enmCtx = dbgfR3FigureEventCtx(pVM);
433 return dbgfR3SendEvent(pVM);
434}
435
436
437/**
438 * Send a debugger event which takes the full source file location.
439 *
440 * @returns VBox status.
441 * @param pVM The VM handle.
442 * @param enmEvent The event to send.
443 * @param pszFile Source file.
444 * @param uLine Line number in source file.
445 * @param pszFunction Function name.
446 * @param pszFormat Message which accompanies the event.
447 * @param ... Message arguments.
448 */
449DBGFR3DECL(int) DBGFR3EventSrc(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszFile, unsigned uLine, const char *pszFunction, const char *pszFormat, ...)
450{
451 va_list args;
452 va_start(args, pszFormat);
453 int rc = DBGFR3EventSrcV(pVM, enmEvent, pszFile, uLine, pszFunction, pszFormat, args);
454 va_end(args);
455 return rc;
456}
457
458
459/**
460 * Send a debugger event which takes the full source file location.
461 *
462 * @returns VBox status.
463 * @param pVM The VM handle.
464 * @param enmEvent The event to send.
465 * @param pszFile Source file.
466 * @param uLine Line number in source file.
467 * @param pszFunction Function name.
468 * @param pszFormat Message which accompanies the event.
469 * @param args Message arguments.
470 */
471DBGFR3DECL(int) DBGFR3EventSrcV(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszFile, unsigned uLine, const char *pszFunction, const char *pszFormat, va_list args)
472{
473 int rc = dbgfR3EventPrologue(pVM, enmEvent);
474 if (VBOX_FAILURE(rc))
475 return rc;
476
477 /*
478 * Format the message.
479 */
480 char *pszMessage = NULL;
481 char szMessage[8192];
482 if (pszFormat && *pszFormat)
483 {
484 pszMessage = &szMessage[0];
485 RTStrPrintfV(szMessage, sizeof(szMessage), pszFormat, args);
486 }
487
488 /*
489 * Send the event and process the reply communication.
490 */
491 pVM->dbgf.s.DbgEvent.enmType = enmEvent;
492 pVM->dbgf.s.DbgEvent.enmCtx = dbgfR3FigureEventCtx(pVM);
493 pVM->dbgf.s.DbgEvent.u.Src.pszFile = pszFile;
494 pVM->dbgf.s.DbgEvent.u.Src.uLine = uLine;
495 pVM->dbgf.s.DbgEvent.u.Src.pszFunction = pszFunction;
496 pVM->dbgf.s.DbgEvent.u.Src.pszMessage = pszMessage;
497 return dbgfR3SendEvent(pVM);
498}
499
500
501/**
502 * Send a debugger event which takes the two assertion messages.
503 *
504 * @returns VBox status.
505 * @param pVM The VM handle.
506 * @param enmEvent The event to send.
507 * @param pszMsg1 First assertion message.
508 * @param pszMsg2 Second assertion message.
509 */
510DBGFR3DECL(int) DBGFR3EventAssertion(PVM pVM, DBGFEVENTTYPE enmEvent, const char *pszMsg1, const char *pszMsg2)
511{
512 int rc = dbgfR3EventPrologue(pVM, enmEvent);
513 if (VBOX_FAILURE(rc))
514 return rc;
515
516 /*
517 * Send the event and process the reply communication.
518 */
519 pVM->dbgf.s.DbgEvent.enmType = enmEvent;
520 pVM->dbgf.s.DbgEvent.enmCtx = dbgfR3FigureEventCtx(pVM);
521 pVM->dbgf.s.DbgEvent.u.Assert.pszMsg1 = pszMsg1;
522 pVM->dbgf.s.DbgEvent.u.Assert.pszMsg2 = pszMsg2;
523 return dbgfR3SendEvent(pVM);
524}
525
526
527/**
528 * Breakpoint was hit somewhere.
529 * Figure out which breakpoint it is and notify the debugger.
530 *
531 * @returns VBox status.
532 * @param pVM The VM handle.
533 * @param enmEvent DBGFEVENT_BREAKPOINT_HYPER or DBGFEVENT_BREAKPOINT.
534 */
535DBGFR3DECL(int) DBGFR3EventBreakpoint(PVM pVM, DBGFEVENTTYPE enmEvent)
536{
537 int rc = dbgfR3EventPrologue(pVM, enmEvent);
538 if (VBOX_FAILURE(rc))
539 return rc;
540
541 /*
542 * Send the event and process the reply communication.
543 */
544 pVM->dbgf.s.DbgEvent.enmType = enmEvent;
545 RTUINT iBp = pVM->dbgf.s.DbgEvent.u.Bp.iBp = pVM->dbgf.s.iActiveBp;
546 pVM->dbgf.s.iActiveBp = ~0U;
547 if (iBp != ~0U)
548 pVM->dbgf.s.DbgEvent.enmCtx = DBGFEVENTCTX_RAW;
549 else
550 {
551 /* REM breakpoints has be been searched for. */
552#if 0 /** @todo get flat PC api! */
553 uint32_t eip = CPUMGetGuestEIP(pVM);
554#else
555 PCPUMCTX pCtx;
556 CPUMQueryGuestCtxPtr(pVM, &pCtx);
557 uint32_t eip = pCtx->eip + pCtx->csHid.u32Base;
558#endif
559 for (iBp = 0; iBp < ELEMENTS(pVM->dbgf.s.aBreakpoints); iBp++)
560 if ( pVM->dbgf.s.aBreakpoints[iBp].enmType == DBGFBPTYPE_REM
561 && pVM->dbgf.s.aBreakpoints[iBp].GCPtr == eip)
562 {
563 pVM->dbgf.s.DbgEvent.u.Bp.iBp = iBp;
564 break;
565 }
566 AssertMsg(pVM->dbgf.s.DbgEvent.u.Bp.iBp != ~0U, ("eip=%08x\n", eip));
567 pVM->dbgf.s.DbgEvent.enmCtx = DBGFEVENTCTX_REM;
568 }
569 return dbgfR3SendEvent(pVM);
570}
571
572
573/**
574 * Waits for the debugger to respond.
575 *
576 * @returns VBox status. (clearify)
577 * @param pVM VM handle.
578 */
579static int dbgfr3VMMWait(PVM pVM)
580{
581 LogFlow(("dbgfr3VMMWait:\n"));
582
583 /** @todo stupid GDT/LDT sync hack. go away! */
584 SELMR3UpdateFromCPUM(pVM);
585 int rcRet = VINF_SUCCESS;
586
587 /*
588 * Waits for the debugger to reply (i.e. issue an command).
589 */
590 for (;;)
591 {
592 /*
593 * Wait.
594 */
595 for (;;)
596 {
597 int rc = RTSemPingWait(&pVM->dbgf.s.PingPong, 250);
598 if (VBOX_SUCCESS(rc))
599 break;
600 if (rc != VERR_TIMEOUT)
601 {
602 LogFlow(("dbgfr3VMMWait: returns %Vrc\n", rc));
603 return rc;
604 }
605
606 if (VM_FF_ISSET(pVM, VM_FF_REQUEST))
607 {
608 LogFlow(("dbgfr3VMMWait: Processes requests...\n"));
609 rc = VMR3ReqProcess(pVM);
610 LogFlow(("dbgfr3VMMWait: VMR3ReqProcess -> %Vrc rcRet=%Vrc\n", rc, rcRet));
611 if (rc >= VINF_EM_FIRST && rc < VINF_EM_LAST)
612 {
613 switch (rc)
614 {
615 case VINF_EM_DBG_BREAKPOINT:
616 case VINF_EM_DBG_STEPPED:
617 case VINF_EM_DBG_STEP:
618 case VINF_EM_DBG_STOP:
619 AssertMsgFailed(("rc=%Vrc\n", rc));
620 break;
621
622 /* return straight away */
623 case VINF_EM_TERMINATE:
624 case VINF_EM_OFF:
625 LogFlow(("dbgfr3VMMWait: returns %Vrc\n", rc));
626 return rc;
627
628 /* remember return code. */
629 default:
630 AssertReleaseMsgFailed(("rc=%Vrc is not in the switch!\n", rc));
631 case VINF_EM_RESET:
632 case VINF_EM_SUSPEND:
633 case VINF_EM_HALT:
634 case VINF_EM_RESUME:
635 case VINF_EM_RESCHEDULE:
636 case VINF_EM_RESCHEDULE_REM:
637 case VINF_EM_RESCHEDULE_RAW:
638 if (rc < rcRet || rcRet == VINF_SUCCESS)
639 rcRet = rc;
640 break;
641 }
642 }
643 else if (VBOX_FAILURE(rc))
644 {
645 LogFlow(("dbgfr3VMMWait: returns %Vrc\n", rc));
646 return rc;
647 }
648 }
649 }
650
651 /*
652 * Process the command.
653 */
654 bool fResumeExecution;
655 DBGFCMDDATA CmdData = pVM->dbgf.s.VMMCmdData;
656 DBGFCMD enmCmd = dbgfr3SetCmd(pVM, DBGFCMD_NO_COMMAND);
657 int rc = dbgfr3VMMCmd(pVM, enmCmd, &CmdData, &fResumeExecution);
658 if (fResumeExecution)
659 {
660 if (VBOX_FAILURE(rc))
661 rcRet = rc;
662 else if ( rc >= VINF_EM_FIRST
663 && rc < VINF_EM_LAST
664 && (rc < rcRet || rcRet == VINF_SUCCESS))
665 rcRet = rc;
666 LogFlow(("dbgfr3VMMWait: returns %Vrc\n", rcRet));
667 return rcRet;
668 }
669 }
670}
671
672
673/**
674 * Executes command from debugger.
675 * The caller is responsible for waiting or resuming execution based on the
676 * value returned in the *pfResumeExecution indicator.
677 *
678 * @returns VBox status. (clearify!)
679 * @param pVM VM Handle.
680 * @param enmCmd The command in question.
681 * @param pCmdData Pointer to the command data.
682 * @param pfResumeExecution Where to store the resume execution / continue waiting indicator.
683 */
684static int dbgfr3VMMCmd(PVM pVM, DBGFCMD enmCmd, PDBGFCMDDATA pCmdData, bool *pfResumeExecution)
685{
686 bool fSendEvent;
687 bool fResume;
688 int rc = VINF_SUCCESS;
689
690 switch (enmCmd)
691 {
692 /*
693 * Halt is answered by an event say that we've halted.
694 */
695 case DBGFCMD_HALT:
696 {
697 pVM->dbgf.s.DbgEvent.enmType = DBGFEVENT_HALT_DONE;
698 pVM->dbgf.s.DbgEvent.enmCtx = dbgfR3FigureEventCtx(pVM);
699 fSendEvent = true;
700 fResume = false;
701 break;
702 }
703
704
705 /*
706 * Resume is not answered we'll just resume execution.
707 */
708 case DBGFCMD_GO:
709 {
710 fSendEvent = false;
711 fResume = true;
712 break;
713 }
714
715 /** @todo implement (and define) the rest of the commands. */
716
717 /*
718 * Disable breakpoints and stuff.
719 * Send an everythings cool event to the debugger thread and resume execution.
720 */
721 case DBGFCMD_DETACH_DEBUGGER:
722 {
723 pVM->dbgf.s.DbgEvent.enmType = DBGFEVENT_DETACH_DONE;
724 pVM->dbgf.s.DbgEvent.enmCtx = DBGFEVENTCTX_OTHER;
725 VM_FF_CLEAR(pVM, VM_FF_DBGF);
726 pVM->dbgf.s.fAttached = false;
727 fSendEvent = true;
728 fResume = true;
729 break;
730 }
731
732 /*
733 * Single step, with trace into.
734 */
735 case DBGFCMD_SINGLE_STEP:
736 {
737 Log2(("Single step\n"));
738 rc = VINF_EM_DBG_STEP;
739 pVM->dbgf.s.fSingleSteppingRaw = true;
740 fSendEvent = false;
741 fResume = true;
742 break;
743 }
744
745 /*
746 * Default is to send an invalid command event.
747 */
748 default:
749 {
750 pVM->dbgf.s.DbgEvent.enmType = DBGFEVENT_INVALID_COMMAND;
751 pVM->dbgf.s.DbgEvent.enmCtx = dbgfR3FigureEventCtx(pVM);
752 fSendEvent = true;
753 fResume = false;
754 break;
755 }
756 }
757
758 /*
759 * Send pending event.
760 */
761 if (fSendEvent)
762 {
763 Log2(("DBGF: Emulation thread: sending event %d\n", pVM->dbgf.s.DbgEvent.enmType));
764 int rc2 = RTSemPing(&pVM->dbgf.s.PingPong);
765 if (VBOX_FAILURE(rc2))
766 {
767 AssertRC(rc2);
768 *pfResumeExecution = true;
769 return rc2;
770 }
771 }
772
773 /*
774 * Return.
775 */
776 *pfResumeExecution = fResume;
777 return rc;
778}
779
780
781
782
783/**
784 * Attaches a debugger to the specified VM.
785 *
786 * Only one debugger at a time.
787 *
788 * @returns VBox status code.
789 * @param pVM VM Handle.
790 */
791DBGFR3DECL(int) DBGFR3Attach(PVM pVM)
792{
793 /*
794 * Check if already attached.
795 */
796 if (pVM->dbgf.s.fAttached)
797 {
798 AssertMsgFailed(("Already attached\n"));
799 return VERR_DBGF_ALREADY_ATTACHED;
800 }
801
802 /*
803 * Create the Ping-Pong structure.
804 */
805 int rc = RTSemPingPongInit(&pVM->dbgf.s.PingPong);
806 if (rc)
807 return rc;
808
809 /*
810 * Set the attached flag.
811 */
812 pVM->dbgf.s.fAttached = true;
813 return VINF_SUCCESS;
814}
815
816
817/**
818 * Detaches a debugger from the specified VM.
819 *
820 * Caller must be attached to the VM.
821 *
822 * @returns VBox status code.
823 * @param pVM VM Handle.
824 */
825DBGFR3DECL(int) DBGFR3Detach(PVM pVM)
826{
827 /*
828 * Check if attached.
829 */
830 if (!pVM->dbgf.s.fAttached)
831 {
832 AssertMsgFailed(("Not attached to VM!\n"));
833 return VERR_DBGF_NOT_ATTACHED;
834 }
835
836 /*
837 * Send detach command.
838 */
839 if (pVM->dbgf.s.PingPong.enmSpeaker == RTPINGPONGSPEAKER_PONG)
840 {
841 dbgfr3SetCmd(pVM, DBGFCMD_DETACH_DEBUGGER);
842 int rc = RTSemPong(&pVM->dbgf.s.PingPong);
843 if (VBOX_FAILURE(rc))
844 {
845 AssertMsgFailed(("Failed to signal emulation thread. rc=%d\n", rc));
846 return rc;
847 }
848 }
849 else
850 dbgfr3SetCmd(pVM, DBGFCMD_DETACH_DEBUGGER);
851
852 /*
853 * Wait for the ok event.
854 */
855 PCDBGFEVENT pEvent;
856 int rc = DBGFR3EventWait(pVM, RT_INDEFINITE_WAIT, &pEvent);
857 if (VBOX_FAILURE(rc))
858 {
859 AssertMsgFailed(("Wait on detach command failed, rc=%d\n", rc));
860 return rc;
861 }
862
863 /*
864 * Destroy the ping-pong construct and return.
865 */
866 pVM->dbgf.s.fAttached = false;
867 RTThreadSleep(10);
868 rc = RTSemPingPongDestroy(&pVM->dbgf.s.PingPong);
869 AssertRC(rc);
870
871 return VINF_SUCCESS;
872}
873
874
875
876/**
877 * Wait for a debug event.
878 *
879 * @returns VBox status. Will not return VBOX_INTERRUPTED.
880 * @param pVM VM handle.
881 * @param cMillies Number of millies to wait.
882 * @param ppEvent Where to store the event pointer.
883 */
884DBGFR3DECL(int) DBGFR3EventWait(PVM pVM, unsigned cMillies, PCDBGFEVENT *ppEvent)
885{
886 /*
887 * Check state.
888 */
889 if (!pVM->dbgf.s.fAttached)
890 {
891 AssertMsgFailed(("Not attached to VM!\n"));
892 return VERR_DBGF_NOT_ATTACHED;
893 }
894 *ppEvent = NULL;
895
896 /*
897 * Wait.
898 */
899 int rc = RTSemPongWait(&pVM->dbgf.s.PingPong, cMillies);
900 if (VBOX_SUCCESS(rc))
901 {
902 *ppEvent = &pVM->dbgf.s.DbgEvent;
903 Log2(("DBGF: Debugger thread: receiving event %d\n", (*ppEvent)->enmType));
904 return VINF_SUCCESS;
905 }
906
907 return rc;
908}
909
910
911/**
912 * Halts VM execution.
913 *
914 * After calling this the VM isn't actually halted till an DBGFEVENT_HALT_DONE
915 * arrives. Until that time it's not possible to issue any new commands.
916 *
917 * @returns VBox status.
918 * @param pVM VM handle.
919 */
920DBGFR3DECL(int) DBGFR3Halt(PVM pVM)
921{
922 /*
923 * Check state.
924 */
925 if (!pVM->dbgf.s.fAttached)
926 {
927 AssertMsgFailed(("Not attached to VM!\n"));
928 return VERR_DBGF_NOT_ATTACHED;
929 }
930 if ( pVM->dbgf.s.PingPong.enmSpeaker == RTPINGPONGSPEAKER_PONG
931 || pVM->dbgf.s.PingPong.enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED)
932 return VWRN_DBGF_ALREADY_HALTED;
933
934 /*
935 * Send command.
936 */
937 dbgfr3SetCmd(pVM, DBGFCMD_HALT);
938
939 return VINF_SUCCESS;
940}
941
942
943/**
944 * Checks if the VM is halted by the debugger.
945 *
946 * @returns True if halted.
947 * @returns False if not halted.
948 * @param pVM VM handle.
949 */
950DBGFR3DECL(bool) DBGFR3IsHalted(PVM pVM)
951{
952 AssertMsg(pVM->dbgf.s.fAttached, ("Not attached to VM!\n"));
953
954 RTPINGPONGSPEAKER enmSpeaker = pVM->dbgf.s.PingPong.enmSpeaker;
955 return pVM->dbgf.s.fAttached
956 && ( enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED
957 || enmSpeaker == RTPINGPONGSPEAKER_PONG);
958}
959
960
961/**
962 * Checks if the the debugger can wait for events or not.
963 *
964 * This function is only used by lazy, multiplexing debuggers. :-)
965 *
966 * @returns True if waitable.
967 * @returns False if not waitable.
968 * @param pVM VM handle.
969 */
970DBGFR3DECL(bool) DBGFR3CanWait(PVM pVM)
971{
972 AssertMsg(pVM->dbgf.s.fAttached, ("Not attached to VM!\n"));
973
974 RTPINGPONGSPEAKER enmSpeaker = pVM->dbgf.s.PingPong.enmSpeaker;
975 return pVM->dbgf.s.fAttached
976 && ( enmSpeaker == RTPINGPONGSPEAKER_PING
977 || enmSpeaker == RTPINGPONGSPEAKER_PING_SIGNALED
978 || enmSpeaker == RTPINGPONGSPEAKER_PONG_SIGNALED);
979}
980
981
982/**
983 * Resumes VM execution.
984 *
985 * There is no receipt event on this command.
986 *
987 * @returns VBox status.
988 * @param pVM VM handle.
989 */
990DBGFR3DECL(int) DBGFR3Resume(PVM pVM)
991{
992 /*
993 * Check state.
994 */
995 if (!pVM->dbgf.s.fAttached)
996 {
997 AssertMsgFailed(("Not attached to VM!\n"));
998 return VERR_DBGF_NOT_ATTACHED;
999 }
1000 if (pVM->dbgf.s.PingPong.enmSpeaker != RTPINGPONGSPEAKER_PONG)
1001 {
1002 AssertMsgFailed(("Speaking out of turn!\n"));
1003 return VERR_SEM_OUT_OF_TURN;
1004 }
1005
1006 /*
1007 * Send the ping back to the emulation thread telling it to run.
1008 */
1009 dbgfr3SetCmd(pVM, DBGFCMD_GO);
1010 int rc = RTSemPong(&pVM->dbgf.s.PingPong);
1011 AssertRC(rc);
1012
1013 return rc;
1014}
1015
1016
1017/**
1018 * Step Into.
1019 *
1020 * A single step event is generated from this command.
1021 * The current implementation is not reliable, so don't rely on the event comming.
1022 *
1023 * @returns VBox status.
1024 * @param pVM VM handle.
1025 */
1026DBGFR3DECL(int) DBGFR3Step(PVM pVM)
1027{
1028 /*
1029 * Check state.
1030 */
1031 if (!pVM->dbgf.s.fAttached)
1032 {
1033 AssertMsgFailed(("Not attached to VM!\n"));
1034 return VERR_DBGF_NOT_ATTACHED;
1035 }
1036 if (pVM->dbgf.s.PingPong.enmSpeaker != RTPINGPONGSPEAKER_PONG)
1037 {
1038 AssertMsgFailed(("Speaking out of turn!\n"));
1039 return VERR_SEM_OUT_OF_TURN;
1040 }
1041
1042 /*
1043 * Send the ping back to the emulation thread telling it to run.
1044 */
1045 dbgfr3SetCmd(pVM, DBGFCMD_SINGLE_STEP);
1046 int rc = RTSemPong(&pVM->dbgf.s.PingPong);
1047 AssertRC(rc);
1048 return rc;
1049}
1050
1051
1052/**
1053 * Call this to single step rawmode or recompiled mode.
1054 *
1055 * You must pass down the return code to the EM loop! That's
1056 * where the actual single stepping take place (at least in the
1057 * current implementation).
1058 *
1059 * @returns VINF_EM_DBG_STEP
1060 * @thread EMT
1061 */
1062DBGFR3DECL(int) DBGFR3PrgStep(PVM pVM)
1063{
1064 VM_ASSERT_EMT(pVM);
1065
1066 pVM->dbgf.s.fSingleSteppingRaw = true;
1067 return VINF_EM_DBG_STEP;
1068}
1069
1070
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