OSDN Git Service

debugger: compile fix msvc. done by hjk.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / debugger / cdb / cdbsymbolgroupcontext.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** Commercial Usage
10 **
11 ** Licensees holding valid Qt Commercial licenses may use this file in
12 ** accordance with the Qt Commercial License Agreement provided with the
13 ** Software or, alternatively, in accordance with the terms contained in
14 ** a written agreement between you and Nokia.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
27 **
28 **************************************************************************/
29
30 #include "cdbsymbolgroupcontext.h"
31 #include "cdbengine_p.h"
32 #include "cdbdumperhelper.h"
33 #include "watchhandler.h"
34 #include "watchutils.h"
35 #include "debuggeractions.h"
36 #include "coreengine.h"
37
38 #include <QtCore/QDebug>
39 #include <QtCore/QTextStream>
40 #include <QtCore/QCoreApplication>
41 #include <QtCore/QRegExp>
42
43 enum { debug = 0 };
44 enum { debugInternalDumpers = 0 };
45
46 namespace Debugger {
47 namespace Internal {
48
49 enum { OwnerNewItem, OwnerSymbolGroup, OwnerSymbolGroupDumper , OwnerDumper };
50
51 typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
52 typedef QList<WatchData> WatchDataList;
53
54 // Predicates for parametrizing the symbol group
55 inline bool truePredicate(const WatchData & /* whatever */) { return true; }
56 inline bool falsePredicate(const WatchData & /* whatever */) { return false; }
57 inline bool isDumperPredicate(const WatchData &wd)
58 { return (wd.source & CdbSymbolGroupContext::SourceMask) == OwnerDumper; }
59
60 static inline void debugWatchDataList(const QList<WatchData> &l, const char *why = 0)
61 {
62     QDebug nospace = qDebug().nospace();
63     if (why)
64         nospace << why << '\n';
65     foreach(const WatchData &wd, l)
66         nospace << wd.toString() << '\n';
67     nospace << '\n';
68 }
69
70 // Match an item that is expanded in the watchhandler.
71 class WatchHandlerExpandedPredicate {
72 public:
73     explicit inline WatchHandlerExpandedPredicate(const WatchHandler *wh) : m_wh(wh) {}
74     inline bool operator()(const WatchData &wd) { return m_wh->isExpandedIName(wd.iname); }
75 private:
76     const WatchHandler *m_wh;
77 };
78
79 // Match an item by iname
80 class MatchINamePredicate {
81 public:
82     explicit inline MatchINamePredicate(const QString &iname) : m_iname(iname) {}
83     inline bool operator()(const WatchData &wd) { return wd.iname == m_iname; }
84 private:
85     const QString &m_iname;
86 };
87
88 // Put a sequence of  WatchData into the model for the non-dumper case
89 class WatchHandlerModelInserter {
90 public:
91     explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
92
93     inline WatchHandlerModelInserter & operator*() { return *this; }
94     inline WatchHandlerModelInserter &operator=(const WatchData &wd)  {
95         m_wh->insertData(wd);
96         return *this;
97     }
98     inline WatchHandlerModelInserter &operator++() { return *this; }
99
100 private:
101     WatchHandler *m_wh;
102 };
103
104 // Put a sequence of  WatchData into the model for the dumper case.
105 // Sorts apart a sequence of  WatchData using the Dumpers.
106 // Puts the stuff for which there is no dumper in the model
107 // as is and sets ownership to symbol group. The rest goes
108 // to the dumpers. Additionally, checks for items pointing to a
109 // dumpeable type and inserts a fake dereferenced item and value.
110 class WatchHandleDumperInserter {
111 public:
112     explicit WatchHandleDumperInserter(WatchHandler *wh,
113                                         const SharedPointerCdbDumperHelper &dumper);
114
115     inline WatchHandleDumperInserter & operator*() { return *this; }
116     inline WatchHandleDumperInserter &operator=(WatchData &wd);
117     inline WatchHandleDumperInserter &operator++() { return *this; }
118
119 private:
120     bool expandPointerToDumpable(const WatchData &wd, QString *errorMessage);
121
122     const QRegExp m_hexNullPattern;
123     WatchHandler *m_wh;
124     const SharedPointerCdbDumperHelper m_dumper;
125     QList<WatchData> m_dumperResult;
126 };
127
128 WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
129                                                        const SharedPointerCdbDumperHelper &dumper) :
130     m_hexNullPattern(QLatin1String("0x0+")),
131     m_wh(wh),
132     m_dumper(dumper)
133 {
134     Q_ASSERT(m_hexNullPattern.isValid());
135 }
136
137 // Prevent recursion of the model by setting value and type
138 static inline bool fixDumperType(WatchData *wd, const WatchData *source = 0)
139 {
140     const bool missing = wd->isTypeNeeded() || wd->type.isEmpty();
141     if (missing) {
142         static const QByteArray unknownType =
143                 QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Type>")
144                         .toUtf8();
145         wd->setType(source ? source->type : unknownType, false);
146     }
147     return missing;
148 }
149
150 static inline bool fixDumperValue(WatchData *wd, const WatchData *source = 0)
151 {
152     const bool missing = wd->isValueNeeded();
153     if (missing) {
154         if (source && source->isValueKnown()) {
155             wd->setValue(source->value);
156         } else {
157             static const QString unknownValue = QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Value>");
158             wd->setValue(unknownValue);
159         }
160     }
161     return missing;
162 }
163
164 // When querying an item, the queried item is sometimes returned in incomplete form.
165 // Take over values from source.
166 static inline void fixDumperResult(const WatchData &source,
167                                    QList<WatchData> *result,
168                                    bool suppressGrandChildren)
169 {
170
171     const int size = result->size();
172     if (!size)
173         return;
174     if (debugCDBWatchHandling)
175         debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult");
176     WatchData &returned = result->front();
177     if (returned.iname != source.iname)
178         return;
179     fixDumperType(&returned, &source);
180     fixDumperValue(&returned, &source);
181     // Indicate owner and known children
182     returned.source = OwnerDumper;
183     if (returned.isChildrenKnown() && returned.isHasChildrenKnown() && returned.hasChildren)
184         returned.source |= CdbSymbolGroupContext::ChildrenKnownBit;
185     if (size == 1)
186         return;
187     // If the model queries the expanding item by pretending childrenNeeded=1,
188     // refuse the request as the children are already known
189     returned.source |= CdbSymbolGroupContext::ChildrenKnownBit;
190     // Fix the children: If the address is missing, we cannot query any further.
191     const QList<WatchData>::iterator wend = result->end();
192     QList<WatchData>::iterator it = result->begin();
193     for (++it; it != wend; ++it) {
194         WatchData &wd = *it;
195         // Indicate owner and known children
196         it->source = OwnerDumper;
197         if (it->isChildrenKnown() && it->isHasChildrenKnown() && it->hasChildren)
198             it->source |= CdbSymbolGroupContext::ChildrenKnownBit;
199         // Cannot dump items with missing addresses or missing types
200         const bool typeFixed = fixDumperType(&wd); // Order of evaluation!
201         if ((wd.addr.isEmpty() && wd.isSomethingNeeded()) || typeFixed) {
202             wd.setHasChildren(false);
203             wd.setAllUnneeded();
204         } else {
205             // Hack: Suppress endless recursion of the model. To be fixed,
206             // the model should not query non-visible items.
207             if (suppressGrandChildren && (wd.isChildrenNeeded() || wd.isHasChildrenNeeded()))
208                 wd.setHasChildren(false);
209         }
210     }
211     if (debugCDBWatchHandling)
212         debugWatchDataList(*result, "<fixDumperResult");
213 }
214
215 // Is this a non-null pointer to a dumpeable item with a value
216 // "0x4343 class QString *" ? - Insert a fake '*' dereferenced item
217 // and run dumpers on it. If that succeeds, insert the fake items owned by dumpers,
218 // which will trigger the ignore predicate.
219 // Note that the symbol context does not create '*' dereferenced items for
220 // classes (see note in its header documentation).
221 bool WatchHandleDumperInserter::expandPointerToDumpable(const WatchData &wd, QString *errorMessage)
222 {
223     if (debugCDBWatchHandling > 1)
224         qDebug() << ">expandPointerToDumpable" << wd.toString();
225
226     bool handled = false;
227     do {
228         if (wd.error || !isPointerType(wd.type))
229             break;
230         const int classPos = wd.value.indexOf(" class ");
231         if (classPos == -1)
232             break;
233         const QString hexAddrS = wd.value.mid(0, classPos);
234         if (m_hexNullPattern.exactMatch(hexAddrS))
235             break;
236         const QByteArray type = stripPointerType(wd.type);
237         WatchData derefedWd;
238         derefedWd.setType(type);
239         derefedWd.setAddress(hexAddrS.toLatin1());
240         derefedWd.name = QString(QLatin1Char('*'));
241         derefedWd.iname = wd.iname + ".*";
242         derefedWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit;
243         const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(derefedWd, true, &m_dumperResult, errorMessage);
244         if (dr != CdbDumperHelper::DumpOk)
245             break;
246         fixDumperResult(derefedWd, &m_dumperResult, false);
247         // Insert the pointer item with 1 additional child + its dumper results
248         // Note: formal arguments might already be expanded in the symbol group.
249         WatchData ptrWd = wd;
250         ptrWd.source = OwnerDumper | CdbSymbolGroupContext::ChildrenKnownBit;
251         ptrWd.setHasChildren(true);
252         ptrWd.setChildrenUnneeded();
253         m_wh->insertData(ptrWd);
254         m_wh->insertBulkData(m_dumperResult);
255         handled = true;
256     } while (false);
257     if (debugCDBWatchHandling > 1)
258         qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
259     return handled;
260 }
261
262 WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
263 {
264     if (debugCDBWatchHandling)
265         qDebug() << "WatchHandleDumperInserter::operator=" << wd.toString();
266     // Check pointer to dumpeable, dumpeable, insert accordingly.
267     QString errorMessage;
268     if (expandPointerToDumpable(wd, &errorMessage)) {
269         // Nasty side effect: Modify owner for the ignore predicate
270         wd.source = OwnerDumper;
271         return *this;
272     }
273     // Expanded by internal dumper? : ok
274     if ((wd.source & CdbSymbolGroupContext::SourceMask) == OwnerSymbolGroupDumper) {
275         m_wh->insertData(wd);
276         return *this;
277     }
278     // Try library dumpers.
279     switch (m_dumper->dumpType(wd, true, &m_dumperResult, &errorMessage)) {
280     case CdbDumperHelper::DumpOk:
281         if (debugCDBWatchHandling)
282             qDebug() << "dumper triggered";
283         // Dumpers omit types for complicated templates
284         fixDumperResult(wd, &m_dumperResult, false);
285         // Discard the original item and insert the dumper results
286         m_wh->insertBulkData(m_dumperResult);
287         // Nasty side effect: Modify owner for the ignore predicate
288         wd.source = OwnerDumper;
289         break;
290     case CdbDumperHelper::DumpNotHandled:
291     case CdbDumperHelper::DumpError:
292         wd.source = OwnerSymbolGroup;
293         m_wh->insertData(wd);
294         break;
295     }
296     return *this;
297 }
298
299
300 CdbSymbolGroupRecursionContext::CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx,
301                                                          int ido) :
302     context(ctx),
303     internalDumperOwner(ido)
304 {
305 }
306
307 static inline CdbSymbolGroupContext::SymbolState getSymbolState(const DEBUG_SYMBOL_PARAMETERS &p)
308 {
309     if (p.SubElements == 0u)
310         return CdbSymbolGroupContext::LeafSymbol;
311     return (p.Flags & DEBUG_SYMBOL_EXPANDED) ?
312                CdbSymbolGroupContext::ExpandedSymbol :
313                CdbSymbolGroupContext::CollapsedSymbol;
314 }
315
316 CdbSymbolGroupContext::CdbSymbolGroupContext(const QString &prefix,
317                                              CIDebugSymbolGroup *symbolGroup,
318                                              const QSharedPointer<CdbDumperHelper> &dumper,
319                                              const QStringList &uninitializedVariables) :
320     CdbCore::SymbolGroupContext(prefix, symbolGroup,
321                                 dumper->comInterfaces()->debugDataSpaces,
322                                 uninitializedVariables),
323     m_useDumpers(dumper->isEnabled() && theDebuggerBoolSetting(UseDebuggingHelpers)),
324     m_dumper(dumper)
325 {
326     setShadowedNameFormat(WatchData::shadowedNameFormat());
327 }
328
329 CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix,
330                                                      CIDebugSymbolGroup *symbolGroup,
331                                                      const QSharedPointer<CdbDumperHelper> &dumper,
332                                                      const QStringList &uninitializedVariables,
333                                                      QString *errorMessage)
334 {
335     CdbSymbolGroupContext *rc = new CdbSymbolGroupContext(prefix, symbolGroup,
336                                                           dumper,
337                                                           uninitializedVariables);
338     if (!rc->init(errorMessage)) {
339         delete rc;
340         return 0;
341     }
342     return rc;
343 }
344
345 // Fix display values: Pass through strings, convert unsigned integers
346 // to decimal ('0x5454`fedf'), remove inner templates from
347 // "0x4343 class list<>".
348 static inline QString fixValue(const QString &value, const QByteArray &type)
349 {
350     // Pass through strings
351     if (value.endsWith(QLatin1Char('"')))
352         return value;
353     const int size = value.size();
354     // Real Integer numbers Unsigned hex numbers (0x)/decimal numbers (0n)
355     if (type != "bool" && isIntType(type)) {
356         const QVariant intValue = CdbCore::SymbolGroupContext::getIntValue(value);
357         if (intValue.isValid())
358             return intValue.toString();
359     }
360     return size < 20 ? value : CdbCore::SymbolGroupContext::removeInnerTemplateType(value);
361 }
362
363 unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd)
364 {
365     ULONG typeId;
366     ULONG64 address;
367     QString iname;
368     QString value;
369     QString type;
370     const unsigned rc = dumpValue(index, &iname, &(wd->name), &address,
371                                   &typeId, &type, &value);
372     wd->exp = wd->iname = iname.toLatin1();
373     wd->setAddress("0x" + QByteArray::number(address, 16));
374     wd->setType(type.toUtf8(), false);
375     if (rc & OutOfScope) {
376         wd->setError(WatchData::msgNotInScope());
377     } else {
378         wd->setValue(fixValue(value, type.toUtf8()));
379
380         const bool hasChildren = rc & HasChildren;
381         wd->setHasChildren(hasChildren);
382         if (hasChildren)
383             wd->setChildrenNeeded();
384     }
385     if (debug > 1)
386         qDebug() << "watchDataAt" << index << QString::number(rc, 16) << wd->toString();
387     return rc;
388 }
389
390 bool CdbSymbolGroupContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
391 {
392     if (debugCDBWatchHandling)
393         qDebug() << ">populateModelInitially dumpers=" << m_useDumpers;
394     // Recurse down items that are initially expanded in the view, stop processing for
395     // dumper items.
396     const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
397     const bool rc = m_useDumpers ?
398         populateModelInitiallyHelper(rctx,
399                                      WatchHandleDumperInserter(wh, m_dumper),
400                                      WatchHandlerExpandedPredicate(wh),
401                                      isDumperPredicate,
402                                      errorMessage) :
403         populateModelInitiallyHelper(rctx,
404                                      WatchHandlerModelInserter(wh),
405                                      WatchHandlerExpandedPredicate(wh),
406                                      falsePredicate,
407                                      errorMessage);
408     if (debugCDBWatchHandling)
409         qDebug("<populateModelInitially\n%s\n", qPrintable(toString()));
410     return rc;
411 }
412
413 bool CdbSymbolGroupContext::completeData(const WatchData &incompleteLocal,
414                                          WatchHandler *wh,
415                                          QString *errorMessage)
416 {
417     if (debugCDBWatchHandling)
418         qDebug(">completeData src=%d %s\n%s\n",
419                incompleteLocal.source, qPrintable(incompleteLocal.toString()),
420                qPrintable(toString()));
421
422     const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
423     // Expand symbol group items, recurse one level from desired item
424     if (!m_useDumpers) {
425         return completeDataHelper(rctx, incompleteLocal,
426                                   WatchHandlerModelInserter(wh),
427                                   MatchINamePredicate(incompleteLocal.iname),
428                                   falsePredicate,
429                                   errorMessage);
430     }
431
432     // Expand artifical dumper items
433     if ((incompleteLocal.source & CdbSymbolGroupContext::SourceMask) == OwnerDumper) {
434         // If the model queries the expanding item by pretending childrenNeeded=1,
435         // refuse the request if the children are already known
436         if (incompleteLocal.state == WatchData::ChildrenNeeded && (incompleteLocal.source & CdbSymbolGroupContext::ChildrenKnownBit)) {
437             WatchData local = incompleteLocal;
438             local.setChildrenUnneeded();
439             wh->insertData(local);
440             return true;
441         }
442         QList<WatchData> dumperResult;
443         const CdbDumperHelper::DumpResult dr = m_dumper->dumpType(incompleteLocal, true, &dumperResult, errorMessage);
444         if (dr == CdbDumperHelper::DumpOk) {
445             // Hack to stop endless model recursion
446             const bool suppressGrandChildren = !wh->isExpandedIName(incompleteLocal.iname);
447             fixDumperResult(incompleteLocal, &dumperResult, suppressGrandChildren);
448             wh->insertBulkData(dumperResult);
449         } else {
450             const QString msg = QString::fromLatin1("Unable to further expand dumper watch data: '%1' (%2): %3/%4")
451                 .arg(incompleteLocal.name)
452                 .arg(QString::fromUtf8(incompleteLocal.type))
453                 .arg(int(dr)).arg(*errorMessage);
454             qWarning("%s", qPrintable(msg));
455             WatchData wd = incompleteLocal;
456             if (wd.isValueNeeded())
457                 wd.setValue(QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown>"));
458             wd.setHasChildren(false);
459             wd.setAllUnneeded();
460             wh->insertData(wd);
461         }
462         return true;
463     }
464
465     // Expand symbol group items, recurse one level from desired item
466     return completeDataHelper(rctx, incompleteLocal,
467                               WatchHandleDumperInserter(wh, m_dumper),
468                               MatchINamePredicate(incompleteLocal.iname),
469                               isDumperPredicate,
470                               errorMessage);
471 }
472
473 bool CdbSymbolGroupContext::editorToolTip(const QString &iname,
474                                          QString *value,
475                                          QString *errorMessage)
476 {
477     if (debugToolTips)
478         qDebug() << iname;
479     value->clear();
480     unsigned long index;
481     if (!lookupPrefix(iname, &index)) {
482         *errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
483         return false;
484     }
485     // Check dumpers. Should actually be just one item.
486
487     WatchData wd;
488     const unsigned rc = watchDataAt(index, &wd);
489     if (m_useDumpers && !wd.error
490         && (0u == (rc & CdbCore::SymbolGroupContext::InternalDumperMask))
491         && m_dumper->state() != CdbDumperHelper::Disabled) {
492         QList<WatchData> result;
493         if (CdbDumperHelper::DumpOk == m_dumper->dumpType(wd, false, &result, errorMessage))  {
494             foreach (const WatchData &dwd, result) {
495                 if (!value->isEmpty())
496                     value->append(QLatin1Char('\n'));
497                 value->append(dwd.toToolTip());
498             }
499             return true;
500         } // Dumped ok
501     }     // has Dumpers
502     if (debugToolTips)
503         qDebug() << iname << wd.toString();
504     *value = wd.toToolTip();
505     return true;
506 }
507
508 } // namespace Internal
509 } // namespace Debugger