1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
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.
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 ** If you are unsure which license is appropriate for your use, please
26 ** contact the sales department at http://qt.nokia.com/contact.
28 **************************************************************************/
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"
38 #include <QtCore/QDebug>
39 #include <QtCore/QTextStream>
40 #include <QtCore/QCoreApplication>
41 #include <QtCore/QRegExp>
44 enum { debugInternalDumpers = 0 };
49 enum { OwnerNewItem, OwnerSymbolGroup, OwnerSymbolGroupDumper , OwnerDumper };
51 typedef QSharedPointer<CdbDumperHelper> SharedPointerCdbDumperHelper;
52 typedef QList<WatchData> WatchDataList;
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; }
60 static inline void debugWatchDataList(const QList<WatchData> &l, const char *why = 0)
62 QDebug nospace = qDebug().nospace();
64 nospace << why << '\n';
65 foreach(const WatchData &wd, l)
66 nospace << wd.toString() << '\n';
70 // Match an item that is expanded in the watchhandler.
71 class WatchHandlerExpandedPredicate {
73 explicit inline WatchHandlerExpandedPredicate(const WatchHandler *wh) : m_wh(wh) {}
74 inline bool operator()(const WatchData &wd) { return m_wh->isExpandedIName(wd.iname); }
76 const WatchHandler *m_wh;
79 // Match an item by iname
80 class MatchINamePredicate {
82 explicit inline MatchINamePredicate(const QString &iname) : m_iname(iname) {}
83 inline bool operator()(const WatchData &wd) { return wd.iname == m_iname; }
85 const QString &m_iname;
88 // Put a sequence of WatchData into the model for the non-dumper case
89 class WatchHandlerModelInserter {
91 explicit WatchHandlerModelInserter(WatchHandler *wh) : m_wh(wh) {}
93 inline WatchHandlerModelInserter & operator*() { return *this; }
94 inline WatchHandlerModelInserter &operator=(const WatchData &wd) {
98 inline WatchHandlerModelInserter &operator++() { return *this; }
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 {
112 explicit WatchHandleDumperInserter(WatchHandler *wh,
113 const SharedPointerCdbDumperHelper &dumper);
115 inline WatchHandleDumperInserter & operator*() { return *this; }
116 inline WatchHandleDumperInserter &operator=(WatchData &wd);
117 inline WatchHandleDumperInserter &operator++() { return *this; }
120 bool expandPointerToDumpable(const WatchData &wd, QString *errorMessage);
122 const QRegExp m_hexNullPattern;
124 const SharedPointerCdbDumperHelper m_dumper;
125 QList<WatchData> m_dumperResult;
128 WatchHandleDumperInserter::WatchHandleDumperInserter(WatchHandler *wh,
129 const SharedPointerCdbDumperHelper &dumper) :
130 m_hexNullPattern(QLatin1String("0x0+")),
134 Q_ASSERT(m_hexNullPattern.isValid());
137 // Prevent recursion of the model by setting value and type
138 static inline bool fixDumperType(WatchData *wd, const WatchData *source = 0)
140 const bool missing = wd->isTypeNeeded() || wd->type.isEmpty();
142 static const QByteArray unknownType =
143 QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Type>")
145 wd->setType(source ? source->type : unknownType, false);
150 static inline bool fixDumperValue(WatchData *wd, const WatchData *source = 0)
152 const bool missing = wd->isValueNeeded();
154 if (source && source->isValueKnown()) {
155 wd->setValue(source->value);
157 static const QString unknownValue = QCoreApplication::translate("CdbSymbolGroupContext", "<Unknown Value>");
158 wd->setValue(unknownValue);
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)
171 const int size = result->size();
174 if (debugCDBWatchHandling)
175 debugWatchDataList(*result, suppressGrandChildren ? ">fixDumperResult suppressGrandChildren" : ">fixDumperResult");
176 WatchData &returned = result->front();
177 if (returned.iname != source.iname)
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;
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) {
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);
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);
211 if (debugCDBWatchHandling)
212 debugWatchDataList(*result, "<fixDumperResult");
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)
223 if (debugCDBWatchHandling > 1)
224 qDebug() << ">expandPointerToDumpable" << wd.toString();
226 bool handled = false;
228 if (wd.error || !isPointerType(wd.type))
230 const int classPos = wd.value.indexOf(" class ");
233 const QString hexAddrS = wd.value.mid(0, classPos);
234 if (m_hexNullPattern.exactMatch(hexAddrS))
236 const QByteArray type = stripPointerType(wd.type);
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)
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);
257 if (debugCDBWatchHandling > 1)
258 qDebug() << "<expandPointerToDumpable returns " << handled << *errorMessage;
262 WatchHandleDumperInserter &WatchHandleDumperInserter::operator=(WatchData &wd)
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;
273 // Expanded by internal dumper? : ok
274 if ((wd.source & CdbSymbolGroupContext::SourceMask) == OwnerSymbolGroupDumper) {
275 m_wh->insertData(wd);
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;
290 case CdbDumperHelper::DumpNotHandled:
291 case CdbDumperHelper::DumpError:
292 wd.source = OwnerSymbolGroup;
293 m_wh->insertData(wd);
300 CdbSymbolGroupRecursionContext::CdbSymbolGroupRecursionContext(CdbSymbolGroupContext *ctx,
303 internalDumperOwner(ido)
307 static inline CdbSymbolGroupContext::SymbolState getSymbolState(const DEBUG_SYMBOL_PARAMETERS &p)
309 if (p.SubElements == 0u)
310 return CdbSymbolGroupContext::LeafSymbol;
311 return (p.Flags & DEBUG_SYMBOL_EXPANDED) ?
312 CdbSymbolGroupContext::ExpandedSymbol :
313 CdbSymbolGroupContext::CollapsedSymbol;
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)),
326 setShadowedNameFormat(WatchData::shadowedNameFormat());
329 CdbSymbolGroupContext *CdbSymbolGroupContext::create(const QString &prefix,
330 CIDebugSymbolGroup *symbolGroup,
331 const QSharedPointer<CdbDumperHelper> &dumper,
332 const QStringList &uninitializedVariables,
333 QString *errorMessage)
335 CdbSymbolGroupContext *rc = new CdbSymbolGroupContext(prefix, symbolGroup,
337 uninitializedVariables);
338 if (!rc->init(errorMessage)) {
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)
350 // Pass through strings
351 if (value.endsWith(QLatin1Char('"')))
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();
360 return size < 20 ? value : CdbCore::SymbolGroupContext::removeInnerTemplateType(value);
363 unsigned CdbSymbolGroupContext::watchDataAt(unsigned long index, WatchData *wd)
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());
378 wd->setValue(fixValue(value, type.toUtf8()));
380 const bool hasChildren = rc & HasChildren;
381 wd->setHasChildren(hasChildren);
383 wd->setChildrenNeeded();
386 qDebug() << "watchDataAt" << index << QString::number(rc, 16) << wd->toString();
390 bool CdbSymbolGroupContext::populateModelInitially(WatchHandler *wh, QString *errorMessage)
392 if (debugCDBWatchHandling)
393 qDebug() << ">populateModelInitially dumpers=" << m_useDumpers;
394 // Recurse down items that are initially expanded in the view, stop processing for
396 const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
397 const bool rc = m_useDumpers ?
398 populateModelInitiallyHelper(rctx,
399 WatchHandleDumperInserter(wh, m_dumper),
400 WatchHandlerExpandedPredicate(wh),
403 populateModelInitiallyHelper(rctx,
404 WatchHandlerModelInserter(wh),
405 WatchHandlerExpandedPredicate(wh),
408 if (debugCDBWatchHandling)
409 qDebug("<populateModelInitially\n%s\n", qPrintable(toString()));
413 bool CdbSymbolGroupContext::completeData(const WatchData &incompleteLocal,
415 QString *errorMessage)
417 if (debugCDBWatchHandling)
418 qDebug(">completeData src=%d %s\n%s\n",
419 incompleteLocal.source, qPrintable(incompleteLocal.toString()),
420 qPrintable(toString()));
422 const CdbSymbolGroupRecursionContext rctx(this, OwnerSymbolGroupDumper);
423 // Expand symbol group items, recurse one level from desired item
425 return completeDataHelper(rctx, incompleteLocal,
426 WatchHandlerModelInserter(wh),
427 MatchINamePredicate(incompleteLocal.iname),
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);
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);
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);
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),
473 bool CdbSymbolGroupContext::editorToolTip(const QString &iname,
475 QString *errorMessage)
481 if (!lookupPrefix(iname, &index)) {
482 *errorMessage = QString::fromLatin1("%1 not found.").arg(iname);
485 // Check dumpers. Should actually be just one item.
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());
503 qDebug() << iname << wd.toString();
504 *value = wd.toToolTip();
508 } // namespace Internal
509 } // namespace Debugger