1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "gdbengine.h"
37 #include "debuggerstartparameters.h"
38 #include "abstractgdbadapter.h"
39 #include "debuggeractions.h"
40 #include "debuggercore.h"
41 #include "debuggerstringutils.h"
43 #include "stackhandler.h"
44 #include "watchhandler.h"
46 #include <utils/qtcassert.h>
47 #include <utils/savedaction.h>
49 #include <QtCore/QFile>
50 #include <QtCore/QFileInfo>
51 #include <QtGui/QMessageBox>
53 #if !defined(Q_OS_WIN)
58 #define PRECONDITION QTC_ASSERT(!hasPython(), /**/)
59 #define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
62 //#define DEBUG_PENDING 1
63 //#define DEBUG_SUBITEM 1
66 # define PENDING_DEBUG(s) qDebug() << s
68 # define PENDING_DEBUG(s)
70 #define PENDING_DEBUGX(s) qDebug() << s
75 static bool isAccessSpecifier(const QByteArray &ba)
77 return ba == "private" || ba == "protected" || ba == "public";
80 // reads a MI-encoded item frome the consolestream
81 static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
83 GdbMi output = response.data.findChild("consolestreamoutput");
84 QByteArray out = output.data();
86 int markerPos = out.indexOf('"') + 1; // position of 'success marker'
87 if (markerPos == 0 || out.at(markerPos) == 'f') { // 't' or 'f'
88 // custom dumper produced no output
92 out = out.mid(markerPos + 1);
93 out = out.left(out.lastIndexOf('"'));
94 // optimization: dumper output never needs real C unquoting
95 out.replace('\\', "");
97 contents->fromStringMultiple(out);
98 //qDebug() << "CONTENTS" << contents->toString(true);
99 return contents->isValid();
102 static double getDumperVersion(const GdbMi &contents)
104 const GdbMi dumperVersionG = contents.findChild("dumperversion");
105 if (dumperVersionG.type() != GdbMi::Invalid) {
107 const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
115 void GdbEngine::updateLocalsClassic(const QVariant &cookie)
118 m_processedNames.clear();
120 //PENDING_DEBUG("\nRESET PENDING");
121 //m_toolTipCache.clear();
123 watchHandler()->beginCycle();
125 QByteArray level = QByteArray::number(currentFrame());
126 // '2' is 'list with type and value'
127 QByteArray cmd = "-stack-list-arguments 2 " + level + ' ' + level;
128 postCommand(cmd, WatchUpdate,
129 CB(handleStackListArgumentsClassic));
130 // '2' is 'list with type and value'
131 postCommand("-stack-list-locals 2", WatchUpdate,
132 CB(handleStackListLocalsClassic), cookie); // stage 2/2
135 static inline QString msgRetrievingWatchData(int pending)
137 return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending);
140 void GdbEngine::runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren)
142 Q_UNUSED(dumpChildren)
143 QByteArray type = data.type;
146 if (type == "QString" || type.endsWith("::QString"))
147 cmd = "qdumpqstring (&(" + data.exp + "))";
148 else if (type == "QStringList" || type.endsWith("::QStringList"))
149 cmd = "qdumpqstringlist (&(" + data.exp + "))";
153 postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3Classic), var);
155 showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000);
158 void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChildren)
161 if (m_debuggingHelperState != DebuggingHelperAvailable) {
162 runDirectDebuggingHelperClassic(data0, dumpChildren);
165 WatchData data = data0;
167 // Avoid endless loops created by faulty dumpers.
168 QByteArray processedName = QByteArray::number(dumpChildren) + '-' + data.iname;
169 if (m_processedNames.contains(processedName)) {
171 _("<Breaking endless loop for " + data.iname + '>'), LogMiscInput);
172 data.setAllUnneeded();
173 data.setValue(_("<unavailable>"));
174 data.setHasChildren(false);
178 m_processedNames.insert(processedName);
181 QList<QByteArray> extraArgs;
182 const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
183 m_dumperHelper.evaluationParameters(data, td, ¶ms, &extraArgs);
185 //int protocol = (data.iname.startsWith("watch") && data.type == "QImage") ? 3 : 2;
186 //int protocol = data.iname.startsWith("watch") ? 3 : 2;
187 const int protocol = 2;
188 //int protocol = isDisplayedIName(data.iname) ? 3 : 2;
192 addr = "(void*)" + data.hexAddress();
193 else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem
194 addr = QByteArray(1, '0');
196 addr = "&(" + data.exp + ')';
198 sendWatchParameters(params);
200 QByteArray cmd = "call (void*)qDumpObjectData440("
201 + QByteArray::number(protocol)
205 + (dumpChildren ? '1' : '0');
206 foreach (const QByteArray &ex, extraArgs)
210 postCommand(cmd, WatchUpdate | NonCriticalResponse);
212 showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000);
215 postCommand("p (char*)&qDumpOutBuffer", WatchUpdate,
216 CB(handleDebuggingHelperValue2Classic), qVariantFromValue(data));
219 void GdbEngine::createGdbVariableClassic(const WatchData &data)
222 if (data.iname == "local.flist.0") {
226 postCommand("-var-delete \"" + data.iname + '"', WatchUpdate);
227 QByteArray exp = data.exp;
228 if (exp.isEmpty() && data.address)
229 exp = "*(" + gdbQuoteTypes(data.type) + "*)" + data.hexAddress();
230 QVariant val = QVariant::fromValue<WatchData>(data);
231 postCommand("-var-create \"" + data.iname + "\" * \"" + exp + '"',
232 WatchUpdate, CB(handleVarCreate), val);
235 void GdbEngine::updateSubItemClassic(const WatchData &data0)
238 WatchData data = data0;
240 qDebug() << "UPDATE SUBITEM:" << data.toString();
242 QTC_ASSERT(data.isValid(), return);
244 // in any case we need the type first
245 if (data.isTypeNeeded()) {
246 // This should only happen if we don't have a variable yet.
247 // Let's play safe, though.
248 if (!data.variable.isEmpty()) {
249 // Update: It does so for out-of-scope watchers.
251 qDebug() << "FIXME: GdbEngine::updateSubItem:"
252 << data.toString() << "should not happen";
254 data.setType(WatchData::msgNotInScope());
255 data.setValue(WatchData::msgNotInScope());
256 data.setHasChildren(false);
261 // The WatchVarCreate handler will receive type information
262 // and re-insert a WatchData item with correct type, so
263 // we will not re-enter this bit.
264 // FIXME: Concurrency issues?
265 createGdbVariableClassic(data);
269 // We should have a type now. This is relied upon further below.
270 QTC_ASSERT(!data.type.isEmpty(), return);
272 // A common case that can be easily solved.
273 if (data.isChildrenNeeded() && isPointerType(data.type)
274 && !hasDebuggingHelperForType(data.type)) {
275 // We sometimes know what kind of children pointers have
277 qDebug() << "IT'S A POINTER";
280 if (debuggerCore()->boolSetting(AutoDerefPointers)) {
281 // Try automatic dereferentiation
282 data.exp = "(*(" + data.exp + "))";
283 data.type = data.type + '.'; // FIXME: fragile HACK to avoid recursion
286 data.setChildrenUnneeded();
289 data1.iname = data.iname + ".*";
290 data1.name = QLatin1Char('*') + data.name;
291 data1.exp = "(*(" + data.exp + "))";
292 data1.type = stripPointerType(data.type);
293 data1.setValueNeeded();
294 data1.setChildrenUnneeded();
300 if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) {
302 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
304 runDebuggingHelperClassic(data,
305 watchHandler()->isExpandedIName(data.iname));
310 if (data.isValueNeeded() && data.exp.isEmpty()) {
312 qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
314 data.setError("<no expression given>");
320 if (data.isValueNeeded() && data.variable.isEmpty()) {
322 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
324 createGdbVariableClassic(data);
325 // the WatchVarCreate handler will re-insert a WatchData
326 // item, with valueNeeded() set.
330 if (data.isValueNeeded()) {
331 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
333 qDebug() << "UPDATE SUBITEM: VALUE";
335 QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"';
336 postCommand(cmd, WatchUpdate,
337 CB(handleEvaluateExpressionClassic), QVariant::fromValue(data));
341 if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
343 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
345 runDebuggingHelperClassic(data, true);
349 if (data.isChildrenNeeded() && data.variable.isEmpty()) {
351 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
353 createGdbVariableClassic(data);
354 // the WatchVarCreate handler will re-insert a WatchData
355 // item, with childrenNeeded() set.
359 if (data.isChildrenNeeded()) {
360 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
361 QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
362 postCommand(cmd, WatchUpdate,
363 CB(handleVarListChildrenClassic), QVariant::fromValue(data));
367 if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
369 qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
371 runDebuggingHelperClassic(data, watchHandler()->isExpandedIName(data.iname));
376 if (data.isHasChildrenNeeded() && data.variable.isEmpty()) {
378 qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
380 createGdbVariableClassic(data);
381 // the WatchVarCreate handler will re-insert a WatchData
382 // item, with childrenNeeded() set.
387 if (data.isHasChildrenNeeded()) {
388 QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
389 QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
390 postCommand(cmd, Discardable,
391 CB(handleVarListChildrenClassic), QVariant::fromValue(data));
395 qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString();
396 QTC_ASSERT(false, return);
399 void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response)
402 WatchData data = response.cookie.value<WatchData>();
403 QTC_ASSERT(data.isValid(), return);
405 // The real dumper might have aborted without giving any answers.
406 // Remove traces of the question, too.
407 if (m_cookieForToken.contains(response.token - 1)) {
408 m_cookieForToken.remove(response.token - 1);
409 showMessage(_("DETECTING LOST COMMAND %1").arg(response.token - 1));
410 --m_pendingWatchRequests;
411 data.setError(WatchData::msgNotInScope());
416 //qDebug() << "CUSTOM VALUE RESULT:" << response.toString();
417 //qDebug() << "FOR DATA:" << data.toString() << response.resultClass;
418 if (response.resultClass != GdbResultDone) {
419 qDebug() << "STRANGE CUSTOM DUMPER RESULT DATA:" << data.toString();
424 if (!parseConsoleStream(response, &contents)) {
425 data.setError(WatchData::msgNotInScope());
430 setWatchDataType(data, response.data.findChild("type"));
431 setWatchDataDisplayedType(data, response.data.findChild("displaytype"));
432 QList<WatchData> list;
433 parseWatchData(watchHandler()->expandedINames(), data, contents, &list);
434 //for (int i = 0; i != list.size(); ++i)
435 // qDebug() << "READ: " << list.at(i).toString();
436 watchHandler()->insertBulkData(list);
439 void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response)
441 if (response.resultClass == GdbResultDone) {
442 WatchData data = response.cookie.value<WatchData>();
443 QByteArray out = response.data.findChild("consolestreamoutput").data();
444 while (out.endsWith(' ') || out.endsWith('\n'))
446 QList<QByteArray> list = out.split(' ');
447 if (list.isEmpty()) {
448 data.setError(WatchData::msgNotInScope());
449 data.setAllUnneeded();
451 } else if (data.type == "QString"
452 || data.type.endsWith("::QString")) {
453 QList<QByteArray> list = out.split(' ');
455 int l = out.isEmpty() ? 0 : list.size();
456 for (int i = 0; i < l; ++i)
457 str.append(list.at(i).toInt());
458 data.setValue(_c('"') + str + _c('"'));
459 data.setHasChildren(false);
460 data.setAllUnneeded();
462 } else if (data.type == "QStringList"
463 || data.type.endsWith("::QStringList")) {
465 data.setValue(tr("<0 items>"));
466 data.setHasChildren(false);
467 data.setAllUnneeded();
472 data.setValue(tr("<%n items>", 0, l));
473 data.setHasChildren(!list.empty());
474 data.setAllUnneeded();
476 for (int i = 0; i < l; ++i) {
478 data1.name = _("[%1]").arg(i);
479 data1.type = data.type.left(data.type.size() - 4);
480 data1.iname = data.iname + '.' + QByteArray::number(i);
481 const QByteArray &addressSpec = list.at(i);
482 if (addressSpec.startsWith("0x")) {
483 data.setHexAddress(addressSpec);
485 data.dumperFlags = addressSpec; // Item model dumpers pull tricks
487 data1.exp = "((" + gdbQuoteTypes(data1.type) + "*)" + addressSpec + ')';
488 data1.setHasChildren(false);
489 data1.setValueNeeded();
490 QByteArray cmd = "qdumpqstring (" + data1.exp + ')';
493 postCommand(cmd, WatchUpdate,
494 CB(handleDebuggingHelperValue3Classic), var);
498 data.setError(WatchData::msgNotInScope());
499 data.setAllUnneeded();
503 WatchData data = response.cookie.value<WatchData>();
504 data.setError(WatchData::msgNotInScope());
505 data.setAllUnneeded();
510 void GdbEngine::tryLoadDebuggingHelpersClassic()
513 if (m_gdbAdapter->dumperHandling() == AbstractGdbAdapter::DumperNotAvailable) {
514 // Load at least gdb macro based dumpers.
515 QFile file(_(":/gdb/gdbmacros.txt"));
516 file.open(QIODevice::ReadOnly);
517 QByteArray contents = file.readAll();
518 m_debuggingHelperState = DebuggingHelperLoadTried;
519 postCommand(contents);
523 PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
524 m_debuggingHelperState = DebuggingHelperUnavailable;
525 if (!checkDebuggingHelpersClassic())
528 m_debuggingHelperState = DebuggingHelperLoadTried;
529 QByteArray dlopenLib;
530 const DebuggerStartMode startMode = startParameters().startMode;
531 if (startMode == AttachToRemote || startMode == StartRemoteGdb)
532 dlopenLib = startParameters().remoteDumperLib;
534 dlopenLib = qtDumperLibraryName().toLocal8Bit();
536 // Do not use STRINGIFY for RTLD_NOW as we really want to expand that to a number.
537 #if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
538 // We are using Python on Windows and Symbian.
539 QTC_ASSERT(false, /**/);
540 #elif defined(Q_OS_MAC)
541 //postCommand("sharedlibrary libc"); // for malloc
542 //postCommand("sharedlibrary libdl"); // for dlopen
543 const QByteArray flag = QByteArray::number(RTLD_NOW);
544 postCommand("call (void)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
545 + "\", " + flag + ")",
546 CB(handleDebuggingHelperSetup));
547 //postCommand("sharedlibrary " + dotEscape(dlopenLib));
549 //postCommand("p dlopen");
550 const QByteArray flag = QByteArray::number(RTLD_NOW);
551 postCommand("sharedlibrary libc"); // for malloc
552 postCommand("sharedlibrary libdl"); // for dlopen
553 postCommand("call (void*)dlopen(\"" + GdbMi::escapeCString(dlopenLib)
554 + "\", " + flag + ")",
555 CB(handleDebuggingHelperSetup));
556 // Some older systems like CentOS 4.6 prefer this:
557 postCommand("call (void*)__dlopen(\"" + GdbMi::escapeCString(dlopenLib)
558 + "\", " + flag + ")",
559 CB(handleDebuggingHelperSetup));
560 postCommand("sharedlibrary " + dotEscape(dlopenLib));
562 tryQueryDebuggingHelpersClassic();
565 void GdbEngine::tryQueryDebuggingHelpersClassic()
568 // Retrieve list of dumpable classes.
569 postCommand("call (void*)qDumpObjectData440(1,0,0,0,0,0,0,0)");
570 postCommand("p (char*)&qDumpOutBuffer",
571 CB(handleQueryDebuggingHelperClassic));
574 // Called from CoreAdapter and AttachAdapter
575 void GdbEngine::updateAllClassic()
578 PENDING_DEBUG("UPDATING ALL\n");
579 QTC_ASSERT(state() == InferiorUnrunnable || state() == InferiorStopOk,
580 qDebug() << state());
581 tryLoadDebuggingHelpersClassic();
582 reloadModulesInternal();
583 postCommand("-stack-list-frames", WatchUpdate,
584 CB(handleStackListFrames),
585 QVariant::fromValue<StackCookie>(StackCookie(false, true)));
586 stackHandler()->setCurrentIndex(0);
587 if (supportsThreads())
588 postCommand("-thread-list-ids", WatchUpdate, CB(handleThreadListIds), 0);
593 void GdbEngine::setDebuggingHelperStateClassic(DebuggingHelperState s)
596 m_debuggingHelperState = s;
599 void GdbEngine::handleStackListArgumentsClassic(const GdbResponse &response)
605 // 12^done,stack-args=
606 // [frame={level="0",args=[
607 // {name="argc",type="int",value="1"},
608 // {name="argv",type="char **",value="(char **) 0x7..."}]}]
610 // 78^done,stack-args=
611 // {frame={level="0",args={
613 // {exp="this",value="0x38a2fab0",name="var21",numchild="3",
614 // type="CurrentDocumentFind * const",typecode="PTR",
615 // dynamic_type="",in_scope="true",block_start_addr="0x3938e946",
616 // block_end_addr="0x3938eb2d"},
618 // {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
619 // name="var22",numchild="1",type="const QString ...} }}}
621 // In both cases, iterating over the children of stack-args/frame/args
623 m_currentFunctionArgs.clear();
624 if (response.resultClass == GdbResultDone) {
625 const GdbMi list = response.data.findChild("stack-args");
626 const GdbMi frame = list.findChild("frame");
627 const GdbMi args = frame.findChild("args");
628 m_currentFunctionArgs = args.children();
630 // Seems to occur on "RedHat 4 based Linux" gdb 7.0.1:
631 // ^error,msg="Cannot access memory at address 0x0"
632 showMessage(_("UNEXPECTED RESPONSE: ") + response.toString());
636 void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response)
641 // There could be shadowed variables
642 QList<GdbMi> locals = response.data.findChild("locals").children();
643 locals += m_currentFunctionArgs;
644 QMap<QByteArray, int> seen;
645 // If desired, retrieve list of uninitialized variables looking at
646 // the current frame. This is invoked first time after a stop from
647 // handleStop1, which passes on the frame as cookie. The whole stack
648 // is not known at this point.
649 QStringList uninitializedVariables;
650 if (debuggerCore()->action(UseCodeModel)->isChecked()) {
651 const StackFrame frame =
652 qVariantCanConvert<Debugger::Internal::StackFrame>(response.cookie)
653 ? qVariantValue<Debugger::Internal::StackFrame>(response.cookie)
654 : stackHandler()->currentFrame();
655 if (frame.isUsable())
656 getUninitializedVariables(debuggerCore()->cppCodeModelSnapshot(),
657 frame.function, frame.file, frame.line,
658 &uninitializedVariables);
660 QList<WatchData> list;
661 foreach (const GdbMi &item, locals) {
662 const WatchData data = localVariable(item, uninitializedVariables, &seen);
664 list.push_back(data);
667 if (!m_resultVarName.isEmpty()) {
669 rd.iname = "return.0";
671 rd.exp = m_resultVarName;
675 watchHandler()->insertBulkData(list);
676 watchHandler()->updateWatchers();
679 bool GdbEngine::checkDebuggingHelpersClassic()
682 if (!qtDumperLibraryEnabled())
684 const QString lib = qtDumperLibraryName();
685 if (QFileInfo(lib).exists())
687 const QStringList &locations = qtDumperLibraryLocations();
688 const QString loc = locations.join(QLatin1String(", "));
689 const QString msg = tr("The debugging helper library was not found at %1.")
692 // This can happen for remote debugging.
693 if (!locations.isEmpty())
694 showQtDumperLibraryWarning(msg); // This might build the library.
695 return QFileInfo(lib).exists();
698 void GdbEngine::handleQueryDebuggingHelperClassic(const GdbResponse &response)
700 const double dumperVersionRequired = 1.0;
701 //qDebug() << "DATA DUMPER TRIAL:" << response.toString();
704 QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString());
705 const bool ok = m_dumperHelper.parseQuery(contents)
706 && m_dumperHelper.typeCount();
708 // Get version and sizes from dumpers. Expression cache
709 // currently causes errors.
710 const double dumperVersion = getDumperVersion(contents);
711 if (dumperVersion < dumperVersionRequired) {
712 showQtDumperLibraryWarning(
713 QtDumperHelper::msgDumperOutdated(dumperVersionRequired, dumperVersion));
714 m_debuggingHelperState = DebuggingHelperUnavailable;
717 m_debuggingHelperState = DebuggingHelperAvailable;
718 const QString successMsg = tr("Dumper version %1, %n custom dumpers found.",
719 0, m_dumperHelper.typeCount()).arg(dumperVersion);
720 showStatusMessage(successMsg);
722 // Sanity check for Qt version of dumpers and debuggee.
723 QByteArray ns = m_dumperHelper.qtNamespace();
724 postCommand("-var-create A@ * '" + ns + "qVersion'()",
725 CB(handleDebuggingHelperVersionCheckClassic));
726 postCommand("-var-delete A@");
728 // Retry if thread has not terminated yet.
729 m_debuggingHelperState = DebuggingHelperUnavailable;
730 showStatusMessage(tr("Debugging helpers not found."));
732 //qDebug() << m_dumperHelper.toString(true);
733 //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
736 void GdbEngine::handleDebuggingHelperVersionCheckClassic(const GdbResponse &response)
738 if (response.resultClass == GdbResultDone) {
739 QString value = _(response.data.findChild("value").data());
740 QString debuggeeQtVersion = value.section(QLatin1Char('"'), 1, 1);
741 QString dumperQtVersion = m_dumperHelper.qtVersionString();
742 if (debuggeeQtVersion.isEmpty()) {
743 showMessage(_("DUMPER VERSION CHECK SKIPPED, NO qVersion() OUTPUT IN")
744 + response.toString());
745 } else if (dumperQtVersion.isEmpty()) {
746 showMessage(_("DUMPER VERSION CHECK SKIPPED, NO VERSION STRING"));
747 } else if (dumperQtVersion != debuggeeQtVersion) {
748 showMessageBox(QMessageBox::Warning,
749 tr("Debugging helpers: Qt version mismatch"),
750 tr("The Qt version used to build the debugging helpers (%1) "
751 "does not match the Qt version used to build the debugged "
752 "application (%2).\nThis might yield incorrect results.")
753 .arg(dumperQtVersion).arg(debuggeeQtVersion));
755 showMessage(_("DUMPER VERSION CHECK SUCCESSFUL: ")
759 showMessage("DUMPER VERSION CHECK NOT COMPLETED");
763 void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item,
764 const WatchData &parent, int sortId)
766 //qDebug() << "VAR_LIST_CHILDREN: PARENT" << parent.toString();
767 //qDebug() << "VAR_LIST_CHILDREN: ITEM" << item.toString();
768 QByteArray exp = item.findChild("exp").data();
769 QByteArray name = item.findChild("name").data();
770 if (isAccessSpecifier(exp)) {
771 // Suppress 'private'/'protected'/'public' level.
773 data.variable = name;
774 data.iname = parent.iname;
775 //data.iname = data.variable;
776 data.exp = parent.exp;
777 data.setTypeUnneeded();
778 data.setValueUnneeded();
779 data.setHasChildrenUnneeded();
780 data.setChildrenUnneeded();
781 QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
782 //iname += '.' + exp;
783 postCommand(cmd, WatchUpdate,
784 CB(handleVarListChildrenClassic), QVariant::fromValue(data));
785 } else if (!startsWithDigit(exp)
786 && item.findChild("numchild").data() == "0") {
787 // Happens for structs without data, e.g. interfaces.
790 data.iname = parent.iname + '.' + data.name.toLatin1();
791 data.variable = name;
792 setWatchDataType(data, item.findChild("type"));
793 setWatchDataValue(data, item);
794 setWatchDataAddress(data, item.findChild("addr"));
795 data.setHasChildren(false);
797 } else if (parent.iname.endsWith('.')) {
798 // Happens with anonymous unions.
801 QByteArray cmd = "-var-list-children --all-values \"" + data.variable + '"';
802 postCommand(cmd, WatchUpdate,
803 CB(handleVarListChildrenClassic), QVariant::fromValue(data));
804 } else if (exp == "staticMetaObject") {
805 // && item.findChild("type").data() == "const QMetaObject")
806 // FIXME: Namespaces?
807 // { do nothing } FIXME: make configurable?
808 // special "clever" hack to avoid clutter in the GUI.
809 // I am not sure this is a good idea...
811 // Suppress 'private'/'protected'/'public' level.
813 data.iname = parent.iname + '.' + exp;
814 data.variable = name;
815 data.sortId = sortId;
816 setWatchDataType(data, item.findChild("type"));
817 setWatchDataValue(data, item);
818 setWatchDataAddress(data, item.findChild("addr"));
819 setWatchDataChildCount(data, item.findChild("numchild"));
820 if (!watchHandler()->isExpandedIName(data.iname))
821 data.setChildrenUnneeded();
824 if (data.type == data.name) {
825 if (isPointerType(parent.type)) {
826 data.exp = "*(" + parent.exp + ')';
827 data.name = _("*") + parent.name;
829 // A type we derive from? gdb crashes when creating variables here
830 data.exp = parent.exp;
832 } else if (exp.startsWith('*')) {
834 data.exp = "*(" + parent.exp + ')';
835 } else if (startsWithDigit(data.name)) {
836 // An array. No variables needed?
837 data.name = _c('[') + data.name + _c(']');
838 data.exp = parent.exp + '[' + exp + ']';
839 } else if (0 && parent.name.endsWith(_c('.'))) {
840 // Happens with anonymous unions
841 data.exp = parent.exp + data.name.toLatin1();
842 //data.name = "<anonymous union>";
843 } else if (exp.isEmpty()) {
844 // Happens with anonymous unions
845 data.exp = parent.exp;
846 data.name = tr("<n/a>");
847 data.iname = parent.iname + ".@";
848 data.type = tr("<anonymous union>").toUtf8();
850 // A structure. Hope there's nothing else...
851 data.exp = '(' + parent.exp + ")." + data.name.toLatin1();
854 if (hasDebuggingHelperForType(data.type)) {
855 // we do not trust gdb if we have a custom dumper
856 data.setValueNeeded();
857 data.setHasChildrenNeeded();
860 //qDebug() << "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
861 //qDebug() << "VAR_LIST_CHILDREN: APPENDEE" << data.toString();
866 void GdbEngine::handleVarListChildrenClassic(const GdbResponse &response)
868 //WatchResultCounter dummy(this, WatchVarListChildren);
869 WatchData data = response.cookie.value<WatchData>();
872 if (response.resultClass == GdbResultDone) {
873 //qDebug() << "VAR_LIST_CHILDREN: PARENT" << data.toString();
874 QList<GdbMi> children = response.data.findChild("children").children();
876 for (int i = 0; i != children.size(); ++i)
877 handleVarListChildrenHelperClassic(children.at(i), data, i);
879 if (children.isEmpty()) {
880 // happens e.g. if no debug information is present or
881 // if the class really has no children
883 data1.iname = data.iname + ".child";
884 //: About variable's value
885 data1.value = tr("<no information>");
886 data1.hasChildren = false;
887 data1.setAllUnneeded();
889 data.setAllUnneeded();
891 } else if (data.variable.endsWith("private")
892 || data.variable.endsWith("protected")
893 || data.variable.endsWith("public")) {
894 // this skips the spurious "public", "private" etc levels
897 data.setChildrenUnneeded();
901 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
905 void GdbEngine::handleEvaluateExpressionClassic(const GdbResponse &response)
907 WatchData data = response.cookie.value<WatchData>();
908 QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
909 if (response.resultClass == GdbResultDone) {
911 // data.name = response.data.findChild("value").data();
913 setWatchDataValue(data, response.data);
915 data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
917 //qDebug() << "HANDLE EVALUATE EXPRESSION:" << data.toString();
919 //updateWatchModel2();
922 } // namespace Internal
923 } // namespace Debugger