VirtualBox

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

Last change on this file since 31510 was 31510, checked in by vboxsync, 14 years ago

The debugger is back in the OSE.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/* $Id: VBoxDbgConsole.cpp 31510 2010-08-10 08:48:11Z vboxsync $ */
2/** @file
3 * VBox Debugger GUI - Console.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * Oracle Corporation confidential
10 * All rights reserved
11 */
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#define LOG_GROUP LOG_GROUP_DBGG
18#include "VBoxDbgConsole.h"
19
20#include <QLabel>
21#include <QApplication>
22#include <QFont>
23#include <QLineEdit>
24#include <QHBoxLayout>
25#include <QAction>
26#include <QContextMenuEvent>
27
28#include <VBox/dbg.h>
29#include <VBox/cfgm.h>
30#include <VBox/err.h>
31
32#include <iprt/thread.h>
33#include <iprt/tcp.h>
34#include <VBox/log.h>
35#include <iprt/assert.h>
36#include <iprt/asm.h>
37#include <iprt/alloc.h>
38#include <iprt/string.h>
39
40
41
42
43/*
44 *
45 * V B o x D b g C o n s o l e O u t p u t
46 * V B o x D b g C o n s o l e O u t p u t
47 * V B o x D b g C o n s o l e O u t p u t
48 *
49 *
50 */
51
52
53VBoxDbgConsoleOutput::VBoxDbgConsoleOutput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
54 : QTextEdit(pParent), m_uCurLine(0), m_uCurPos(0), m_hGUIThread(RTThreadNativeSelf())
55{
56 setReadOnly(true);
57 setUndoRedoEnabled(false);
58 setOverwriteMode(false);
59 setPlainText("");
60 setTextInteractionFlags(Qt::TextBrowserInteraction);
61 setAutoFormatting(QTextEdit::AutoAll);
62 setTabChangesFocus(true);
63 setAcceptRichText(false);
64
65#ifdef Q_WS_MAC
66 QFont Font("Monaco", 10, QFont::Normal, FALSE);
67 Font.setStyleStrategy(QFont::NoAntialias);
68#else
69 QFont Font = font();
70 Font.setStyleHint(QFont::TypeWriter);
71 Font.setFamily("Courier [Monotype]");
72#endif
73 setFont(Font);
74
75 /* green on black */
76 QPalette Pal(palette());
77 Pal.setColor(QPalette::All, QPalette::Base, QColor(Qt::black));
78 setPalette(Pal);
79 setTextColor(QColor(qRgb(0, 0xe0, 0)));
80 NOREF(pszName);
81}
82
83
84VBoxDbgConsoleOutput::~VBoxDbgConsoleOutput()
85{
86 Assert(m_hGUIThread == RTThreadNativeSelf());
87}
88
89
90void
91VBoxDbgConsoleOutput::appendText(const QString &rStr)
92{
93 Assert(m_hGUIThread == RTThreadNativeSelf());
94
95 if (rStr.isEmpty() || rStr.isNull() || !rStr.length())
96 return;
97
98 /*
99 * Insert all in one go and make sure it's visible.
100 */
101 QTextCursor Cursor = textCursor();
102 if (!Cursor.atEnd())
103 moveCursor(QTextCursor::End); /* make sure we append the text */
104 Cursor.insertText(rStr);
105 ensureCursorVisible();
106}
107
108
109
110
111/*
112 *
113 * V B o x D b g C o n s o l e I n p u t
114 * V B o x D b g C o n s o l e I n p u t
115 * V B o x D b g C o n s o l e I n p u t
116 *
117 *
118 */
119
120
121VBoxDbgConsoleInput::VBoxDbgConsoleInput(QWidget *pParent/* = NULL*/, const char *pszName/* = NULL*/)
122 : QComboBox(pParent), m_iBlankItem(0), m_hGUIThread(RTThreadNativeSelf())
123{
124 insertItem(m_iBlankItem, "");
125 setEditable(true);
126 setInsertPolicy(NoInsert);
127 setAutoCompletion(false);
128 setMaxCount(50);
129 const QLineEdit *pEdit = lineEdit();
130 if (pEdit)
131 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
132
133 NOREF(pszName);
134}
135
136
137VBoxDbgConsoleInput::~VBoxDbgConsoleInput()
138{
139 Assert(m_hGUIThread == RTThreadNativeSelf());
140}
141
142
143void
144VBoxDbgConsoleInput::setLineEdit(QLineEdit *pEdit)
145{
146 Assert(m_hGUIThread == RTThreadNativeSelf());
147 QComboBox::setLineEdit(pEdit);
148 if (lineEdit() == pEdit && pEdit)
149 connect(pEdit, SIGNAL(returnPressed()), this, SLOT(returnPressed()));
150}
151
152
153void
154VBoxDbgConsoleInput::returnPressed()
155{
156 Assert(m_hGUIThread == RTThreadNativeSelf());
157 /* deal with the current command. */
158 QString Str = currentText();
159 emit commandSubmitted(Str);
160
161 /* update the history and clear the entry field */
162 QString PrevStr = m_iBlankItem > 0 ? itemText(m_iBlankItem - 1) : "";
163 if (PrevStr != Str)
164 {
165 setItemText(m_iBlankItem, Str);
166 if ( m_iBlankItem > 0
167 && m_iBlankItem >= maxCount() - 1)
168 removeItem(m_iBlankItem - maxCount() - 1);
169 insertItem(++m_iBlankItem, "");
170 }
171
172 clearEditText();
173 setCurrentIndex(m_iBlankItem);
174}
175
176
177
178
179
180
181/*
182 *
183 * V B o x D b g C o n s o l e
184 * V B o x D b g C o n s o l e
185 * V B o x D b g C o n s o l e
186 *
187 *
188 */
189
190
191VBoxDbgConsole::VBoxDbgConsole(VBoxDbgGui *a_pDbgGui, QWidget *a_pParent/* = NULL*/)
192 : VBoxDbgBaseWindow(a_pDbgGui, a_pParent), m_pOutput(NULL), m_pInput(NULL), m_fInputRestoreFocus(false),
193 m_pszInputBuf(NULL), m_cbInputBuf(0), m_cbInputBufAlloc(0),
194 m_pszOutputBuf(NULL), m_cbOutputBuf(0), m_cbOutputBufAlloc(0),
195 m_pTimer(NULL), m_fUpdatePending(false), m_Thread(NIL_RTTHREAD), m_EventSem(NIL_RTSEMEVENT),
196 m_fTerminate(false), m_fThreadTerminated(false)
197{
198 setWindowTitle("VBoxDbg - Console");
199
200 /*
201 * Create the output text box.
202 */
203 m_pOutput = new VBoxDbgConsoleOutput(this);
204
205 /* try figure a suitable size */
206 QLabel *pLabel = new QLabel( "11111111111111111111111111111111111111111111111111111111111111111111111111111112222222222", this);
207 pLabel->setFont(m_pOutput->font());
208 QSize Size = pLabel->sizeHint();
209 delete pLabel;
210 Size.setWidth((int)(Size.width() * 1.10));
211 Size.setHeight(Size.width() / 2);
212 resize(Size);
213
214 /*
215 * Create the input combo box (with a label).
216 */
217 QHBoxLayout *pLayout = new QHBoxLayout();
218 //pLayout->setSizeConstraint(QLayout::SetMaximumSize);
219
220 pLabel = new QLabel(" Command ");
221 pLayout->addWidget(pLabel);
222 pLabel->setMaximumSize(pLabel->sizeHint());
223 pLabel->setAlignment(Qt::AlignCenter);
224
225 m_pInput = new VBoxDbgConsoleInput(NULL);
226 pLayout->addWidget(m_pInput);
227 m_pInput->setDuplicatesEnabled(false);
228 connect(m_pInput, SIGNAL(commandSubmitted(const QString &)), this, SLOT(commandSubmitted(const QString &)));
229
230# if 0//def Q_WS_MAC
231 pLabel = new QLabel(" ");
232 pLayout->addWidget(pLabel);
233 pLabel->setMaximumSize(20, m_pInput->sizeHint().height() + 6);
234 pLabel->setMinimumSize(20, m_pInput->sizeHint().height() + 6);
235# endif
236
237 QWidget *pHBox = new QWidget(this);
238 pHBox->setLayout(pLayout);
239
240 m_pInput->setEnabled(false); /* (we'll get a ready notification) */
241
242
243 /*
244 * Vertical layout box on the whole widget.
245 */
246 QVBoxLayout *pVLayout = new QVBoxLayout();
247 pVLayout->setContentsMargins(0, 0, 0, 0);
248 pVLayout->setSpacing(5);
249 pVLayout->addWidget(m_pOutput);
250 pVLayout->addWidget(pHBox);
251 setLayout(pVLayout);
252
253 /*
254 * The tab order is from input to output, not the otherway around as it is by default.
255 */
256 setTabOrder(m_pInput, m_pOutput);
257
258 /*
259 * Setup the timer.
260 */
261 m_pTimer = new QTimer(this);
262 connect(m_pTimer, SIGNAL(timeout()), SLOT(updateOutput()));
263
264 /*
265 * Init the backend structure.
266 */
267 m_Back.Core.pfnInput = backInput;
268 m_Back.Core.pfnRead = backRead;
269 m_Back.Core.pfnWrite = backWrite;
270 m_Back.Core.pfnSetReady = backSetReady;
271 m_Back.pSelf = this;
272
273 /*
274 * Create the critical section, the event semaphore and the debug console thread.
275 */
276 int rc = RTCritSectInit(&m_Lock);
277 AssertRC(rc);
278
279 rc = RTSemEventCreate(&m_EventSem);
280 AssertRC(rc);
281
282 rc = RTThreadCreate(&m_Thread, backThread, this, 0, RTTHREADTYPE_DEBUGGER, RTTHREADFLAGS_WAITABLE, "VBoxDbgC");
283 AssertRC(rc);
284 if (RT_FAILURE(rc))
285 m_Thread = NIL_RTTHREAD;
286
287 /*
288 * Shortcuts.
289 */
290 m_pFocusToInput = new QAction("", this);
291 m_pFocusToInput->setShortcut(QKeySequence("Ctrl+L"));
292 addAction(m_pFocusToInput);
293 connect(m_pFocusToInput, SIGNAL(triggered(bool)), this, SLOT(actFocusToInput()));
294
295 m_pFocusToOutput = new QAction("", this);
296 m_pFocusToOutput->setShortcut(QKeySequence("Ctrl+O"));
297 addAction(m_pFocusToOutput);
298 connect(m_pFocusToOutput, SIGNAL(triggered(bool)), this, SLOT(actFocusToOutput()));
299}
300
301
302VBoxDbgConsole::~VBoxDbgConsole()
303{
304 Assert(isGUIThread());
305
306 /*
307 * Wait for the thread.
308 */
309 ASMAtomicWriteBool(&m_fTerminate, true);
310 RTSemEventSignal(m_EventSem);
311 if (m_Thread != NIL_RTTHREAD)
312 {
313 int rc = RTThreadWait(m_Thread, 15000, NULL);
314 AssertRC(rc);
315 m_Thread = NIL_RTTHREAD;
316 }
317
318 /*
319 * Free resources.
320 */
321 delete m_pTimer;
322 m_pTimer = NULL;
323 RTCritSectDelete(&m_Lock);
324 RTSemEventDestroy(m_EventSem);
325 m_EventSem = 0;
326 m_pOutput = NULL;
327 m_pInput = NULL;
328 if (m_pszInputBuf)
329 {
330 RTMemFree(m_pszInputBuf);
331 m_pszInputBuf = NULL;
332 }
333 m_cbInputBuf = 0;
334 m_cbInputBufAlloc = 0;
335
336 delete m_pFocusToInput;
337 m_pFocusToInput = NULL;
338 delete m_pFocusToOutput;
339 m_pFocusToOutput = NULL;
340}
341
342
343void
344VBoxDbgConsole::commandSubmitted(const QString &rCommand)
345{
346 Assert(isGUIThread());
347
348 lock();
349 RTSemEventSignal(m_EventSem);
350
351 QByteArray Utf8Array = rCommand.toUtf8();
352 const char *psz = Utf8Array.constData();
353 size_t cb = strlen(psz);
354
355 /*
356 * Make sure we've got space for the input.
357 */
358 if (cb + m_cbInputBuf >= m_cbInputBufAlloc)
359 {
360 size_t cbNew = RT_ALIGN_Z(cb + m_cbInputBufAlloc + 1, 128);
361 void *pv = RTMemRealloc(m_pszInputBuf, cbNew);
362 if (!pv)
363 {
364 unlock();
365 return;
366 }
367 m_pszInputBuf = (char *)pv;
368 m_cbInputBufAlloc = cbNew;
369 }
370
371 /*
372 * Add the input and output it.
373 */
374 memcpy(m_pszInputBuf + m_cbInputBuf, psz, cb);
375 m_cbInputBuf += cb;
376 m_pszInputBuf[m_cbInputBuf++] = '\n';
377
378 m_pOutput->appendText(rCommand + "\n");
379 m_pOutput->ensureCursorVisible();
380
381 m_fInputRestoreFocus = m_pInput->hasFocus(); /* dirty focus hack */
382 m_pInput->setEnabled(false);
383
384 Log(("VBoxDbgConsole::commandSubmitted: %s (input-enabled=%RTbool)\n", psz, m_pInput->isEnabled()));
385 unlock();
386}
387
388
389void
390VBoxDbgConsole::updateOutput()
391{
392 Assert(isGUIThread());
393
394 lock();
395 m_fUpdatePending = false;
396 if (m_cbOutputBuf)
397 {
398 m_pOutput->appendText(QString::fromUtf8((const char *)m_pszOutputBuf, (int)m_cbOutputBuf));
399 m_cbOutputBuf = 0;
400 }
401 unlock();
402}
403
404
405/**
406 * Lock the object.
407 */
408void
409VBoxDbgConsole::lock()
410{
411 RTCritSectEnter(&m_Lock);
412}
413
414
415/**
416 * Unlocks the object.
417 */
418void
419VBoxDbgConsole::unlock()
420{
421 RTCritSectLeave(&m_Lock);
422}
423
424
425
426/**
427 * Checks if there is input.
428 *
429 * @returns true if there is input ready.
430 * @returns false if there not input ready.
431 * @param pBack Pointer to VBoxDbgConsole::m_Back.
432 * @param cMillies Number of milliseconds to wait on input data.
433 */
434/*static*/ DECLCALLBACK(bool)
435VBoxDbgConsole::backInput(PDBGCBACK pBack, uint32_t cMillies)
436{
437 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
438 pThis->lock();
439
440 bool fRc = true;
441 if (!pThis->m_cbInputBuf)
442 {
443 /*
444 * Wait outside the lock for the requested time, then check again.
445 */
446 pThis->unlock();
447 RTSemEventWait(pThis->m_EventSem, cMillies);
448 pThis->lock();
449 fRc = pThis->m_cbInputBuf
450 || ASMAtomicUoReadBool(&pThis->m_fTerminate);
451 }
452
453 pThis->unlock();
454 return fRc;
455}
456
457
458/**
459 * Read input.
460 *
461 * @returns VBox status code.
462 * @param pBack Pointer to VBoxDbgConsole::m_Back.
463 * @param pvBuf Where to put the bytes we read.
464 * @param cbBuf Maximum nymber of bytes to read.
465 * @param pcbRead Where to store the number of bytes actually read.
466 * If NULL the entire buffer must be filled for a
467 * successful return.
468 */
469/*static*/ DECLCALLBACK(int)
470VBoxDbgConsole::backRead(PDBGCBACK pBack, void *pvBuf, size_t cbBuf, size_t *pcbRead)
471{
472 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
473 Assert(pcbRead); /** @todo implement this bit */
474 if (pcbRead)
475 *pcbRead = 0;
476
477 pThis->lock();
478 int rc = VINF_SUCCESS;
479 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
480 {
481 if (pThis->m_cbInputBuf)
482 {
483 const char *psz = pThis->m_pszInputBuf;
484 size_t cbRead = RT_MIN(pThis->m_cbInputBuf, cbBuf);
485 memcpy(pvBuf, psz, cbRead);
486 psz += cbRead;
487 pThis->m_cbInputBuf -= cbRead;
488 if (*psz)
489 memmove(pThis->m_pszInputBuf, psz, pThis->m_cbInputBuf);
490 pThis->m_pszInputBuf[pThis->m_cbInputBuf] = '\0';
491 *pcbRead = cbRead;
492 }
493 }
494 else
495 rc = VERR_GENERAL_FAILURE;
496 pThis->unlock();
497 return rc;
498}
499
500
501/**
502 * Write (output).
503 *
504 * @returns VBox status code.
505 * @param pBack Pointer to VBoxDbgConsole::m_Back.
506 * @param pvBuf What to write.
507 * @param cbBuf Number of bytes to write.
508 * @param pcbWritten Where to store the number of bytes actually written.
509 * If NULL the entire buffer must be successfully written.
510 */
511/*static*/ DECLCALLBACK(int)
512VBoxDbgConsole::backWrite(PDBGCBACK pBack, const void *pvBuf, size_t cbBuf, size_t *pcbWritten)
513{
514 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
515 int rc = VINF_SUCCESS;
516
517 pThis->lock();
518 if (cbBuf + pThis->m_cbOutputBuf >= pThis->m_cbOutputBufAlloc)
519 {
520 size_t cbNew = RT_ALIGN_Z(cbBuf + pThis->m_cbOutputBufAlloc + 1, 1024);
521 void *pv = RTMemRealloc(pThis->m_pszOutputBuf, cbNew);
522 if (!pv)
523 {
524 pThis->unlock();
525 if (pcbWritten)
526 *pcbWritten = 0;
527 return VERR_NO_MEMORY;
528 }
529 pThis->m_pszOutputBuf = (char *)pv;
530 pThis->m_cbOutputBufAlloc = cbNew;
531 }
532
533 /*
534 * Add the output.
535 */
536 memcpy(pThis->m_pszOutputBuf + pThis->m_cbOutputBuf, pvBuf, cbBuf);
537 pThis->m_cbOutputBuf += cbBuf;
538 pThis->m_pszOutputBuf[pThis->m_cbOutputBuf] = '\0';
539 if (pcbWritten)
540 *pcbWritten = cbBuf;
541
542 if (ASMAtomicUoReadBool(&pThis->m_fTerminate))
543 rc = VERR_GENERAL_FAILURE;
544
545 /*
546 * Tell the GUI thread to draw this text.
547 * We cannot do it from here without frequent crashes.
548 */
549 if (!pThis->m_fUpdatePending)
550 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kUpdate));
551
552 pThis->unlock();
553
554 return rc;
555}
556
557
558/*static*/ DECLCALLBACK(void)
559VBoxDbgConsole::backSetReady(PDBGCBACK pBack, bool fReady)
560{
561 VBoxDbgConsole *pThis = VBOXDBGCONSOLE_FROM_DBGCBACK(pBack);
562 if (fReady)
563 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(VBoxDbgConsoleEvent::kInputEnable));
564}
565
566
567/**
568 * The Debugger Console Thread
569 *
570 * @returns VBox status code (ignored).
571 * @param Thread The thread handle.
572 * @param pvUser Pointer to the VBoxDbgConsole object.s
573 */
574/*static*/ DECLCALLBACK(int)
575VBoxDbgConsole::backThread(RTTHREAD Thread, void *pvUser)
576{
577 VBoxDbgConsole *pThis = (VBoxDbgConsole *)pvUser;
578 LogFlow(("backThread: Thread=%p pvUser=%p\n", (void *)Thread, pvUser));
579
580 NOREF(Thread);
581
582 /*
583 * Create and execute the console.
584 */
585 int rc = pThis->dbgcCreate(&pThis->m_Back.Core, 0);
586
587 ASMAtomicUoWriteBool(&pThis->m_fThreadTerminated, true);
588 if (!ASMAtomicUoReadBool(&pThis->m_fTerminate))
589 QApplication::postEvent(pThis, new VBoxDbgConsoleEvent(rc == VINF_SUCCESS
590 ? VBoxDbgConsoleEvent::kTerminatedUser
591 : VBoxDbgConsoleEvent::kTerminatedOther));
592 LogFlow(("backThread: returns %Rrc (m_fTerminate=%RTbool)\n", rc, ASMAtomicUoReadBool(&pThis->m_fTerminate)));
593 return rc;
594}
595
596
597bool
598VBoxDbgConsole::event(QEvent *pGenEvent)
599{
600 Assert(isGUIThread());
601 if (pGenEvent->type() == (QEvent::Type)VBoxDbgConsoleEvent::kEventNumber)
602 {
603 VBoxDbgConsoleEvent *pEvent = (VBoxDbgConsoleEvent *)pGenEvent;
604
605 switch (pEvent->command())
606 {
607 /* make update pending. */
608 case VBoxDbgConsoleEvent::kUpdate:
609 lock();
610 if (!m_fUpdatePending)
611 {
612 m_fUpdatePending = true;
613 m_pTimer->setSingleShot(true);
614 m_pTimer->start(10);
615 }
616 unlock();
617 break;
618
619 /* Re-enable the input field and restore focus. */
620 case VBoxDbgConsoleEvent::kInputEnable:
621 Log(("VBoxDbgConsole: kInputEnable (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
622 m_pInput->setEnabled(true);
623 if ( m_fInputRestoreFocus
624 && !m_pInput->hasFocus())
625 m_pInput->setFocus(); /* this is a hack. */
626 m_fInputRestoreFocus = false;
627 break;
628
629 /* The thread terminated by user command (exit, quit, bye). */
630 case VBoxDbgConsoleEvent::kTerminatedUser:
631 Log(("VBoxDbgConsole: kTerminatedUser (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
632 m_pInput->setEnabled(false);
633 close();
634 break;
635
636 /* The thread terminated for some unknown reason., disable input */
637 case VBoxDbgConsoleEvent::kTerminatedOther:
638 Log(("VBoxDbgConsole: kTerminatedOther (input-enabled=%RTbool)\n", m_pInput->isEnabled()));
639 m_pInput->setEnabled(false);
640 break;
641
642 /* paranoia */
643 default:
644 AssertMsgFailed(("command=%d\n", pEvent->command()));
645 break;
646 }
647 return true;
648 }
649
650 return VBoxDbgBaseWindow::event(pGenEvent);
651}
652
653
654void
655VBoxDbgConsole::closeEvent(QCloseEvent *a_pCloseEvt)
656{
657 if (m_fThreadTerminated)
658 {
659 a_pCloseEvt->accept();
660 delete this;
661 }
662}
663
664
665void
666VBoxDbgConsole::actFocusToInput()
667{
668 if (!m_pInput->hasFocus())
669 m_pInput->setFocus(Qt::ShortcutFocusReason);
670}
671
672
673void
674VBoxDbgConsole::actFocusToOutput()
675{
676 if (!m_pOutput->hasFocus())
677 m_pOutput->setFocus(Qt::ShortcutFocusReason);
678}
679
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