OSDN Git Service

1b0c2e61b1f216fbd84f824d75611ab258e7262f
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / gdb / classicgdbengine.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "gdbengine.h"
35 #include "gdbmi.h"
36
37 #include "debuggerstartparameters.h"
38 #include "abstractgdbadapter.h"
39 #include "debuggeractions.h"
40 #include "debuggercore.h"
41 #include "debuggerstringutils.h"
42
43 #include "stackhandler.h"
44 #include "watchhandler.h"
45
46 #include <utils/qtcassert.h>
47 #include <utils/savedaction.h>
48
49 #include <QtCore/QFile>
50 #include <QtCore/QFileInfo>
51 #include <QtGui/QMessageBox>
52
53 #if !defined(Q_OS_WIN)
54 #include <dlfcn.h>
55 #endif
56
57
58 #define PRECONDITION QTC_ASSERT(!hasPython(), /**/)
59 #define CB(callback) &GdbEngine::callback, STRINGIFY(callback)
60
61
62 //#define DEBUG_PENDING  1
63 //#define DEBUG_SUBITEM  1
64
65 #if DEBUG_PENDING
66 #   define PENDING_DEBUG(s) qDebug() << s
67 #else
68 #   define PENDING_DEBUG(s)
69 #endif
70 #define PENDING_DEBUGX(s) qDebug() << s
71
72 namespace Debugger {
73 namespace Internal {
74
75 static bool isAccessSpecifier(const QByteArray &ba)
76 {
77     return ba == "private" || ba == "protected" || ba == "public";
78 }
79
80 // reads a MI-encoded item frome the consolestream
81 static bool parseConsoleStream(const GdbResponse &response, GdbMi *contents)
82 {
83     GdbMi output = response.data.findChild("consolestreamoutput");
84     QByteArray out = output.data();
85
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
89         return false;
90     }
91
92     out = out.mid(markerPos +  1);
93     out = out.left(out.lastIndexOf('"'));
94     // optimization: dumper output never needs real C unquoting
95     out.replace('\\', "");
96
97     contents->fromStringMultiple(out);
98     //qDebug() << "CONTENTS" << contents->toString(true);
99     return contents->isValid();
100 }
101
102 static double getDumperVersion(const GdbMi &contents)
103 {
104     const GdbMi dumperVersionG = contents.findChild("dumperversion");
105     if (dumperVersionG.type() != GdbMi::Invalid) {
106         bool ok;
107         const double v = QString::fromAscii(dumperVersionG.data()).toDouble(&ok);
108         if (ok)
109             return v;
110     }
111     return 1.0;
112 }
113
114
115 void GdbEngine::updateLocalsClassic(const QVariant &cookie)
116 {
117     PRECONDITION;
118     m_processedNames.clear();
119
120     //PENDING_DEBUG("\nRESET PENDING");
121     //m_toolTipCache.clear();
122     clearToolTip();
123     watchHandler()->beginCycle();
124
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
133 }
134
135 static inline QString msgRetrievingWatchData(int pending)
136 {
137     return GdbEngine::tr("Retrieving data for watch view (%n requests pending)...", 0, pending);
138 }
139
140 void GdbEngine::runDirectDebuggingHelperClassic(const WatchData &data, bool dumpChildren)
141 {
142     Q_UNUSED(dumpChildren)
143     QByteArray type = data.type;
144     QByteArray cmd;
145
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 + "))";
150
151     QVariant var;
152     var.setValue(data);
153     postCommand(cmd, WatchUpdate, CB(handleDebuggingHelperValue3Classic), var);
154
155     showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000);
156 }
157
158 void GdbEngine::runDebuggingHelperClassic(const WatchData &data0, bool dumpChildren)
159 {
160     PRECONDITION;
161     if (m_debuggingHelperState != DebuggingHelperAvailable) {
162         runDirectDebuggingHelperClassic(data0, dumpChildren);
163         return;
164     }
165     WatchData data = data0;
166
167     // Avoid endless loops created by faulty dumpers.
168     QByteArray processedName = QByteArray::number(dumpChildren) + '-' + data.iname;
169     if (m_processedNames.contains(processedName)) {
170         showMessage(
171             _("<Breaking endless loop for " + data.iname + '>'), LogMiscInput);
172         data.setAllUnneeded();
173         data.setValue(_("<unavailable>"));
174         data.setHasChildren(false);
175         insertData(data);
176         return;
177     }
178     m_processedNames.insert(processedName);
179
180     QByteArray params;
181     QList<QByteArray> extraArgs;
182     const QtDumperHelper::TypeData td = m_dumperHelper.typeData(data0.type);
183     m_dumperHelper.evaluationParameters(data, td, &params, &extraArgs);
184
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;
189
190     QByteArray addr;
191     if (data.address)
192         addr = "(void*)" + data.hexAddress();
193     else if (data.exp.isEmpty()) // happens e.g. for QAbstractItem
194         addr = QByteArray(1, '0');
195     else
196         addr = "&(" + data.exp + ')';
197
198     sendWatchParameters(params);
199
200     QByteArray cmd = "call (void*)qDumpObjectData440("
201         + QByteArray::number(protocol)
202         + ",0,"
203         + addr
204         + ','
205         + (dumpChildren ? '1' : '0');
206     foreach (const QByteArray &ex, extraArgs)
207         cmd += ',' + ex;
208     cmd += ')';
209
210     postCommand(cmd, WatchUpdate | NonCriticalResponse);
211
212     showStatusMessage(msgRetrievingWatchData(m_pendingWatchRequests + 1), 10000);
213
214     // retrieve response
215     postCommand("p (char*)&qDumpOutBuffer", WatchUpdate,
216         CB(handleDebuggingHelperValue2Classic), qVariantFromValue(data));
217 }
218
219 void GdbEngine::createGdbVariableClassic(const WatchData &data)
220 {
221     PRECONDITION;
222     if (data.iname == "local.flist.0") {
223         int i = 1;
224         Q_UNUSED(i);
225     }
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);
233 }
234
235 void GdbEngine::updateSubItemClassic(const WatchData &data0)
236 {
237     PRECONDITION;
238     WatchData data = data0;
239 #    if DEBUG_SUBITEM
240     qDebug() << "UPDATE SUBITEM:" << data.toString();
241 #    endif
242     QTC_ASSERT(data.isValid(), return);
243
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.
250 #            if 1
251             qDebug() << "FIXME: GdbEngine::updateSubItem:"
252                  << data.toString() << "should not happen";
253 #            else
254             data.setType(WatchData::msgNotInScope());
255             data.setValue(WatchData::msgNotInScope());
256             data.setHasChildren(false);
257             insertData(data);
258             return;
259 #            endif
260         }
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);
266         return;
267     }
268
269     // We should have a type now. This is relied upon further below.
270     QTC_ASSERT(!data.type.isEmpty(), return);
271
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
276 #        if DEBUG_SUBITEM
277         qDebug() << "IT'S A POINTER";
278 #        endif
279
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
284             insertData(data);
285         } else {
286             data.setChildrenUnneeded();
287             insertData(data);
288             WatchData data1;
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();
295             insertData(data1);
296         }
297         return;
298     }
299
300     if (data.isValueNeeded() && hasDebuggingHelperForType(data.type)) {
301 #        if DEBUG_SUBITEM
302         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE";
303 #        endif
304         runDebuggingHelperClassic(data,
305             watchHandler()->isExpandedIName(data.iname));
306         return;
307     }
308
309 /*
310     if (data.isValueNeeded() && data.exp.isEmpty()) {
311 #        if DEBUG_SUBITEM
312         qDebug() << "UPDATE SUBITEM: NO EXPRESSION?";
313 #        endif
314         data.setError("<no expression given>");
315         insertData(data);
316         return;
317     }
318 */
319
320     if (data.isValueNeeded() && data.variable.isEmpty()) {
321 #        if DEBUG_SUBITEM
322         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR VALUE";
323 #        endif
324         createGdbVariableClassic(data);
325         // the WatchVarCreate handler will re-insert a WatchData
326         // item, with valueNeeded() set.
327         return;
328     }
329
330     if (data.isValueNeeded()) {
331         QTC_ASSERT(!data.variable.isEmpty(), return); // tested above
332 #        if DEBUG_SUBITEM
333         qDebug() << "UPDATE SUBITEM: VALUE";
334 #        endif
335         QByteArray cmd = "-var-evaluate-expression \"" + data.iname + '"';
336         postCommand(cmd, WatchUpdate,
337             CB(handleEvaluateExpressionClassic), QVariant::fromValue(data));
338         return;
339     }
340
341     if (data.isChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
342 #        if DEBUG_SUBITEM
343         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
344 #        endif
345         runDebuggingHelperClassic(data, true);
346         return;
347     }
348
349     if (data.isChildrenNeeded() && data.variable.isEmpty()) {
350 #        if DEBUG_SUBITEM
351         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDREN";
352 #        endif
353         createGdbVariableClassic(data);
354         // the WatchVarCreate handler will re-insert a WatchData
355         // item, with childrenNeeded() set.
356         return;
357     }
358
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));
364         return;
365     }
366
367     if (data.isHasChildrenNeeded() && hasDebuggingHelperForType(data.type)) {
368 #        if DEBUG_SUBITEM
369         qDebug() << "UPDATE SUBITEM: CUSTOMVALUE WITH CHILDREN";
370 #        endif
371         runDebuggingHelperClassic(data, watchHandler()->isExpandedIName(data.iname));
372         return;
373     }
374
375 //#if !X
376     if (data.isHasChildrenNeeded() && data.variable.isEmpty()) {
377 #        if DEBUG_SUBITEM
378         qDebug() << "UPDATE SUBITEM: VARIABLE NEEDED FOR CHILDCOUNT";
379 #        endif
380         createGdbVariableClassic(data);
381         // the WatchVarCreate handler will re-insert a WatchData
382         // item, with childrenNeeded() set.
383         return;
384     }
385 //#endif
386
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));
392         return;
393     }
394
395     qDebug() << "FIXME: UPDATE SUBITEM:" << data.toString();
396     QTC_ASSERT(false, return);
397 }
398
399 void GdbEngine::handleDebuggingHelperValue2Classic(const GdbResponse &response)
400 {
401     PRECONDITION;
402     WatchData data = response.cookie.value<WatchData>();
403     QTC_ASSERT(data.isValid(), return);
404
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());
412         insertData(data);
413         return;
414     }
415
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();
420         return;
421     }
422
423     GdbMi contents;
424     if (!parseConsoleStream(response, &contents)) {
425         data.setError(WatchData::msgNotInScope());
426         insertData(data);
427         return;
428     }
429
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);
437 }
438
439 void GdbEngine::handleDebuggingHelperValue3Classic(const GdbResponse &response)
440 {
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'))
445             out.chop(1);
446         QList<QByteArray> list = out.split(' ');
447         if (list.isEmpty()) {
448             data.setError(WatchData::msgNotInScope());
449             data.setAllUnneeded();
450             insertData(data);
451         } else if (data.type == "QString"
452                 || data.type.endsWith("::QString")) {
453             QList<QByteArray> list = out.split(' ');
454             QString str;
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();
461             insertData(data);
462         } else if (data.type == "QStringList"
463                 || data.type.endsWith("::QStringList")) {
464             if (out.isEmpty()) {
465                 data.setValue(tr("<0 items>"));
466                 data.setHasChildren(false);
467                 data.setAllUnneeded();
468                 insertData(data);
469             } else {
470                 int l = list.size();
471                 //: In string list
472                 data.setValue(tr("<%n items>", 0, l));
473                 data.setHasChildren(!list.empty());
474                 data.setAllUnneeded();
475                 insertData(data);
476                 for (int i = 0; i < l; ++i) {
477                     WatchData data1;
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);
484                     } else {
485                         data.dumperFlags = addressSpec; // Item model dumpers pull tricks
486                     }
487                     data1.exp = "((" + gdbQuoteTypes(data1.type) + "*)" + addressSpec + ')';
488                     data1.setHasChildren(false);
489                     data1.setValueNeeded();
490                     QByteArray cmd = "qdumpqstring (" + data1.exp + ')';
491                     QVariant var;
492                     var.setValue(data1);
493                     postCommand(cmd, WatchUpdate,
494                         CB(handleDebuggingHelperValue3Classic), var);
495                 }
496             }
497         } else {
498             data.setError(WatchData::msgNotInScope());
499             data.setAllUnneeded();
500             insertData(data);
501         }
502     } else {
503         WatchData data = response.cookie.value<WatchData>();
504         data.setError(WatchData::msgNotInScope());
505         data.setAllUnneeded();
506         insertData(data);
507     }
508 }
509
510 void GdbEngine::tryLoadDebuggingHelpersClassic()
511 {
512     PRECONDITION;
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);
520         return;
521     }
522
523     PENDING_DEBUG("TRY LOAD CUSTOM DUMPERS");
524     m_debuggingHelperState = DebuggingHelperUnavailable;
525     if (!checkDebuggingHelpersClassic())
526         return;
527
528     m_debuggingHelperState = DebuggingHelperLoadTried;
529     QByteArray dlopenLib;
530     const DebuggerStartMode startMode = startParameters().startMode;
531     if (startMode == AttachToRemote || startMode == StartRemoteGdb)
532         dlopenLib = startParameters().remoteDumperLib;
533     else
534         dlopenLib = qtDumperLibraryName().toLocal8Bit();
535
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));
548 #else
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));
561 #endif
562     tryQueryDebuggingHelpersClassic();
563 }
564
565 void GdbEngine::tryQueryDebuggingHelpersClassic()
566 {
567     PRECONDITION;
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));
572 }
573
574 // Called from CoreAdapter and AttachAdapter
575 void GdbEngine::updateAllClassic()
576 {
577     PRECONDITION;
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);
589     reloadRegisters();
590     updateLocals();
591 }
592
593 void GdbEngine::setDebuggingHelperStateClassic(DebuggingHelperState s)
594 {
595     PRECONDITION;
596     m_debuggingHelperState = s;
597 }
598
599 void GdbEngine::handleStackListArgumentsClassic(const GdbResponse &response)
600 {
601     PRECONDITION;
602     // stage 1/2
603
604     // Linux:
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..."}]}]
609     // Mac:
610     // 78^done,stack-args=
611     //    {frame={level="0",args={
612     //      varobj=
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"},
617     //      varobj=
618     //         {exp="before",value="@0xbfffb9f8: {d = 0x3a7f2a70}",
619     //              name="var22",numchild="1",type="const QString  ...} }}}
620     //
621     // In both cases, iterating over the children of stack-args/frame/args
622     // is ok.
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();
629     } else {
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());
633     }
634 }
635
636 void GdbEngine::handleStackListLocalsClassic(const GdbResponse &response)
637 {
638     PRECONDITION;
639     // stage 2/2
640
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);
659     }
660     QList<WatchData> list;
661     foreach (const GdbMi &item, locals) {
662         const WatchData data = localVariable(item, uninitializedVariables, &seen);
663         if (data.isValid())
664             list.push_back(data);
665     }
666
667     if (!m_resultVarName.isEmpty()) {
668         WatchData rd;
669         rd.iname = "return.0";
670         rd.name = "return";
671         rd.exp = m_resultVarName;
672         list.append(rd);
673     }
674
675     watchHandler()->insertBulkData(list);
676     watchHandler()->updateWatchers();
677 }
678
679 bool GdbEngine::checkDebuggingHelpersClassic()
680 {
681     PRECONDITION;
682     if (!qtDumperLibraryEnabled())
683         return false;
684     const QString lib = qtDumperLibraryName();
685     if (QFileInfo(lib).exists())
686         return true;
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.")
690             .arg(loc);
691     showMessage(msg);
692     // This can happen for remote debugging.
693     if (!locations.isEmpty())
694         showQtDumperLibraryWarning(msg); // This might build the library.
695     return QFileInfo(lib).exists();
696 }
697
698 void GdbEngine::handleQueryDebuggingHelperClassic(const GdbResponse &response)
699 {
700     const double dumperVersionRequired = 1.0;
701     //qDebug() << "DATA DUMPER TRIAL:" << response.toString();
702
703     GdbMi contents;
704     QTC_ASSERT(parseConsoleStream(response, &contents), qDebug() << response.toString());
705     const bool ok = m_dumperHelper.parseQuery(contents)
706         && m_dumperHelper.typeCount();
707     if (ok) {
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;
715             return;
716         }
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);
721
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@");
727     } else {
728         // Retry if thread has not terminated yet.
729         m_debuggingHelperState = DebuggingHelperUnavailable;
730         showStatusMessage(tr("Debugging helpers not found."));
731     }
732     //qDebug() << m_dumperHelper.toString(true);
733     //qDebug() << m_availableSimpleDebuggingHelpers << "DATA DUMPERS AVAILABLE";
734 }
735
736 void GdbEngine::handleDebuggingHelperVersionCheckClassic(const GdbResponse &response)
737 {
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));
754         } else {
755             showMessage(_("DUMPER VERSION CHECK SUCCESSFUL: ")
756                          + dumperQtVersion);
757         }
758     } else {
759         showMessage("DUMPER VERSION CHECK NOT COMPLETED");
760     }
761 }
762
763 void GdbEngine::handleVarListChildrenHelperClassic(const GdbMi &item,
764     const WatchData &parent, int sortId)
765 {
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.
772         WatchData data;
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.
788         WatchData data;
789         data.name = _(exp);
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);
796         insertData(data);
797     } else if (parent.iname.endsWith('.')) {
798         // Happens with anonymous unions.
799         WatchData data;
800         data.iname = name;
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...
810     } else {
811         // Suppress 'private'/'protected'/'public' level.
812         WatchData data;
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();
822
823         data.name = _(exp);
824         if (data.type == data.name) {
825             if (isPointerType(parent.type)) {
826                 data.exp = "*(" + parent.exp + ')';
827                 data.name = _("*") + parent.name;
828             } else {
829                 // A type we derive from? gdb crashes when creating variables here
830                 data.exp = parent.exp;
831             }
832         } else if (exp.startsWith('*')) {
833             // A pointer
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();
849         } else {
850             // A structure. Hope there's nothing else...
851             data.exp = '(' + parent.exp + ")." + data.name.toLatin1();
852         }
853
854         if (hasDebuggingHelperForType(data.type)) {
855             // we do not trust gdb if we have a custom dumper
856             data.setValueNeeded();
857             data.setHasChildrenNeeded();
858         }
859
860         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT 3" << parent.toString();
861         //qDebug() <<  "VAR_LIST_CHILDREN: APPENDEE" << data.toString();
862         insertData(data);
863     }
864 }
865
866 void GdbEngine::handleVarListChildrenClassic(const GdbResponse &response)
867 {
868     //WatchResultCounter dummy(this, WatchVarListChildren);
869     WatchData data = response.cookie.value<WatchData>();
870     if (!data.isValid())
871         return;
872     if (response.resultClass == GdbResultDone) {
873         //qDebug() <<  "VAR_LIST_CHILDREN: PARENT" << data.toString();
874         QList<GdbMi> children = response.data.findChild("children").children();
875
876         for (int i = 0; i != children.size(); ++i)
877             handleVarListChildrenHelperClassic(children.at(i), data, i);
878
879         if (children.isEmpty()) {
880             // happens e.g. if no debug information is present or
881             // if the class really has no children
882             WatchData data1;
883             data1.iname = data.iname + ".child";
884             //: About variable's value
885             data1.value = tr("<no information>");
886             data1.hasChildren = false;
887             data1.setAllUnneeded();
888             insertData(data1);
889             data.setAllUnneeded();
890             insertData(data);
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
895             // gdb produces
896         } else {
897             data.setChildrenUnneeded();
898             insertData(data);
899         }
900     } else {
901         data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
902     }
903 }
904
905 void GdbEngine::handleEvaluateExpressionClassic(const GdbResponse &response)
906 {
907     WatchData data = response.cookie.value<WatchData>();
908     QTC_ASSERT(data.isValid(), qDebug() << "HUH?");
909     if (response.resultClass == GdbResultDone) {
910         //if (col == 0)
911         //    data.name = response.data.findChild("value").data();
912         //else
913             setWatchDataValue(data, response.data);
914     } else {
915         data.setError(QString::fromLocal8Bit(response.data.findChild("msg").data()));
916     }
917     //qDebug() << "HANDLE EVALUATE EXPRESSION:" << data.toString();
918     insertData(data);
919     //updateWatchModel2();
920 }
921
922 } // namespace Internal
923 } // namespace Debugger