VirtualBox

source: vbox/trunk/src/VBox/Debugger/VBoxDbgConsole.cpp@ 86327

Last change on this file since 86327 was 86327, checked in by vboxsync, 4 years ago

Debugger: Allow for different I/O providers instead of only TCP

So far TCP was the only option to communicate remotely with the internal debugger, the other option
was to use the console from the GUI directly. This commit reworks basic I/O to allow for different
providers where TCP is just one option. The second one being introduced is an IPC provider using a local
socket or named pipe depending on the platform. This allows for Windows kernel debugging over a pipe
using the KD stub in VirtualBox and WinDbg running on the host (not tested yet).

Furthermore this commit allows multiple stubs to be listening for connections at the same time, so
one can have a GDB stub listening on one TCP port and the native VBox debugger listening on another one
or even using a different I/O provider. Only one session can be active at a time though, because sharing
debugger states is impossible. To configure this the following CFGM keys need to be set for each listener:

"DBGC/<Some unique ID>/Provider" "tcp|ipc"
"DBGC/<Some unique ID>/StubType" "native|gdb|kd"
"DBGC/<Some unique ID>/Address" "<ip>|<local named pipe or socket path>"
"DBGC/<Some unique ID>/Port" "<port>" (for TCP only)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.2 KB
Line 
1/* $Id: VBoxDbgConsole.cpp 86327 2020-09-28 16:20:50Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#define LOG_GROUP LOG_GROUP_DBGG
23#include "VBoxDbgConsole.h"
24#include "VBoxDbgGui.h"
25
26#include <QLabel>
27#include <QApplication>
28#include <QFont>
29#include <QLineEdit>
30#include <QHBoxLayout>
31#include <QAction>
32#include <QContextMenuEvent>
33#include <QMenu>
34
35#include <VBox/dbg.h>
36#include <VBox/vmm/cfgm.h>
37#include <iprt/errcore.h>
38
39#include <iprt/thread.h>
40#include <iprt/tcp.h>
41#include <VBox/log.h>
42#include <iprt/assert.h>
43#include <iprt/asm.h>
44#include <iprt/alloc.h>
45#include <iprt/string.h>
46
47#include <VBox/com/string.h>
48
49
50
51/*
52 *
53 * V B o x D b g C o n s o l e O u t p u t
54 * V B o x D b g C o n s o l e O u t p u t
55 * V B o x D b g C o n s o l e O u t p u t
56 *
57 *
58 */
59
60/*static*/ const uint32_t VBoxDbgConsoleOutput::s_uMinFontSize = 6;
61
62
63VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, IVirtualBox *a_pVirtualBox /* = NULL */,
64 const char *pszName/* = NULL*/)
65 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf()), m_pVirtualBox(a_pVirtualBox)
66{
67 setReadOnly(true);
68 setUndoRedoEnabled(false);
69 setOverwriteMode(false);
70 setPlainText("");
71 setTextInteractionFlags(Qt::TextBrowserInteraction);
72 setAutoFormatting(QTextEdit::AutoAll);
73 setTabChangesFocus(true);
74 setAcceptRichText(false);
75
76 /*
77 * Create actions for color-scheme menu items.
78 */
79 m_pGreenOnBlackAction = new QAction(tr("Green On Black"), this);
80 m_pGreenOnBlackAction->setCheckable(true);
81 m_pGreenOnBlackAction->setShortcut(Qt::ControlModifier + Qt::Key_1);
82 m_pGreenOnBlackAction->setData((int)kGreenOnBlack);
83 connect(m_pGreenOnBlackAction, SIGNAL(triggered()), this, SLOT(sltSelectColorScheme()));
84
85 m_pBlackOnWhiteAction = new QAction(tr("Black On White"), this);
86 m_pBlackOnWhiteAction->setCheckable(true);
87 m_pBlackOnWhiteAction->setShortcut(Qt::ControlModifier + Qt::Key_2);
88 m_pBlackOnWhiteAction->setData((int)kBlackOnWhite);
89 connect(m_pBlackOnWhiteAction, SIGNAL(triggered()), this, SLOT(sltSelectColorScheme()));
90
91 /* Create action group for grouping of exclusive color-scheme menu items. */
92 QActionGroup *pActionColorGroup = new QActionGroup(this);
93 pActionColorGroup->addAction(m_pGreenOnBlackAction);
94 pActionColorGroup->addAction(m_pBlackOnWhiteAction);
95 pActionColorGroup->setExclusive(true);
96
97 /*
98 * Create actions for font menu items.
99 */
100 m_pCourierFontAction = new QAction(tr("Courier"), this);
101 m_pCourierFontAction->setCheckable(true);
102 m_pCourierFontAction->setShortcut(Qt::ControlModifier + Qt::Key_D);
103 m_pCourierFontAction->setData((int)kFontType_Courier);
104 connect(m_pCourierFontAction, SIGNAL(triggered()), this, SLOT(sltSelectFontType()));
105
106 m_pMonospaceFontAction = new QAction(tr("Monospace"), this);
107 m_pMonospaceFontAction->setCheckable(true);
108 m_pMonospaceFontAction->setShortcut(Qt::ControlModifier + Qt::Key_M);
109 m_pMonospaceFontAction->setData((int)kFontType_Monospace);
110 connect(m_pMonospaceFontAction, SIGNAL(triggered()), this, SLOT(sltSelectFontType()));
111
112 /* Create action group for grouping of exclusive font menu items. */
113 QActionGroup *pActionFontGroup = new QActionGroup(this);
114 pActionFontGroup->addAction(m_pCourierFontAction);
115 pActionFontGroup->addAction(m_pMonospaceFontAction);
116 pActionFontGroup->setExclusive(true);
117
118 /*
119 * Create actions for font size menu.
120 */
121 uint32_t const uDefaultFontSize = font().pointSize();
122 m_pActionFontSizeGroup = new QActionGroup(this);
123 for (uint32_t i = 0; i < RT_ELEMENTS(m_apFontSizeActions); i++)
124 {
125 char szTitle[32];
126 RTStrPrintf(szTitle, sizeof(szTitle), s_uMinFontSize + i != uDefaultFontSize ? "%upt" : "%upt (default)",
127 s_uMinFontSize + i);
128 m_apFontSizeActions[i] = new QAction(tr(szTitle), this);
129 m_apFontSizeActions[i]->setCheckable(true);
130 m_apFontSizeActions[i]->setData(i + s_uMinFontSize);
131 connect(m_apFontSizeActions[i], SIGNAL(triggered()), this, SLOT(sltSelectFontSize()));
132 m_pActionFontSizeGroup->addAction(m_apFontSizeActions[i]);
133 }
134
135 /*
136 * Set the defaults (which syncs with the menu item checked state).
137 */
138 /* color scheme: */
139 com::Bstr bstrColor;
140 HRESULT hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/ColorScheme").raw(), bstrColor.asOutParam()) : E_FAIL;
141 if ( SUCCEEDED(hrc)
142 && bstrColor.compareUtf8("blackonwhite", com::Bstr::CaseInsensitive) == 0)
143 setColorScheme(kBlackOnWhite, false /*fSaveIt*/);
144 else
145 setColorScheme(kGreenOnBlack, false /*fSaveIt*/);
146
147 /* font: */
148 com::Bstr bstrFont;
149 hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/Font").raw(), bstrFont.asOutParam()) : E_FAIL;
150 if ( SUCCEEDED(hrc)
151 && bstrFont.compareUtf8("monospace", com::Bstr::CaseInsensitive) == 0)
152 setFontType(kFontType_Monospace, false /*fSaveIt*/);
153 else
154 setFontType(kFontType_Courier, false /*fSaveIt*/);
155
156 /* font size: */
157 com::Bstr bstrFontSize;
158 hrc = m_pVirtualBox ? m_pVirtualBox->GetExtraData(com::Bstr("DbgConsole/FontSize").raw(), bstrFontSize.asOutParam()) : E_FAIL;
159 if (SUCCEEDED(hrc))
160 {
161 com::Utf8Str strFontSize(bstrFontSize);
162 uint32_t uFontSizePrf = strFontSize.strip().toUInt32();
163 if ( uFontSizePrf - s_uMinFontSize < (uint32_t)RT_ELEMENTS(m_apFontSizeActions)
164 && uFontSizePrf != uDefaultFontSize)
165 setFontSize(uFontSizePrf, false /*fSaveIt*/);
166 }
167
168 NOREF(pszName);
169}
170
171
172VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
173{
174 Assert(m_hGUIThread == RTThreadNativeSelf());
175 if (m_pVirtualBox)
176 {
177 m_pVirtualBox->Release();
178 m_pVirtualBox = NULL;
179 }
180}
181
182
183void
184VBoxDbgConsoleOutput::contextMenuEvent(QContextMenuEvent *pEvent)
185{
186 /*
187 * Create the context menu and add the menu items.
188 */
189 QMenu *pMenu = createStandardContextMenu();
190 pMenu->addSeparator();
191
192 QMenu *pColorMenu = pMenu->addMenu(tr("Co&lor Scheme"));
193 pColorMenu->addAction(m_pGreenOnBlackAction);
194 pColorMenu->addAction(m_pBlackOnWhiteAction);
195
196 QMenu *pFontMenu = pMenu->addMenu(tr("&Font Family"));
197 pFontMenu->addAction(m_pCourierFontAction);
198 pFontMenu->addAction(m_pMonospaceFontAction);
199
200 QMenu *pFontSize = pMenu->addMenu(tr("Font &Size"));
201 for (unsigned i = 0; i < RT_ELEMENTS(m_apFontSizeActions); i++)
202 pFontSize->addAction(m_apFontSizeActions[i]);
203
204 pMenu->exec(pEvent->globalPos());
205 delete pMenu;
206}
207
208
209void
210VBoxDbgConsoleOutput::setColorScheme(VBoxDbgConsoleColor enmScheme, bool fSaveIt)
211{
212 const char *pszSetting;
213 QAction *pAction;
214 switch (enmScheme)
215 {
216 case kGreenOnBlack:
217 setStyleSheet("QTextEdit { background-color: black; color: rgb(0, 224, 0) }");
218 pszSetting = "GreenOnBlack";
219 pAction = m_pGreenOnBlackAction;
220 break;
221 case kBlackOnWhite:
222 setStyleSheet("QTextEdit { background-color: white; color: black }");
223 pszSetting = "BlackOnWhite";
224 pAction = m_pBlackOnWhiteAction;
225 break;
226 default:
227 AssertFailedReturnVoid();
228 }
229
230 m_enmColorScheme = kGreenOnBlack;
231
232 /* When going through a slot, the action is typically checked already by Qt. */
233 if (!pAction->isChecked())
234 pAction->setChecked(true);
235
236 /* Make this setting persistent. */
237 if (m_pVirtualBox && fSaveIt)
238 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/ColorScheme").raw(), com::Bstr(pszSetting).raw());
239}
240
241
242void
243VBoxDbgConsoleOutput::setFontType(VBoxDbgConsoleFontType enmFontType, bool fSaveIt)
244{
245 QFont Font = font();
246 QAction *pAction;
247 const char *pszSetting;
248 switch (enmFontType)
249 {
250 case kFontType_Courier:
251#ifdef Q_WS_MAC
252 Font = QFont("Monaco", Font.pointSize(), QFont::Normal, FALSE);
253 Font.setStyleStrategy(QFont::NoAntialias);
254#else
255 Font.setStyleHint(QFont::TypeWriter);
256 Font.setFamily("Courier [Monotype]");
257#endif
258 pszSetting = "Courier";
259 pAction = m_pCourierFontAction;
260 break;
261
262 case kFontType_Monospace:
263 Font.setStyleHint(QFont::TypeWriter);
264 Font.setStyleStrategy(QFont::PreferAntialias);
265 Font.setFamily("Monospace [Monotype]");
266 pszSetting = "Monospace";
267 pAction = m_pMonospaceFontAction;
268 break;
269
270 default:
271 AssertFailedReturnVoid();
272 }
273
274 setFont(Font);
275
276 /* When going through a slot, the action is typically checked already by Qt. */
277 if (!pAction->isChecked())
278 pAction->setChecked(true);
279
280 /* Make this setting persistent. */
281 if (m_pVirtualBox && fSaveIt)
282 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/Font").raw(), com::Bstr(pszSetting).raw());
283}
284
285
286void
287VBoxDbgConsoleOutput::setFontSize(uint32_t uFontSize, bool fSaveIt)
288{
289 uint32_t idxAction = uFontSize - s_uMinFontSize;
290 if (idxAction < (uint32_t)RT_ELEMENTS(m_apFontSizeActions))
291 {
292 if (!m_apFontSizeActions[idxAction]->isChecked())
293 m_apFontSizeActions[idxAction]->setChecked(true);
294
295 QFont Font = font();
296 Font.setPointSize(uFontSize);
297 setFont(Font);
298
299 /* Make this setting persistent if requested. */
300 if (fSaveIt && m_pVirtualBox)
301 m_pVirtualBox->SetExtraData(com::Bstr("DbgConsole/FontSize").raw(), com::BstrFmt("%u", uFontSize).raw());
302 }
303}
304
305
306void
307VBoxDbgConsoleOutput::sltSelectColorScheme()
308{
309 QAction *pAction = qobject_cast<QAction *>(sender());
310 if (pAction)
311 setColorScheme((VBoxDbgConsoleColor)pAction->data().toInt(), true /*fSaveIt*/);
312}
313
314
315void
316VBoxDbgConsoleOutput::sltSelectFontType()
317{
318 QAction *pAction = qobject_cast<QAction *>(sender());
319 if (pAction)
320 setFontType((VBoxDbgConsoleFontType)pAction->data().toInt(), true /*fSaveIt*/);
321}
322
323
324void
325VBoxDbgConsoleOutput::sltSelectFontSize()
326{
327 QAction *pAction = qobject_cast<QAction *>(sender());
328 if (pAction)
329 setFontSize(pAction->data().toUInt(), true /*fSaveIt*/);
330}
331
332
333void
334VBoxDbgConsoleOutput::appendText(const QString &rStr, bool fClearSelection)
335{
336 Assert(m_hGUIThread == RTThreadNativeSelf());
337
338 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
339 return;
340
341 /*
342 * Insert all in one go and make sure it's visible.
343 *
344 * We need to move the cursor and unselect any selected text before
345 * inserting anything, otherwise, text will disappear.
346 */
347 QTextCursor Cursor = textCursor();
348 if (!fClearSelection && Cursor.hasSelection())
349 {
350 QTextCursor SavedCursor = Cursor;
351 Cursor.clearSelection();
352 Cursor.movePosition(QTextCursor::End);
353
354 Cursor.insertText(rStr);
355
356 setTextCursor(SavedCursor);
357 }
358 else
359 {
360 if (Cursor.hasSelection())
361 Cursor.clearSelection();
362 if (!Cursor.atEnd())
363 Cursor.movePosition(QTextCursor::End);
364
365 Cursor.insertText(rStr);
366
367 setTextCursor(Cursor);
368 ensureCursorVisible();
369 }
370}
371
372
373
374
375/*
376 *
377 * V B o x D b g C o n s o l e I n p u t
378 * V B o x D b g C o n s o l e I n p u t
379 * V B o x D b g C o n s o l e I n p u t
380 *
381 *
382 */
383
384
385VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
386 : QComboBox(pParent), m_hGUIThread(RTThreadNativeSelf())
387{
388 addItem(""); /* invariant: empty command line is the last item */
389
390 setEditable(true);
391 setInsertPolicy(NoInsert);
392 setAutoCompletion(false);
393 setMaxCount(50);
394 const QLineEdit *pEdit = lineEdit();
395 if (pEdit)
396 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
397
398 NOREF(pszName);
399}
400
401
402VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
403{
404 Assert(m_hGUIThread == RTThreadNativeSelf());
405}
406
407
408void
409VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
410{
411 Assert(m_hGUIThread == RTThreadNativeSelf());
412 QComboBox::setLineEdit(pEdit);
413 if (lineEdit() == pEdit && pEdit)
414 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
415}
416
417
418void
419VBoxDbgConsoleInput::returnPressed()
420{
421 Assert(m_hGUIThread == RTThreadNativeSelf());
422
423 QString strCommand = currentText();
424 /** @todo trim whitespace? */
425 if (strCommand.isEmpty())
426 return;
427
428 /* deal with the current command. */
429 emit commandSubmitted(strCommand);
430
431
432 /*
433 * Add current command to history.
434 */
435 bool fNeedsAppending = true;
436
437 /* invariant: empty line at the end */
438 int iLastItem = count() - 1;
439 Assert(itemText(iLastItem).isEmpty());
440
441 /* have previous command? check duplicate. */
442 if (iLastItem > 0)
443 {
444 const QString strPrevCommand(itemText(iLastItem - 1));
445 if (strCommand == strPrevCommand)
446 fNeedsAppending = false;
447 }
448
449 if (fNeedsAppending)
450 {
451 /* history full? drop the oldest command. */
452 if (count() == maxCount())
453 {
454 removeItem(0);
455 --iLastItem;
456 }
457
458 /* insert before the empty line. */
459 insertItem(iLastItem, strCommand);
460 }
461
462 /* invariant: empty line at the end */
463 int iNewLastItem = count() - 1;
464 Assert(itemText(iNewLastItem).isEmpty());
465
466 /* select empty line to present "new" command line to the user */
467 setCurrentIndex(iNewLastItem);
468}
469
470
471
472
473
474
475/*
476 *
477 * V B o x D b g C o n s o l e
478 * V B o x D b g C o n s o l e
479 * V B o x D b g C o n s o l e
480 *
481 *
482 */
483
484
485VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/, IVirtualBox *a_pVirtualBox/* = NULL */)
486 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent, "Console"), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
487 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
488 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
489 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
490 m_fTerminate(false), m_fThreadTerminated(false)
491{
492 /* Delete dialog on close: */
493 setAttribute(Qt::WA_DeleteOnClose);
494
495 /*
496 * Create the output text box.
497 */
498 m_pOutput = new VBoxDbgConsoleOutput(this, a_pVirtualBox);
499
500 /* try figure a suitable size */
501 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
502 pLabel->setFont(m_pOutput->font());
503 QSize Size = pLabel->sizeHint();
504 delete pLabel;
505 Size.setWidth((int)(Size.width() * 1.10));
506 Size.setHeight(Size.width() / 2);
507 resize(Size);
508
509 /*
510 * Create the input combo box (with a label).
511 */
512 QHBoxLayout *pLayout = new QHBoxLayout();
513 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
514
515 pLabel = new QLabel(" Command ");
516 pLayout->addWidget(pLabel);
517 pLabel->setMaximumSize(pLabel->sizeHint());
518 pLabel->setAlignment(Qt::AlignCenter);
519
520 m_pInput = new VBoxDbgConsoleInput(NULL);
521 pLayout->addWidget(m_pInput);
522 m_pInput->setDuplicatesEnabled(false);
523 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
524
525# if 0//def Q_WS_MAC
526 pLabel = new QLabel(" ");
527 pLayout->addWidget(pLabel);
528 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
529 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
530# endif
531
532 QWidget *pHBox = new QWidget(this);
533 pHBox->setLayout(pLayout);
534
535 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
536
537
538 /*
539 * Vertical layout box on the whole widget.
540 */
541 QVBoxLayout *pVLayout = new QVBoxLayout();
542 pVLayout->setContentsMargins(0, 0, 0, 0);
543 pVLayout->setSpacing(5);
544 pVLayout->addWidget(m_pOutput);
545 pVLayout->addWidget(pHBox);
546 setLayout(pVLayout);
547
548 /*
549 * The tab order is from input to output, not the other way around as it is by default.
550 */
551 setTabOrder(m_pInput, m_pOutput);
552 m_fInputRestoreFocus = true; /* hack */
553
554 /*
555 * Setup the timer.
556 */
557 m_pTimer = new QTimer(this);
558 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
559
560 /*
561 * Init the backend structure.
562 */
563 m_Back.Core.pfnInput = backInput;
564 m_Back.Core.pfnRead = backRead;
565 m_Back.Core.pfnWrite = backWrite;
566 m_Back.Core.pfnSetReady = backSetReady;
567 m_Back.pSelf = this;
568
569 /*
570 * Create the critical section, the event semaphore and the debug console thread.
571 */
572 int rc = RTCritSectInit(&m_Lock);
573 AssertRC(rc);
574
575 rc = RTSemEventCreate(&m_EventSem);
576 AssertRC(rc);
577
578 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
579 AssertRC(rc);
580 if (RT_FAILURE(rc))
581 m_Thread = NIL_RTTHREAD;
582
583 /*
584 * Shortcuts.
585 */
586 m_pFocusToInput = new QAction("", this);
587 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
588 addAction(m_pFocusToInput);
589 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
590
591 m_pFocusToOutput = new QAction("", this);
592 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
593 addAction(m_pFocusToOutput);
594 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
595
596 addAction(m_pOutput->m_pBlackOnWhiteAction);
597 addAction(m_pOutput->m_pGreenOnBlackAction);
598 addAction(m_pOutput->m_pCourierFontAction);
599 addAction(m_pOutput->m_pMonospaceFontAction);
600}
601
602
603VBoxDbgConsole::~VBoxDbgConsole()
604{
605 Assert(isGUIThread());
606
607 /*
608 * Wait for the thread.
609 */
610 ASMAtomicWriteBool(&m_fTerminate, true);
611 RTSemEventSignal(m_EventSem);
612 if (m_Thread != NIL_RTTHREAD)
613 {
614 int rc = RTThreadWait(m_Thread, 15000, NULL);
615 AssertRC(rc);
616 m_Thread = NIL_RTTHREAD;
617 }
618
619 /*
620 * Free resources.
621 */
622 delete m_pTimer;
623 m_pTimer = NULL;
624 RTCritSectDelete(&m_Lock);
625 RTSemEventDestroy(m_EventSem);
626 m_EventSem = 0;
627 m_pOutput = NULL;
628 m_pInput = NULL;
629 if (m_pszInputBuf)
630 {
631 RTMemFree(m_pszInputBuf);
632 m_pszInputBuf = NULL;
633 }
634 m_cbInputBuf = 0;
635 m_cbInputBufAlloc = 0;
636
637 delete m_pFocusToInput;
638 m_pFocusToInput = NULL;
639 delete m_pFocusToOutput;
640 m_pFocusToOutput = NULL;
641
642 if (m_pszOutputBuf)
643 {
644 RTMemFree(m_pszOutputBuf);
645 m_pszOutputBuf = NULL;
646 }
647}
648
649
650void
651VBoxDbgConsole::commandSubmitted(const QString &rCommand)
652{
653 Assert(isGUIThread());
654
655 lock();
656 RTSemEventSignal(m_EventSem);
657
658 QByteArray Utf8Array = rCommand.toUtf8();
659 const char *psz = Utf8Array.constData();
660 size_t cb = strlen(psz);
661
662 /*
663 * Make sure we've got space for the input.
664 */
665 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
666 {
667 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
668 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
669 if (!pv)
670 {
671 unlock();
672 return;
673 }
674 m_pszInputBuf = (char *)pv;
675 m_cbInputBufAlloc = cbNew;
676 }
677
678 /*
679 * Add the input and output it.
680 */
681 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
682 m_cbInputBuf += cb;
683 m_pszInputBuf[m_cbInputBuf++] = '\n';
684
685 m_pOutput->appendText(rCommand + "\n", true /*fClearSelection*/);
686 m_pOutput->ensureCursorVisible();
687
688 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
689 m_pInput->setEnabled(false);
690
691 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
692 unlock();
693}
694
695
696void
697VBoxDbgConsole::updateOutput()
698{
699 Assert(isGUIThread());
700
701 lock();
702 m_fUpdatePending = false;
703 if (m_cbOutputBuf)
704 {
705 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf), false /*fClearSelection*/);
706 m_cbOutputBuf = 0;
707 }
708 unlock();
709}
710
711
712/**
713 * Lock the object.
714 */
715void
716VBoxDbgConsole::lock()
717{
718 RTCritSectEnter(&m_Lock);
719}
720
721
722/**
723 * Unlocks the object.
724 */
725void
726VBoxDbgConsole::unlock()
727{
728 RTCritSectLeave(&m_Lock);
729}
730
731
732
733/**
734 * Checks if there is input.
735 *
736 * @returns true if there is input ready.
737 * @returns false if there not input ready.
738 * @param pBack Pointer to VBoxDbgConsole::m_Back.
739 * @param cMillies Number of milliseconds to wait on input data.
740 */
741/*static*/ DECLCALLBACK(bool)
742VBoxDbgConsole::backInput(PCDBGCIO pBack, uint32_t cMillies)
743{
744 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCIO(pBack);
745 pThis->lock();
746
747 bool fRc = true;
748 if (!pThis->m_cbInputBuf)
749 {
750 /*
751 * Wait outside the lock for the requested time, then check again.
752 */
753 pThis->unlock();
754 RTSemEventWait(pThis->m_EventSem, cMillies);
755 pThis->lock();
756 fRc = pThis->m_cbInputBuf
757 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
758 }
759
760 pThis->unlock();
761 return fRc;
762}
763
764
765/**
766 * Read input.
767 *
768 * @returns VBox status code.
769 * @param pBack Pointer to VBoxDbgConsole::m_Back.
770 * @param pvBuf Where to put the bytes we read.
771 * @param cbBuf Maximum nymber of bytes to read.
772 * @param pcbRead Where to store the number of bytes actually read.
773 * If NULL the entire buffer must be filled for a
774 * successful return.
775 */
776/*static*/ DECLCALLBACK(int)
777VBoxDbgConsole::backRead(PCDBGCIO pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
778{
779 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCIO(pBack);
780 Assert(pcbRead); /** @todo implement this bit */
781 if (pcbRead)
782 *pcbRead = 0;
783
784 pThis->lock();
785 int rc = VINF_SUCCESS;
786 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
787 {
788 if (pThis->m_cbInputBuf)
789 {
790 const char *psz = pThis->m_pszInputBuf;
791 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
792 memcpy(pvBuf, psz, cbRead);
793 psz += cbRead;
794 pThis->m_cbInputBuf -= cbRead;
795 if (*psz)
796 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
797 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
798 *pcbRead = cbRead;
799 }
800 }
801 else
802 rc = VERR_GENERAL_FAILURE;
803 pThis->unlock();
804 return rc;
805}
806
807
808/**
809 * Write (output).
810 *
811 * @returns VBox status code.
812 * @param pBack Pointer to VBoxDbgConsole::m_Back.
813 * @param pvBuf What to write.
814 * @param cbBuf Number of bytes to write.
815 * @param pcbWritten Where to store the number of bytes actually written.
816 * If NULL the entire buffer must be successfully written.
817 */
818/*static*/ DECLCALLBACK(int)
819VBoxDbgConsole::backWrite(PCDBGCIO pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
820{
821 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCIO(pBack);
822 int rc = VINF_SUCCESS;
823
824 pThis->lock();
825 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
826 {
827 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
828 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
829 if (!pv)
830 {
831 pThis->unlock();
832 if (pcbWritten)
833 *pcbWritten = 0;
834 return VERR_NO_MEMORY;
835 }
836 pThis->m_pszOutputBuf = (char *)pv;
837 pThis->m_cbOutputBufAlloc = cbNew;
838 }
839
840 /*
841 * Add the output.
842 */
843 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
844 pThis->m_cbOutputBuf += cbBuf;
845 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
846 if (pcbWritten)
847 *pcbWritten = cbBuf;
848
849 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
850 rc = VERR_GENERAL_FAILURE;
851
852 /*
853 * Tell the GUI thread to draw this text.
854 * We cannot do it from here without frequent crashes.
855 */
856 if (!pThis->m_fUpdatePending)
857 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
858
859 pThis->unlock();
860
861 return rc;
862}
863
864
865/*static*/ DECLCALLBACK(void)
866VBoxDbgConsole::backSetReady(PCDBGCIO pBack, bool fReady)
867{
868 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCIO(pBack);
869 if (fReady)
870 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
871}
872
873
874/**
875 * The Debugger Console Thread
876 *
877 * @returns VBox status code (ignored).
878 * @param Thread The thread handle.
879 * @param pvUser Pointer to the VBoxDbgConsole object.s
880 */
881/*static*/ DECLCALLBACK(int)
882VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
883{
884 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
885 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
886
887 NOREF(Thread);
888
889 /*
890 * Create and execute the console.
891 */
892 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
893
894 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
895 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
896 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
897 ? VBoxDbgConsoleEvent::kTerminatedUser
898 : VBoxDbgConsoleEvent::kTerminatedOther));
899 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
900 return rc;
901}
902
903
904bool
905VBoxDbgConsole::event(QEvent *pGenEvent)
906{
907 Assert(isGUIThread());
908 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
909 {
910 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
911
912 switch (pEvent->command())
913 {
914 /* make update pending. */
915 case VBoxDbgConsoleEvent::kUpdate:
916 lock();
917 if (!m_fUpdatePending)
918 {
919 m_fUpdatePending = true;
920 m_pTimer->setSingleShot(true);
921 m_pTimer->start(10);
922 }
923 unlock();
924 break;
925
926 /* Re-enable the input field and restore focus. */
927 case VBoxDbgConsoleEvent::kInputEnable:
928 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
929 m_pInput->setEnabled(true);
930 if ( m_fInputRestoreFocus
931 && !m_pInput->hasFocus())
932 m_pInput->setFocus(); /* this is a hack. */
933 m_fInputRestoreFocus = false;
934 break;
935
936 /* The thread terminated by user command (exit, quit, bye). */
937 case VBoxDbgConsoleEvent::kTerminatedUser:
938 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
939 m_pInput->setEnabled(false);
940 close();
941 break;
942
943 /* The thread terminated for some unknown reason., disable input */
944 case VBoxDbgConsoleEvent::kTerminatedOther:
945 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
946 m_pInput->setEnabled(false);
947 break;
948
949 /* paranoia */
950 default:
951 AssertMsgFailed(("command=%d\n", pEvent->command()));
952 break;
953 }
954 return true;
955 }
956
957 return VBoxDbgBaseWindow::event(pGenEvent);
958}
959
960
961void
962VBoxDbgConsole::keyReleaseEvent(QKeyEvent *pEvent)
963{
964 //RTAssertMsg2("VBoxDbgConsole::keyReleaseEvent: %d (%#x); mod=%#x\n", pEvent->key(), pEvent->key(), pEvent->modifiers());
965 switch (pEvent->key())
966 {
967 case Qt::Key_F5:
968 if (pEvent->modifiers() == 0)
969 commandSubmitted("g");
970 break;
971
972 case Qt::Key_F8:
973 if (pEvent->modifiers() == 0)
974 commandSubmitted("t");
975 break;
976
977 case Qt::Key_F10:
978 if (pEvent->modifiers() == 0)
979 commandSubmitted("p");
980 break;
981
982 case Qt::Key_F11:
983 if (pEvent->modifiers() == 0)
984 commandSubmitted("t");
985 else if (pEvent->modifiers() == Qt::ShiftModifier)
986 commandSubmitted("gu");
987 break;
988
989 case Qt::Key_Cancel: /* == break */
990 if (pEvent->modifiers() == Qt::ControlModifier)
991 commandSubmitted("stop");
992 break;
993 case Qt::Key_Delete:
994 if (pEvent->modifiers() == Qt::AltModifier)
995 commandSubmitted("stop");
996 break;
997 }
998}
999
1000
1001void
1002VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
1003{
1004 if (m_fThreadTerminated)
1005 a_pCloseEvt->accept();
1006}
1007
1008
1009void
1010VBoxDbgConsole::actFocusToInput()
1011{
1012 if (!m_pInput->hasFocus())
1013 m_pInput->setFocus(Qt::ShortcutFocusReason);
1014}
1015
1016
1017void
1018VBoxDbgConsole::actFocusToOutput()
1019{
1020 if (!m_pOutput->hasFocus())
1021 m_pOutput->setFocus(Qt::ShortcutFocusReason);
1022}
1023
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