1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 // std::copy is perfectly fine, don't let MSVC complain about it being deprecated
27 #pragma warning (disable: 4996)
28 
29 #ifndef NOMINMAX
30 #  define NOMINMAX
31 #endif
32 
33 #include "symbolgroupvalue.h"
34 #include "symbolgroup.h"
35 #include "stringutils.h"
36 #include "containers.h"
37 #include "extensioncontext.h"
38 
39 #include <iomanip>
40 #include <algorithm>
41 #include <limits>
42 #include <ctype.h>
43 #include <unordered_map>
44 
45 typedef std::vector<int>::size_type VectorIndexType;
46 
47 typedef std::unordered_map<std::string, SymbolAncestorInfo> AncestorInfos;
48 
49 static std::unordered_map<std::string, AncestorInfos> typeAncestorInfos;
50 
51 /*!
52     \class SymbolGroupValueContext
53     \brief The SymbolGroupValueContext class passes all IDebug interfaces
54     required for SymbolGroupValue.
55     \ingroup qtcreatorcdbext */
56 
57 /*! \class SymbolGroupValue
58 
59     \brief The SymbolGroupValue class is a flyweight tied to a SymbolGroupNode
60     providing a convenient operator[] (name, index) and value
61     getters for notation of dumpers.
62 
63     Inaccessible members return a SymbolGroupValue in state 'invalid'.
64     Example:
65     \code
66     SymbolGroupValue container(symbolGroupNode, symbolGroupValueContext);
67     if (SymbolGroupValue sizeV = container["d"]["size"])
68       int size = sizeV.intValue()
69     \endcode
70     \ingroup qtcreatorcdbext
71     */
72 
73 unsigned SymbolGroupValue::verbose = 0;
74 
SymbolGroupValue(const std::string & parentError)75 SymbolGroupValue::SymbolGroupValue(const std::string &parentError) :
76     m_errorMessage(parentError)
77 {
78     if (m_errorMessage.empty())
79         m_errorMessage = "Invalid";
80 }
81 
SymbolGroupValue(SymbolGroupNode * node,const SymbolGroupValueContext & ctx)82 SymbolGroupValue::SymbolGroupValue(SymbolGroupNode *node,
83                                    const SymbolGroupValueContext &ctx) :
84     m_node(node), m_context(ctx)
85 {
86     if (m_node && !m_node->isMemoryAccessible()) { // Invalid if no value
87         m_node = 0;
88         if (SymbolGroupValue::verbose)
89             DebugPrint() << node->name() << '/' << node->iName() << '/'
90                          << node->type() << " memory access error";
91     }
92 }
93 
SymbolGroupValue()94 SymbolGroupValue::SymbolGroupValue() :
95     m_errorMessage("Invalid")
96 {
97 }
98 
isValid() const99 bool SymbolGroupValue::isValid() const
100 {
101     return m_node != 0 && m_context.dataspaces != 0;
102 }
103 
104 // Debug helper
formatNodeError(const AbstractSymbolGroupNode * n,std::ostream & os)105 static void formatNodeError(const AbstractSymbolGroupNode *n, std::ostream &os)
106 {
107     const AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector &children = n->children();
108     const VectorIndexType size = children.size();
109     if (const SymbolGroupNode *sn = n->asSymbolGroupNode()) {
110         os << "type: " << sn->type() << ", raw value: \"" << wStringToString(sn->symbolGroupRawValue())
111            << "\", 0x" << std::hex << sn->address() << ", " << std::dec;
112     }
113     if (size) {
114         os << "children (" << size << "): [";
115         for (VectorIndexType i = 0; i < size; ++i)
116             os << ' ' << children.at(i)->name();
117         os << ']';
118     } else {
119         os << "No children";
120     }
121 }
122 
operator [](unsigned index) const123 SymbolGroupValue SymbolGroupValue::operator[](unsigned index) const
124 {
125     if (ensureExpanded())
126         if (index < m_node->children().size())
127             if (SymbolGroupNode *n = m_node->childAt(index)->asSymbolGroupNode())
128                 return SymbolGroupValue(n, m_context);
129     if (isValid() && SymbolGroupValue::verbose) {
130         DebugPrint dp;
131         dp << name() << "::operator[](#" << index << ") failed. ";
132         formatNodeError(m_node, dp);
133     }
134     return SymbolGroupValue(m_errorMessage);
135 }
136 
addSymbolForAncestor(const std::string & ancestorName) const137 SymbolGroupValue SymbolGroupValue::addSymbolForAncestor(const std::string &ancestorName) const
138 {
139     const SymbolAncestorInfo info = infoOfAncestor(ancestorName);
140     if (info.isValid()) {
141         const ULONG64 base = isPointerType(type()) ? pointerValue() : address();
142         return addSymbol(base + info.offset, stripClassPrefixes(info.type));
143     }
144     if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors
145         DebugPrint dp;
146         dp << this->name() << "::addSymbolForAncestor(\"" << ancestorName << "\") failed. ";
147         formatNodeError(m_node, dp);
148     }
149     return SymbolGroupValue(m_errorMessage);
150 }
151 
readIntegerFromAncestor(const std::string & name,int defaultValue) const152 int SymbolGroupValue::readIntegerFromAncestor(const std::string &name, int defaultValue) const
153 {
154     return readPODFromAncestor<int>(name, defaultValue);
155 }
156 
offsetOfChild(const SymbolGroupValue & child) const157 ULONG64 SymbolGroupValue::offsetOfChild(const SymbolGroupValue &child) const
158 {
159     const ULONG64 base = isPointerType(type()) ? pointerValue() : address();
160     const ULONG64 childAddress = child.address();
161     if (base == 0 || childAddress == 0)
162         return 0;
163     return childAddress - base;
164 }
165 
offsetOfAncestor(const std::string & name) const166 LONG64 SymbolGroupValue::offsetOfAncestor(const std::string &name) const
167 {
168     return infoOfAncestor(name).offset;
169 }
170 
addressOfAncestor(const std::string & name) const171 ULONG64 SymbolGroupValue::addressOfAncestor(const std::string &name) const
172 {
173     const ULONG64 base = isPointerType(type()) ? pointerValue() : address();
174     LONG64 offset = offsetOfAncestor(name);
175     return offset >= 0 ? base + ULONG64(offset) : 0;
176 }
177 
typeOfAncestor(const std::string & name) const178 std::string SymbolGroupValue::typeOfAncestor(const std::string &name) const
179 {
180     return infoOfAncestor(name).type;
181 }
182 
infoOfAncestor(const std::string & name) const183 SymbolAncestorInfo SymbolGroupValue::infoOfAncestor(const std::string &name) const
184 {
185     const std::string &typeName = type();
186     AncestorInfos &offsets = typeAncestorInfos[typeName];
187     auto offsetIt = offsets.find(name);
188     if (offsetIt != offsets.end())
189         return offsetIt->second;
190 
191     SymbolAncestorInfo info;
192     if (!ensureExpanded())
193         return info;
194 
195     if (AbstractSymbolGroupNode *abstractChildNode = m_node->childByIName(name.c_str())) {
196         if (SymbolGroupNode *childNode = abstractChildNode->asSymbolGroupNode()) {
197             SymbolGroupValue child(childNode, m_context);
198             ULONG64 childAddress = child.address();
199             if (childAddress == 0)
200                 return info;
201             const ULONG64 base = isPointerType(typeName) ? pointerValue() : address();
202             info.offset = LONG64(childAddress - base);
203             info.type = child.type();
204         }
205     }
206 
207     if (!info.isValid()) {
208         // Search recursively for ancestor
209         for (AbstractSymbolGroupNode *abstractChildNode : m_node->children())  {
210             if (SymbolGroupNode *childNode = abstractChildNode->asSymbolGroupNode()) {
211                 SymbolGroupValue child(childNode, m_context);
212                 if (isPointerType(child.type()))
213                     continue;
214                 info = child.infoOfAncestor(name);
215                 if (info.isValid()) {
216                     info.offset += offsetOfChild(child);
217                     break;
218                 }
219             }
220         }
221     }
222 
223     if (info.isValid())
224         offsets[name] = info;
225     return info;
226 }
227 
addSymbol(const ULONG64 address,const std::string & type) const228 SymbolGroupValue SymbolGroupValue::addSymbol(const ULONG64 address, const std::string &type) const
229 {
230     const std::string &pointerToType = pointedToSymbolName(address, type);
231     std::string tmp;
232     if (SymbolGroupNode *ancestorNode =
233             node()->symbolGroup()->addSymbol(module(), pointerToType, "", "", &tmp)) {
234         return SymbolGroupValue(ancestorNode, m_context);
235     }
236     if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors
237         DebugPrint dp;
238         dp << this->name() << "::addSymbol(\"" << address << "\", \"" << address << "\") failed. ";
239         formatNodeError(m_node, dp);
240     }
241     return SymbolGroupValue(m_errorMessage);
242 
243 }
244 
childCount() const245 unsigned SymbolGroupValue::childCount() const
246 {
247     if (ensureExpanded())
248         return unsigned(m_node->children().size());
249     return 0;
250 }
251 
parent() const252 SymbolGroupValue SymbolGroupValue::parent() const
253 {
254     if (isValid())
255         if (AbstractSymbolGroupNode *aParent = m_node->parent())
256             if (SymbolGroupNode *parent = aParent->asSymbolGroupNode())
257                 return SymbolGroupValue(parent, m_context);
258     return SymbolGroupValue("parent() invoked on invalid value.");
259 }
260 
ensureExpanded() const261 bool SymbolGroupValue::ensureExpanded() const
262 {
263     if (!isValid() || !m_node->canExpand())
264         return false;
265 
266     if (m_node->isExpanded())
267         return true;
268 
269     // Set a flag indicating the node was expanded by SymbolGroupValue
270     // and not by an explicit request from the watch model.
271     if (m_node->expand(&m_errorMessage))
272         return true;
273     if (SymbolGroupValue::verbose)
274         DebugPrint() << "Expand failure of '" << name() << "': " << m_errorMessage;
275     return false;
276 }
277 
operator [](const char * name) const278 SymbolGroupValue SymbolGroupValue::operator[](const char *name) const
279 {
280     if (ensureExpanded())
281         if (AbstractSymbolGroupNode *child = m_node->childByIName(name))
282             if (SymbolGroupNode *n = child->asSymbolGroupNode())
283                 return SymbolGroupValue(n, m_context);
284     if (isValid() && SymbolGroupValue::verbose) { // Do not report subsequent errors
285         DebugPrint dp;
286         dp << this->name() << "::operator[](\"" << name << "\") failed. ";
287         formatNodeError(m_node, dp);
288     }
289     return SymbolGroupValue(m_errorMessage);
290 }
291 
type() const292 std::string SymbolGroupValue::type() const
293 {
294     return isValid() ? m_node->type() : std::string();
295 }
296 
name() const297 std::string SymbolGroupValue::name() const
298 {
299     return isValid() ? m_node->name() : std::string();
300 }
301 
size() const302 unsigned SymbolGroupValue::size() const
303 {
304     return isValid() ? m_node->size() : 0;
305 }
306 
value() const307 std::wstring SymbolGroupValue::value() const
308 {
309     return isValid() ? m_node->symbolGroupFixedValue() : std::wstring();
310 }
311 
floatValue(double defaultValue) const312 double SymbolGroupValue::floatValue(double defaultValue) const
313 {
314     double f = defaultValue;
315     if (isValid()) {
316         std::wistringstream str(value());
317         str >> f;
318         if (str.fail())
319             f = defaultValue;
320     }
321     return f;
322 }
323 
intValue(int defaultValue) const324 int SymbolGroupValue::intValue(int defaultValue) const
325 {
326     if (isValid()) {
327         int rc = 0;
328         // Is this an enumeration "EnumValue (0n12)", -> convert to integer
329         std::wstring v = value();
330         const std::wstring::size_type enPos = v.find(L"(0n");
331         if (enPos != std::wstring::npos && v.at(v.size() - 1) == L')')
332             v = v.substr(enPos + 3, v.size() - 4);
333         if (integerFromString(wStringToString(v), &rc))
334             return rc;
335     }
336     if (SymbolGroupValue::verbose)
337         DebugPrint() << name() << '/' << type() << '/'<< m_errorMessage << ": intValue() fails";
338     return defaultValue;
339 }
340 
pointerValue(ULONG64 defaultValue) const341 ULONG64 SymbolGroupValue::pointerValue(ULONG64 defaultValue) const
342 {
343     if (isValid()) {
344         ULONG64 rc = 0;
345         if (integerFromString(wStringToString(value()), &rc))
346             return rc;
347     }
348     if (SymbolGroupValue::verbose)
349         DebugPrint() << name() << '/'<< type() << '/' << m_errorMessage << ": pointerValue() fails";
350     return defaultValue;
351 }
352 
353 // Read a POD value from debuggee memory and convert into host variable type POD.
354 // For unsigned integer types, it is possible to read a smaller debuggee-unsigned
355 // into a big ULONG64 on the host side (due to endianness).
356 template<class POD>
readPODFromMemory(CIDebugDataSpaces * ds,ULONG64 address,ULONG debuggeeTypeSize,POD defaultValue,std::string * errorMessage)357     POD readPODFromMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG debuggeeTypeSize,
358                           POD defaultValue, std::string *errorMessage /* = 0 */)
359 {
360     POD rc = defaultValue;
361     if (debuggeeTypeSize == 0 || debuggeeTypeSize > sizeof(POD)) // Safety check.
362         return rc;
363     if (const unsigned char *buffer = SymbolGroupValue::readMemory(ds, address, debuggeeTypeSize, errorMessage)) {
364         memcpy(&rc, buffer, debuggeeTypeSize);
365         delete [] buffer;
366     }
367     return rc;
368 }
369 
370 template<class POD>
readPODFromAncestor(const std::string & name,POD defaultValue) const371 POD SymbolGroupValue::readPODFromAncestor(const std::string &name, POD defaultValue) const
372 {
373     ULONG64 address = addressOfAncestor(name.c_str());
374     if (address == 0)
375         return defaultValue;
376     return readPODFromMemory<POD>(m_context.dataspaces, address, sizeof(POD), defaultValue, 0);
377 }
378 
readPointerValue(CIDebugDataSpaces * ds,ULONG64 address,std::string * errorMessage)379 ULONG64 SymbolGroupValue::readPointerValue(CIDebugDataSpaces *ds, ULONG64 address,
380                                            std::string *errorMessage /* = 0 */)
381 {
382     return readPODFromMemory<ULONG64>(ds, address, SymbolGroupValue::pointerSize(), 0, errorMessage);
383 }
384 
readPointerValueFromAncestor(const std::string & name) const385 ULONG64 SymbolGroupValue::readPointerValueFromAncestor(const std::string &name) const
386 {
387     ULONG64 address = addressOfAncestor(name.c_str());
388     if (address == 0)
389         return 0;
390     return readPointerValue(m_context.dataspaces, address);
391 }
392 
readUnsignedValue(CIDebugDataSpaces * ds,ULONG64 address,ULONG debuggeeTypeSize,ULONG64 defaultValue,std::string * errorMessage)393 ULONG64 SymbolGroupValue::readUnsignedValue(CIDebugDataSpaces *ds,
394                                             ULONG64 address, ULONG debuggeeTypeSize,
395                                             ULONG64 defaultValue,
396                                             std::string *errorMessage /* = 0 */)
397 {
398     return readPODFromMemory<ULONG64>(ds, address, debuggeeTypeSize, defaultValue, errorMessage);
399 }
400 
readSignedValue(CIDebugDataSpaces * ds,ULONG64 address,ULONG debuggeeTypeSize,LONG64 defaultValue,std::string * errorMessage)401 LONG64 SymbolGroupValue::readSignedValue(CIDebugDataSpaces *ds,
402                                          ULONG64 address, ULONG debuggeeTypeSize,
403                                          LONG64 defaultValue,
404                                          std::string *errorMessage /* = 0 */)
405 {
406     return readPODFromMemory<LONG64>(ds, address, debuggeeTypeSize, defaultValue, errorMessage);
407 }
408 
409 // Note: sizeof(int) should always be 4 on Win32/Win64, no need to
410 // differentiate between host and debuggee int types. When implementing
411 // something like 'long', different size on host/debuggee must be taken
412 // into account  (shifting signedness bits around).
readIntValue(CIDebugDataSpaces * ds,ULONG64 address,int defaultValue,std::string * errorMessage)413 int SymbolGroupValue::readIntValue(CIDebugDataSpaces *ds,
414                                    ULONG64 address, int defaultValue,
415                                    std::string *errorMessage /* = 0 */)
416 {
417     return readPODFromMemory<int>(ds, address, sizeof(int),
418                                   defaultValue, errorMessage);
419 }
420 
readDouble(CIDebugDataSpaces * ds,ULONG64 address,double defaultValue,std::string * errorMessage)421 double SymbolGroupValue::readDouble(CIDebugDataSpaces *ds, ULONG64 address, double defaultValue,
422                                     std::string *errorMessage /* = 0 */)
423 {
424     return readPODFromMemory<double>(ds, address, sizeof(double), defaultValue, errorMessage);
425 }
426 
427 // Read memory and return allocated array
readMemory(CIDebugDataSpaces * ds,ULONG64 address,ULONG length,std::string * errorMessage)428 unsigned char *SymbolGroupValue::readMemory(CIDebugDataSpaces *ds, ULONG64 address, ULONG length,
429                                             std::string *errorMessage /* = 0 */)
430 {
431     unsigned char *buffer = new unsigned char[length];
432     std::fill(buffer, buffer + length, 0);
433     ULONG received = 0;
434     const HRESULT hr = ds->ReadVirtual(address, buffer, length, &received);
435     if (FAILED(hr)) {
436         delete [] buffer;
437         if (errorMessage) {
438             std::ostringstream estr;
439             estr << "Cannot read " << length << " bytes from 0x"
440                  << std::hex << address << ": "
441                  << msgDebugEngineComFailed("ReadVirtual", hr);
442             *errorMessage = estr.str();
443         }
444         return 0;
445     }
446     if (received < length && errorMessage) {
447         std::ostringstream estr;
448         estr << "Warning: Received only " << received
449              << " bytes of " << length
450              << " requested at 0x" << std::hex << address << '.';
451         *errorMessage = estr.str();
452     }
453     return buffer;
454 }
455 
writeMemory(CIDebugDataSpaces * ds,ULONG64 address,const unsigned char * data,ULONG length,std::string * errorMessage)456 bool SymbolGroupValue::writeMemory(CIDebugDataSpaces *ds, ULONG64 address,
457                                    const unsigned char *data, ULONG length,
458                                    std::string *errorMessage /* =0 */)
459 {
460     ULONG filled = 0;
461     const HRESULT hr = ds->FillVirtual(address, length,
462                                        const_cast<unsigned char *>(data),
463                                        length, &filled);
464     if (FAILED(hr)) {
465         if (errorMessage) {
466             std::ostringstream estr;
467             estr << "Cannot write " << length << " bytes to 0x"
468                  << std::hex << address << ": "
469                  << msgDebugEngineComFailed("FillVirtual", hr);
470             *errorMessage = estr.str();
471         }
472         return false;
473     }
474     if (filled < length) {
475         if (errorMessage) {
476             std::ostringstream estr;
477             estr << "Filled only " << filled << " bytes of " << length
478                  << " at 0x" << std::hex << address << '.';
479             *errorMessage = estr.str();
480         }
481         return false;
482     }
483     return true;
484 }
485 
486 // Return allocated array of data
pointerData(unsigned length) const487 unsigned char *SymbolGroupValue::pointerData(unsigned length) const
488 {
489     if (isValid())
490         if (const ULONG64 ptr = pointerValue())
491             return SymbolGroupValue::readMemory(m_context.dataspaces, ptr, length);
492     return 0;
493 }
494 
address() const495 ULONG64 SymbolGroupValue::address() const
496 {
497     if (isValid())
498         return m_node->address();
499     return 0;
500 }
501 
module() const502 std::string SymbolGroupValue::module() const
503 {
504     return isValid() ? m_node->module() : std::string();
505 }
506 
507 // Temporary iname
additionalSymbolIname(const SymbolGroup * g)508 static inline std::string additionalSymbolIname(const SymbolGroup *g)
509 {
510     std::ostringstream str;
511     str << "__additional" << g->root()->children().size();
512     return str.str();
513 }
514 
typeCast(const char * type) const515 SymbolGroupValue SymbolGroupValue::typeCast(const char *type) const
516 {
517     return typeCastedValue(address(), type);
518 }
519 
pointerTypeCast(const char * type) const520 SymbolGroupValue SymbolGroupValue::pointerTypeCast(const char *type) const
521 {
522     return typeCastedValue(pointerValue(), type);
523 }
524 
typeCastedValue(ULONG64 address,const char * type) const525 SymbolGroupValue SymbolGroupValue::typeCastedValue(ULONG64 address, const char *type) const
526 {
527     if (!address)
528         return SymbolGroupValue();
529     const size_t len = strlen(type);
530     if (!len)
531         return SymbolGroupValue();
532     const bool nonPointer = type[len - 1] != '*';
533     SymbolGroup *sg = m_node->symbolGroup();
534     // A bit of magic: For desired pointer types, we can do
535     //     'Foo *' -> '(Foo *)(address)'.
536     // For non-pointers, we need to de-reference:
537     //      'Foo' -> '*(Foo *)(address)'
538     std::ostringstream str;
539     if (nonPointer)
540         str << '*';
541     str << '(' << type;
542     if (nonPointer)
543         str << " *";
544     str << ")(" << std::showbase << std::hex << address << ')';
545     if (SymbolGroupNode *node = sg->addSymbol(module(), str.str(),
546                                               additionalSymbolIname(sg),
547                                               &m_errorMessage))
548         return SymbolGroupValue(node, m_context);
549     return SymbolGroupValue();
550 }
551 
findMember(const SymbolGroupValue & start,const std::string & symbolName)552 SymbolGroupValue SymbolGroupValue::findMember(const SymbolGroupValue &start,
553                                               const std::string &symbolName)
554 {
555     const unsigned count = start.childCount();
556     for (unsigned i = 0; i < count ; ++i) { // check members
557         const SymbolGroupValue child = start[i];
558         if (child.name() == symbolName)
559             return child;
560     }
561     // Recurse down base classes at the beginning that have class names
562     // as value.
563     for (unsigned i = 0; i < count; ++i) {
564         const SymbolGroupValue child = start[i];
565         const std::wstring value = child.value();
566         if (value.compare(0, 6, L"class ") && value.compare(0, 7, L"struct ")) {
567            break;
568         } else {
569             if (SymbolGroupValue r = findMember(child, symbolName))
570                 return r;
571         }
572     }
573     return SymbolGroupValue();
574 }
575 
wcharPointerData(unsigned charCount,unsigned maxCharCount) const576 std::wstring SymbolGroupValue::wcharPointerData(unsigned charCount, unsigned maxCharCount) const
577 {
578     const bool truncated = charCount > maxCharCount;
579     if (truncated)
580         charCount = maxCharCount;
581     if (const unsigned char *ucData = pointerData(charCount * sizeof(wchar_t))) {
582         const wchar_t *utf16Data = reinterpret_cast<const wchar_t *>(ucData);
583         // Be smart about terminating 0-character
584         if (charCount && utf16Data[charCount - 1] == 0)
585             charCount--;
586         std::wstring rc = std::wstring(utf16Data, charCount);
587         if (truncated)
588             rc += L"...";
589         delete [] ucData;
590         return rc;
591     }
592     return std::wstring();
593 }
594 
error() const595 std::string SymbolGroupValue::error() const
596 {
597     return m_errorMessage;
598 }
599 
600 // Return number of characters to strip for pointer type
isPointerType(const std::string & t)601 unsigned SymbolGroupValue::isPointerType(const std::string &t)
602 {
603     if (endsWith(t, "**"))
604         return 1;
605     if (endsWith(t, " *"))
606         return 2;
607     return 0;
608 }
609 
isArrayType(const std::string & t)610 bool SymbolGroupValue::isArrayType(const std::string &t)
611 {
612     return endsWith(t, ']');
613 }
614 
615 // Return number of characters to strip for pointer type
isVTableType(const std::string & t)616 bool SymbolGroupValue::isVTableType(const std::string &t)
617 {
618     const char vtableTypeC[] = "__fptr()";
619     return t.compare(0, sizeof(vtableTypeC) - 1, vtableTypeC) == 0;
620 }
621 
622 // add pointer type 'Foo' -> 'Foo *', 'Foo *' -> 'Foo **'
pointerType(const std::string & type)623 std::string SymbolGroupValue::pointerType(const std::string &type)
624 {
625     std::string rc = type;
626     if (!endsWith(type, '*'))
627         rc.push_back(' ');
628     rc.push_back('*');
629     return rc;
630 }
631 
pointerSize()632 unsigned SymbolGroupValue::pointerSize()
633 {
634     static const unsigned ps = SymbolGroupValue::sizeOf("char *");
635     return ps;
636 }
637 
intSize()638 unsigned SymbolGroupValue::intSize()
639 {
640     static const unsigned is = SymbolGroupValue::sizeOf("int");
641     return is;
642 }
643 
pointerDiffSize()644 unsigned SymbolGroupValue::pointerDiffSize()
645 {
646     static const unsigned is = SymbolGroupValue::sizeOf("ptrdiff_t");
647     return is;
648 }
649 
sizeOf(const char * type)650 unsigned SymbolGroupValue::sizeOf(const char *type)
651 {
652     const unsigned rc = GetTypeSize(type);
653     if (!rc && SymbolGroupValue::verbose)
654         DebugPrint() << "GetTypeSize fails for '" << type << '\'';
655     return rc;
656 }
657 
fieldOffset(const char * type,const char * field)658 unsigned SymbolGroupValue::fieldOffset(const char *type, const char *field)
659 {
660     ULONG rc = 0;
661     if (GetFieldOffset(type, field, &rc)) {
662         if (SymbolGroupValue::verbose)
663             DebugPrint() << "GetFieldOffset fails for '" << type << "' '" << field << '\'';
664         return 0;
665     }
666     return rc;
667 }
668 
stripPointerType(const std::string & t)669 std::string SymbolGroupValue::stripPointerType(const std::string &t)
670 {
671     // 'Foo *' -> 'Foo', 'Bar **' -> 'Bar *'.
672     if (const unsigned stripLength = isPointerType(t))
673         return t.substr(0, t.size() - stripLength);
674     return t;
675 }
676 
677 // Strip "class Foo", "struct Bar"-> "Foo", "Bar "
stripClassPrefixes(const std::string & type)678 std::string SymbolGroupValue::stripClassPrefixes(const std::string &type)
679 {
680     std::string rc = type;
681     if (rc.compare(0, 6, "class ") == 0) {
682         rc.erase(0, 6);
683     } else {
684         if (rc.compare(0, 7, "struct ") == 0)
685             rc.erase(0, 7);
686     }
687     return rc;
688 }
689 
690 // Strip " const" from end of type ("XX const", "XX const *[*]"
stripConst(const std::string & type)691 std::string SymbolGroupValue::stripConst(const std::string &type)
692 {
693     const std::string::size_type constPos = type.rfind(" const");
694     if (constPos == std::string::npos)
695         return type;
696     // Strip 'const' only if it is at the end 'QString const'
697     // or of some pointer like 'foo ***'
698     std::string rc = type;
699     const std::string::size_type size = rc.size();
700     std::string::size_type nextPos = constPos + 6;
701     if (nextPos == size) { // Ends with - easy.
702         rc.erase(constPos, nextPos - constPos);
703         return rc;
704     }
705     // Ensure it ends with ' ****'.
706     if (rc.at(nextPos) != ' ')
707         return rc;
708     for (std::string::size_type i = nextPos + 1; i < size; ++i)
709         if (rc.at(i) != '*')
710             return rc;
711     rc.erase(constPos, nextPos - constPos);
712     return rc;
713 }
714 
addPointerType(const std::string & t)715 std::string SymbolGroupValue::addPointerType(const std::string &t)
716 {
717     // 'char' -> 'char *' -> 'char **'
718     std::string rc = t;
719     if (!endsWith(rc, '*'))
720         rc.push_back(' ');
721     rc.push_back('*');
722     return rc;
723 }
724 
stripArrayType(const std::string & t)725 std::string SymbolGroupValue::stripArrayType(const std::string &t)
726 {
727     const std::string::size_type bracket = t.rfind('[');
728     if (bracket != std::string::npos) {
729         std::string rc = t.substr(0, bracket);
730         trimBack(rc);
731         return rc;
732     }
733     return t;
734 }
735 
stripModuleFromType(const std::string & type)736 std::string SymbolGroupValue::stripModuleFromType(const std::string &type)
737 {
738     const std::string::size_type exclPos = type.find('!');
739     return exclPos != std::string::npos ? type.substr(exclPos + 1, type.size() - exclPos - 1) : type;
740 }
741 
moduleOfType(const std::string & type)742 std::string SymbolGroupValue::moduleOfType(const std::string &type)
743 {
744     const std::string::size_type exclPos = type.find('!');
745     return exclPos != std::string::npos ? type.substr(0, exclPos) : std::string();
746 }
747 
748 // Symbol Name/(Expression) of a pointed-to instance ('Foo' at 0x10') ==> '*(Foo *)0x10'
pointedToSymbolName(ULONG64 address,const std::string & type)749 std::string SymbolGroupValue::pointedToSymbolName(ULONG64 address, const std::string &type)
750 {
751     std::ostringstream str;
752     str << "*(" << type;
753     if (!endsWith(type, '*'))
754         str << ' ';
755     str << "*)" << std::showbase << std::hex << address;
756     return str.str();
757 }
758 
759 /*!
760     \class QtInfo
761 
762     \brief The QtInfo class provides Qt information determined on demand.
763 
764     Namespace, modules, and basic class
765     names containing the module for fast lookup.
766     \ingroup qtcreatorcdbext
767 */
768 
get(const SymbolGroupValueContext & ctx)769 const QtInfo &QtInfo::get(const SymbolGroupValueContext &ctx)
770 {
771     static QtInfo rc;
772     if (!rc.libInfix.empty())
773         return rc;
774 
775     typedef std::list<std::string> StringList;
776     typedef StringList::const_iterator StringListConstIt;
777 
778     std::string moduleName;
779     std::string::size_type exclPos = std::string::npos;
780     std::string::size_type libPos = std::string::npos;
781     std::string::size_type qtPos = std::string::npos;
782 
783     const StringList &modules = SymbolGroupValue::getAllModuleNames(ctx);
784     for (StringListConstIt module = modules.begin(), total = modules.end();
785          module != total; ++module) {
786         moduleName = *module;
787         qtPos = moduleName.find("Qt");
788         if (qtPos != std::string::npos) {
789             libPos = moduleName.find("Core");
790             if (libPos != std::string::npos && (libPos - qtPos) < 4)
791                 break;
792         }
793     }
794 
795     if (libPos == std::string::npos)
796         moduleName.clear();
797     else
798         moduleName += '!';
799 
800     const char* qstrdupSymbol = "*qstrdup";
801     std::string qualifiedSymbol;
802     do {
803         // Lookup qstrdup() to get the core module
804         const std::string pattern = moduleName + qstrdupSymbol;
805         const StringList &allMatches = SymbolGroupValue::resolveSymbolName(pattern.c_str(), ctx);
806         if (!allMatches.empty()) {
807             qualifiedSymbol = allMatches.front();
808         } else {
809             if (!moduleName.empty()) {
810                 // If there is no qstrdup symbol in the module fall back to a global lookup.
811                 moduleName.clear();
812                 continue;
813             }
814         }
815 
816         exclPos = qualifiedSymbol.find('!');
817         libPos = qualifiedSymbol.find("Core");
818 
819         if (exclPos != std::string::npos) {
820             if (!moduleName.empty()) {
821                 // found the core module
822                 // determine the qt version from the module name
823                 if (isdigit(qualifiedSymbol.at(2)))
824                     rc.version = qualifiedSymbol.at(2) - '0';
825                 else
826                     rc.version = qualifiedSymbol.at(exclPos - 1) - '0';
827                 rc.libInfix = qualifiedSymbol.substr(libPos + 4, exclPos - libPos - 4);
828             } else {
829                 // Found the Qt symbol but in an unexpected module, most probably
830                 // it is a static build.
831                 rc.isStatic = true;
832                 rc.libInfix = qualifiedSymbol.substr(0, exclPos);
833                 // The Qt version cannot be determined by the module name. Looking up
834                 // qInstallMessageHandler which is in Qt since 5.0 to determine the version.
835                 const std::string pattern = rc.libInfix + "!*qInstallMessageHandler";
836                 const StringList &allMatches = SymbolGroupValue::resolveSymbolName(pattern.c_str(), ctx);
837                 const StringListConstIt match = std::find_if(allMatches.begin(), allMatches.end(),
838                                                              SubStringPredicate(rc.libInfix.c_str()));
839                 rc.version = match == allMatches.end() ? 4 : 5;
840             }
841             // Any namespace? 'QtCored4!nsp::qstrdup'
842             const std::string::size_type nameSpaceStart = exclPos + 1;
843             const std::string::size_type colonPos = qualifiedSymbol.find(':', nameSpaceStart);
844             if (colonPos != std::string::npos)
845                 rc.nameSpace = qualifiedSymbol.substr(nameSpaceStart, colonPos - nameSpaceStart);
846         } else {
847             // Can't find a basic Qt symbol so use a fallback
848             rc.libInfix = "d4";
849             rc.version = 4;
850         }
851     } while (false);
852 
853     rc.qObjectType = rc.prependQtCoreModule("QObject");
854     rc.qObjectPrivateType = rc.prependQtCoreModule("QObjectPrivate");
855     rc.qWindowPrivateType = rc.prependQtGuiModule("QWindowPrivate");
856     rc.qWidgetPrivateType = rc.prependQtModule("QWidgetPrivate", rc.version >= 5 ? Widgets : Gui);
857     if (SymbolGroupValue::verbose)
858         DebugPrint() << rc;
859     return rc;
860 }
861 
moduleName(Module m) const862 std::string QtInfo::moduleName(Module m) const
863 {
864     if (isStatic)
865         return libInfix;
866     // Must match the enumeration
867     static const char* modNames[] =
868         {"Core", "Gui", "Widgets", "Network", "Script", "Qml" };
869     std::ostringstream result;
870     result << "Qt";
871     if (version >= 5)
872         result << version;
873     result << modNames[m] << libInfix;
874     return result.str();
875 }
876 
prependModuleAndNameSpace(const std::string & type,const std::string & module,const std::string & aNameSpace)877 std::string QtInfo::prependModuleAndNameSpace(const std::string &type,
878                                               const std::string &module,
879                                               const std::string &aNameSpace)
880 {
881     // Strip the prefixes "class ", "struct ".
882     std::string rc = SymbolGroupValue::stripClassPrefixes(type);
883     // Is the namespace 'nsp::' missing?
884     if (!aNameSpace.empty()) {
885         const bool nameSpaceMissing = rc.size() <= aNameSpace.size()
886                 || rc.compare(0, aNameSpace.size(), aNameSpace) != 0
887                 || rc.at(aNameSpace.size()) != ':';
888         if (nameSpaceMissing) {
889             rc.insert(0, "::");
890             rc.insert(0, aNameSpace);
891         }
892     }
893     // Is the module 'Foo!' missing?
894     if (!module.empty()) {
895         const bool moduleMissing = rc.size() <= module.size()
896                 || rc.compare(0, module.size(), module) != 0
897                 || rc.at(module.size()) != '!';
898         if (moduleMissing) {
899             rc.insert(0, 1, '!');
900             rc.insert(0, module);
901         }
902     }
903     return rc;
904 }
905 
qtTypeInfoVersion(const SymbolGroupValueContext & ctx)906 int QtInfo::qtTypeInfoVersion(const SymbolGroupValueContext &ctx)
907 {
908     const QtInfo qtInfo = QtInfo::get(ctx);
909     const std::string &hookDataSymbolName = qtInfo.prependQtCoreModule("qtHookData");
910     ULONG64 offset = 0;
911     if (FAILED(ctx.symbols->GetOffsetByName(hookDataSymbolName.c_str(), &offset)))
912         return 0;
913     ULONG64 hookVer = SymbolGroupValue::readPointerValue(ctx.dataspaces, offset);
914     if (hookVer < 3)
915         return 0;
916     offset += 6 * SymbolGroupValue::pointerSize();
917     return static_cast<int>(SymbolGroupValue::readPointerValue(ctx.dataspaces, offset));
918 }
919 
operator <<(std::ostream & os,const QtInfo & i)920 std::ostream &operator<<(std::ostream &os, const QtInfo &i)
921 {
922     os << "Qt Info: Version: " << i.version << " Modules '"
923        << i.moduleName(QtInfo::Core) << "', '" << i.moduleName(QtInfo::Gui)
924        << "', Namespace='" << i.nameSpace
925        << "', types: " << i.qObjectType << ','
926        << i.qObjectPrivateType << ',' << i.qWidgetPrivateType;
927     return os;
928 }
929 
930 std::list<std::string>
resolveSymbolName(const char * pattern,const SymbolGroupValueContext & c,std::string * errorMessage)931     SymbolGroupValue::resolveSymbolName(const char *pattern,
932                                     const SymbolGroupValueContext &c,
933                                     std::string *errorMessage /* = 0 */)
934 {
935     // Extract the names
936     const SymbolList symbols = resolveSymbol(pattern, c, errorMessage);
937     std::list<std::string> rc;
938     if (!symbols.empty()) {
939         const SymbolList::const_iterator cend = symbols.end();
940         for (SymbolList::const_iterator it = symbols.begin(); it != cend; ++it)
941             rc.push_back(it->first);
942     }
943     return rc;
944 
945 }
946 
947 SymbolGroupValue::SymbolList
resolveSymbol(const char * pattern,const SymbolGroupValueContext & c,std::string * errorMessage)948     SymbolGroupValue::resolveSymbol(const char *pattern,
949                                     const SymbolGroupValueContext &c,
950                                     std::string *errorMessage /* = 0 */)
951 {
952     enum { bufSize = 2048 };
953     std::list<Symbol> rc;
954     if (errorMessage)
955         errorMessage->clear();
956     // Is it an incomplete symbol?
957     if (!pattern[0])
958         return rc;
959 
960     ULONG64 handle = 0;
961     // E_NOINTERFACE means "no match". Apparently, it does not always
962     // set handle.
963     HRESULT hr = c.symbols->StartSymbolMatch(pattern, &handle);
964     if (hr == E_NOINTERFACE) {
965         if (handle)
966             c.symbols->EndSymbolMatch(handle);
967         return rc;
968     }
969     if (FAILED(hr)) {
970         if (errorMessage)
971             *errorMessage= msgDebugEngineComFailed("StartSymbolMatch", hr);
972         return rc;
973     }
974     char buf[bufSize];
975     ULONG64 offset;
976     while (true) {
977         hr = c.symbols->GetNextSymbolMatch(handle, buf, bufSize - 1, 0, &offset);
978         if (hr == E_NOINTERFACE)
979             break;
980         if (hr == S_OK)
981             rc.push_back(Symbol(std::string(buf), offset));
982     }
983     c.symbols->EndSymbolMatch(handle);
984     return rc;
985 }
986 
getAllModuleNames(const SymbolGroupValueContext & c,std::string * errorMessage)987 std::list<std::string> SymbolGroupValue::getAllModuleNames(const SymbolGroupValueContext &c,
988                                                            std::string *errorMessage)
989 {
990     enum { bufSize = 2048 };
991 
992     std::list<std::string> rc;
993     ULONG moduleCount = 0;
994     ULONG unloaded = 0;
995 
996     HRESULT hr = c.symbols->GetNumberModules(&moduleCount, &unloaded);
997     if (FAILED(hr)) {
998         if (errorMessage)
999             *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr);
1000         return rc;
1001     }
1002     moduleCount += unloaded;
1003 
1004     // lookup module names
1005     char moduleBuffer[bufSize];
1006     for (ULONG index = 0; index < moduleCount; ++index) {
1007         hr = c.symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, index,
1008                                             NULL, moduleBuffer, bufSize - 1, NULL);
1009         if (FAILED(hr)) {
1010             if (errorMessage)
1011                 *errorMessage = msgDebugEngineComFailed("GetNumberModules", hr);
1012             return rc;
1013         }
1014         rc.push_back(moduleBuffer);
1015     }
1016     return rc;
1017 }
1018 
1019 // Resolve a type, that is, obtain its module name ('QString'->'QtCored4!QString')
resolveType(const std::string & typeIn,const SymbolGroupValueContext & ctx,const std::string & currentModule)1020 std::string SymbolGroupValue::resolveType(const std::string &typeIn,
1021                                           const SymbolGroupValueContext &ctx,
1022                                           const std::string &currentModule /* = "" */)
1023 {
1024     enum { BufSize = 512 };
1025 
1026     if (typeIn.empty() || typeIn.find('!') != std::string::npos)
1027         return typeIn;
1028 
1029     const std::string stripped = SymbolGroupValue::stripClassPrefixes(typeIn);
1030 
1031     // Use the module of the current symbol group for templates.
1032     // This is because resolving some template types (std::list<> has been
1033     // observed to result in 'QtGui4d!std::list', which subsequently fails.
1034     if (!currentModule.empty() && stripped.find('<') != std::string::npos) {
1035         std::string trc = currentModule;
1036         trc.push_back('!');
1037         trc += stripped;
1038         return trc;
1039     }
1040     // Obtain the base address of the module using an obscure ioctl() call.
1041     // See inline implementation of GetTypeSize() and docs.
1042     SYM_DUMP_PARAM symParameters = { sizeof (SYM_DUMP_PARAM), (PUCHAR)stripped.c_str(), DBG_DUMP_NO_PRINT, 0,
1043                                      NULL, NULL, NULL, 0, NULL };
1044     const ULONG typeSize = Ioctl(IG_GET_TYPE_SIZE, &symParameters, symParameters.size);
1045     if (!typeSize || !symParameters.ModBase) // Failed?
1046         return stripped;
1047     const std::string module = moduleNameByOffset(ctx.symbols, symParameters.ModBase);
1048     if (module.empty())
1049         return stripped;
1050     std::string rc = module;
1051     rc.push_back('!');
1052     rc += stripped;
1053     return rc;
1054 }
1055 
1056 // get the inner types: "QMap<int, double>" -> "int", "double"
innerTypesOf(const std::string & t)1057 std::vector<std::string> SymbolGroupValue::innerTypesOf(const std::string &t)
1058 {
1059     std::vector<std::string> rc;
1060 
1061     std::string::size_type pos = t.find('<');
1062     if (pos == std::string::npos)
1063         return rc;
1064 
1065     rc.reserve(5);
1066     const std::string::size_type size = t.size();
1067     // Record all elements of level 1 to work correctly for
1068     // 'std::map<std::basic_string< .. > >'
1069     unsigned level = 0;
1070     std::string::size_type start = 0;
1071     for ( ; pos < size ; pos++) {
1072         const char c = t.at(pos);
1073         switch (c) {
1074         case '<':
1075             if (++level == 1)
1076                 start = pos + 1;
1077             break;
1078         case '>':
1079             if (--level == 0) { // last element
1080                 std::string innerType = t.substr(start, pos - start);
1081                 trimFront(innerType);
1082                 trimBack(innerType);
1083                 rc.push_back(innerType);
1084                 return rc;
1085             }
1086             break;
1087         case ',':
1088             if (level == 1) { // std::map<a, b>: start anew at ','.
1089                 std::string innerType = t.substr(start, pos - start);
1090                 trimFront(innerType);
1091                 trimBack(innerType);
1092                 rc.push_back(innerType);
1093                 start = pos + 1;
1094             }
1095             break;
1096         }
1097     }
1098     return rc;
1099 }
1100 
operator <<(std::ostream & str,const SymbolGroupValue & v)1101 std::ostream &operator<<(std::ostream &str, const SymbolGroupValue &v)
1102 {
1103     if (v) {
1104         str << '\'' << v.name() << "' 0x" << std::hex << v.address() <<
1105                std::dec << ' ' << v.type() << ": '" << wStringToString(v.value()) << '\'';
1106     } else {
1107         str << "Invalid value '" << v.error() << '\'';
1108     }
1109     return str;
1110 }
1111 
1112 // -------------------- Simple dumping helpers
1113 
1114 // Courtesy of qdatetime.cpp
getDateFromJulianDay(unsigned julianDay,int * year,int * month,int * day)1115 static inline void getDateFromJulianDay(unsigned julianDay, int *year, int *month, int *day)
1116 {
1117     int y, m, d;
1118 
1119     if (julianDay >= 2299161) {
1120         typedef unsigned long long qulonglong;
1121         // Gregorian calendar starting from October 15, 1582
1122         // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
1123         qulonglong ell, n, i, j;
1124         ell = qulonglong(julianDay) + 68569;
1125         n = (4 * ell) / 146097;
1126         ell = ell - (146097 * n + 3) / 4;
1127         i = (4000 * (ell + 1)) / 1461001;
1128         ell = ell - (1461 * i) / 4 + 31;
1129         j = (80 * ell) / 2447;
1130         d = int(ell - (2447 * j) / 80);
1131         ell = j / 11;
1132         m = int(j + 2 - (12 * ell));
1133         y = int(100 * (n - 49) + i + ell);
1134     } else {
1135         // Julian calendar until October 4, 1582
1136         // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering
1137         julianDay += 32082;
1138         int dd = (4 * julianDay + 3) / 1461;
1139         int ee = julianDay - (1461 * dd) / 4;
1140         int mm = ((5 * ee) + 2) / 153;
1141         d = ee - (153 * mm + 2) / 5 + 1;
1142         m = mm + 3 - 12 * (mm / 10);
1143         y = dd - 4800 + (mm / 10);
1144         if (y <= 0)
1145             --y;
1146     }
1147     if (year)
1148         *year = y;
1149     if (month)
1150         *month = m;
1151     if (day)
1152         *day = d;
1153 }
1154 
1155 const char *stdStringTypeC = "std::basic_string<char,std::char_traits<char>,std::allocator<char> >";
1156 const char *stdWStringTypeC = "std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >";
1157 // Compiler option:  -Zc:wchar_t-:
1158 const char *stdWStringWCharTypeC = "std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >";
1159 
knownPODTypeHelper(const std::string & type,std::string::size_type endPos)1160 static KnownType knownPODTypeHelper(const std::string &type, std::string::size_type endPos)
1161 {
1162     if (type.empty() || !endPos)
1163         return KT_Unknown;
1164     // Strip pointer types.
1165     const bool isPointer = type.at(endPos - 1) == '*';
1166     if (isPointer) {
1167         endPos--;
1168         if (endPos > 0 && type.at(endPos - 1) == ' ')
1169             endPos--;
1170     }
1171     switch (type.at(0)) {
1172     case 'c':
1173         if (endPos == 4 && !type.compare(0, endPos, "char"))
1174             return isPointer ? KT_POD_PointerType : KT_Char;
1175         break;
1176     case 'd':
1177         if (endPos == 6 && !type.compare(0, endPos, "double"))
1178             return isPointer ? KT_POD_PointerType : KT_FloatType;
1179         break;
1180     case 'f':
1181         if (endPos == 5 && !type.compare(0, endPos, "float"))
1182             return isPointer ? KT_POD_PointerType : KT_FloatType;
1183         break;
1184     case 'l':
1185         if (endPos >= 4 && !type.compare(0, 4, "long"))
1186             if (endPos == 4 || type.at(4) == ' ')
1187                 return isPointer ? KT_POD_PointerType : KT_IntType;
1188         break;
1189     case 'i':
1190         // 'int' 'int64'
1191         if (endPos >= 3 && !type.compare(0, 3, "int"))
1192             if (endPos == 3 || type.at(3) == ' ' || type.at(3) == '6')
1193                 return isPointer ? KT_POD_PointerType : KT_IntType;
1194         break;
1195     case 's':
1196         if (endPos == 5 && !type.compare(0, 5, "short"))
1197             return isPointer ? KT_POD_PointerType : KT_IntType;
1198         if (endPos >= 6 && !type.compare(0, 6, "signed"))
1199             if (endPos == 6 || type.at(6) == ' ')
1200                 return isPointer ? KT_POD_PointerType : KT_IntType;
1201         break;
1202     case 'u':
1203         if (endPos >= 8 && !type.compare(0, 8, "unsigned")) {
1204             if (endPos == 8 || type.at(8) == ' ') {
1205                 if (isPointer)
1206                     return KT_POD_PointerType;
1207                 return type.compare(0, 13, "unsigned char") ?
1208                             KT_UnsignedIntType :
1209                             KT_UnsignedChar;
1210             }
1211         }
1212         break;
1213     }
1214     return isPointer ? KT_PointerType : KT_Unknown;
1215 }
1216 
1217 // Determine type starting from a position (with/without 'class '/'struct ' prefix).
knownClassTypeHelper(const std::string & type,std::string::size_type pos,std::string::size_type endPos)1218 static KnownType knownClassTypeHelper(const std::string &type,
1219                                       std::string::size_type pos,
1220                                       std::string::size_type endPos)
1221 {
1222     // STL ?
1223     const std::wstring::size_type templatePos = type.find('<', pos);
1224     static const std::wstring::size_type stlClassLen = 5;
1225     if (!type.compare(pos, stlClassLen, "std::")) {
1226         // STL containers
1227         const std::wstring::size_type hPos = pos + stlClassLen;
1228         if (templatePos != std::string::npos) {
1229             switch (templatePos - stlClassLen - pos) {
1230             case 3:
1231                 if (!type.compare(hPos, 3, "set"))
1232                     return KT_StdSet;
1233                 if (!type.compare(hPos, 3, "map"))
1234                     return KT_StdMap;
1235                 break;
1236             case 4:
1237                 if (!type.compare(hPos, 4, "list"))
1238                     return KT_StdList;
1239                 break;
1240             case 5:
1241                 if (!type.compare(hPos, 5, "stack"))
1242                     return KT_StdStack;
1243                 if (!type.compare(hPos, 5, "deque"))
1244                     return KT_StdDeque;
1245                 if (!type.compare(hPos, 5, "array"))
1246                     return KT_StdArray;
1247                 break;
1248             case 6:
1249                 if (!type.compare(hPos, 6, "vector"))
1250                     return KT_StdVector;
1251                 break;
1252             case 7:
1253                 if (!type.compare(hPos, 7, "complex"))
1254                     return KT_StdComplex;
1255                 break;
1256             case 8:
1257                 if (!type.compare(hPos, 8, "multimap"))
1258                     return KT_StdMultiMap;
1259                 if (!type.compare(hPos, 8, "multiset"))
1260                     return KT_StdMultiSet;
1261                 if (!type.compare(hPos, 8, "valarray"))
1262                     return KT_StdValArray;
1263                 break;
1264             case 13:
1265                 if (!type.compare(hPos, 13, "unordered_map"))
1266                     return KT_StdUnorderedMap;
1267                 if (!type.compare(hPos, 13, "unordered_set"))
1268                     return KT_StdUnorderedSet;
1269                 break;
1270             case 18:
1271                 if (!type.compare(hPos, 18, "unordered_multimap"))
1272                     return KT_StdUnorderedMultiMap;
1273                 if (!type.compare(hPos, 18, "unordered_multiset"))
1274                     return KT_StdUnorderedMultiSet;
1275                 break;
1276             }
1277         }
1278         // STL strings
1279         if (!type.compare(pos, endPos - pos, stdStringTypeC))
1280             return KT_StdString;
1281         if (!type.compare(pos, endPos - pos, stdWStringTypeC)
1282             || !type.compare(pos, endPos - pos, stdWStringWCharTypeC))
1283             return KT_StdWString;
1284         return KT_Unknown;
1285     } // std::sth
1286     // Check for a 'Q' past the last namespace (beware of namespaced Qt:
1287     // 'nsp::QString').
1288     const std::wstring::size_type lastNameSpacePos = type.rfind(':', templatePos);
1289     const std::wstring::size_type qPos =
1290             lastNameSpacePos == std::string::npos ? type.find('Q', pos) : lastNameSpacePos + 1;
1291     if (qPos == std::string::npos || qPos >= type.size() || type.at(qPos) != 'Q')
1292         return KT_Unknown;
1293     // Qt types (templates)
1294     if (templatePos != std::string::npos) {
1295         // Do not fall for QMap<K,T>::iterator, which is actually an inner class.
1296         if (endPos > templatePos && type.at(endPos - 1) != '>')
1297             return KT_Unknown;
1298         switch (templatePos - qPos) {
1299         case 4:
1300             if (!type.compare(qPos, 4, "QSet"))
1301                 return KT_QSet;
1302             if (!type.compare(qPos, 4, "QMap"))
1303                 return KT_QMap;
1304             break;
1305         case 5:
1306             if (!type.compare(qPos, 5, "QHash"))
1307                 return KT_QHash;
1308             if (!type.compare(qPos, 5, "QList"))
1309                 return KT_QList;
1310             break;
1311         case 6:
1312             if (!type.compare(qPos, 6, "QFlags"))
1313                 return KT_QFlags;
1314             if (!type.compare(qPos, 6, "QStack"))
1315                 return KT_QStack;
1316             if (!type.compare(qPos, 6, "QQueue"))
1317                 return KT_QQueue;
1318             break;
1319         case 7:
1320             if (!type.compare(qPos, 7, "QVector"))
1321                 return KT_QVector;
1322             break;
1323         case 9:
1324             if (!type.compare(qPos, 9, "QMultiMap"))
1325                 return KT_QMultiMap;
1326             break;
1327         case 10:
1328             if (!type.compare(qPos, 10, "QMultiHash"))
1329                 return KT_QMultiHash;
1330             break;
1331         case 11:
1332             if (!type.compare(qPos, 11, "QLinkedList"))
1333                 return KT_QLinkedList;
1334             break;
1335         case 12:
1336             if (!type.compare(qPos, 12, "QWeakPointer"))
1337                 return KT_QWeakPointer;
1338             break;
1339         case 14:
1340             if (!type.compare(qPos, 14, "QSharedPointer"))
1341                 return KT_QSharedPointer;
1342             break;
1343         }
1344     }
1345     // Remaining non-template types
1346     switch (endPos - qPos) {
1347     case 4:
1348         if (!type.compare(qPos, 4, "QPen"))
1349             return KT_QPen;
1350         if (!type.compare(qPos, 4, "QUrl"))
1351             return KT_QUrl;
1352         if (!type.compare(qPos, 4, "QDir"))
1353             return KT_QDir;
1354         break;
1355     case 5:
1356         if (!type.compare(qPos, 5, "QChar"))
1357             return KT_QChar;
1358         if (!type.compare(qPos, 5, "QDate"))
1359             return KT_QDate;
1360         if (!type.compare(qPos, 5, "QTime"))
1361             return KT_QTime;
1362         if (!type.compare(qPos, 5, "QSize"))
1363             return KT_QSize;
1364         if (!type.compare(qPos, 5, "QLine"))
1365             return KT_QLine;
1366         if (!type.compare(qPos, 5, "QRect"))
1367             return KT_QRect;
1368         if (!type.compare(qPos, 5, "QIcon"))
1369             return KT_QIcon;
1370         if (!type.compare(qPos, 5, "QFile"))
1371             return KT_QFile;
1372         break;
1373     case 6:
1374         if (!type.compare(qPos, 6, "QColor"))
1375             return KT_QColor;
1376         if (!type.compare(qPos, 6, "QSizeF"))
1377             return KT_QSizeF;
1378         if (!type.compare(qPos, 6, "QPoint"))
1379             return KT_QPoint;
1380         if (!type.compare(qPos, 6, "QLineF"))
1381             return KT_QLineF;
1382         if (!type.compare(qPos, 6, "QRectF"))
1383             return KT_QRectF;
1384         if (!type.compare(qPos, 6, "QBrush"))
1385             return KT_QBrush;
1386         if (!type.compare(qPos, 6, "QImage"))
1387             return KT_QImage;
1388         if (!type.compare(qPos, 6, "QFixed"))
1389             return KT_QFixed;
1390         break;
1391     case 7:
1392         if (!type.compare(qPos, 7, "QString"))
1393             return KT_QString;
1394         if (!type.compare(qPos, 7, "QPointF"))
1395             return KT_QPointF;
1396         if (!type.compare(qPos, 7, "QObject"))
1397             return KT_QObject;
1398         if (!type.compare(qPos, 7, "QWidget"))
1399             return KT_QWidget;
1400         if (!type.compare(qPos, 7, "QWindow"))
1401             return KT_QWindow;
1402         if (!type.compare(qPos, 7, "QLocale"))
1403             return KT_QLocale;
1404         if (!type.compare(qPos, 7, "QMatrix"))
1405             return KT_QMatrix;
1406         if (!type.compare(qPos, 7, "QRegExp"))
1407             return KT_QRegExp;
1408         if (!type.compare(qPos, 7, "QRegion"))
1409             return KT_QRegion;
1410         if (!type.compare(qPos, 7, "QPixmap"))
1411             return KT_QPixmap;
1412         break;
1413     case 8:
1414         if (!type.compare(qPos, 8, "QVariant"))
1415             return KT_QVariant;
1416         if (!type.compare(qPos, 8, "QMargins"))
1417             return KT_QMargins;
1418         if (!type.compare(qPos, 8, "QXmlItem"))
1419             return KT_QXmltem;
1420         if (!type.compare(qPos, 8, "QXmlName"))
1421             return KT_QXmlName;
1422         if (!type.compare(qPos, 8, "QProcess"))
1423             return KT_QProcess;
1424         break;
1425     case 9:
1426         if (!type.compare(qPos, 9, "QBitArray"))
1427             return KT_QBitArray;
1428         if (!type.compare(qPos, 9, "QDateTime"))
1429             return KT_QDateTime;
1430         if (!type.compare(qPos, 9, "QFileInfo"))
1431             return KT_QFileInfo;
1432         if (!type.compare(qPos, 9, "QMetaEnum"))
1433             return KT_QMetaEnum;
1434         if (!type.compare(qPos, 9, "QTextItem"))
1435             return KT_QTextItem;
1436         if (!type.compare(qPos, 9, "QTimeZone"))
1437             return KT_QTimeZone;
1438         if (!type.compare(qPos, 9, "QVector2D"))
1439             return KT_QVector2D;
1440         if (!type.compare(qPos, 9, "QVector3D"))
1441             return KT_QVector3D;
1442         if (!type.compare(qPos, 9, "QVector4D"))
1443             return KT_QVector4D;
1444         break;
1445     case 10:
1446         if (!type.compare(qPos, 10, "QAtomicInt"))
1447             return KT_QAtomicInt;
1448         if (!type.compare(qPos, 10, "QByteArray"))
1449             return KT_QByteArray;
1450         if (!type.compare(qPos, 10, "QMatrix4x4"))
1451             return KT_QMatrix4x4;
1452         if (!type.compare(qPos, 10, "QTextBlock"))
1453             return KT_QTextBlock;
1454         if (!type.compare(qPos, 10, "QTransform"))
1455             return KT_QTransform;
1456         if (!type.compare(qPos, 10, "QFixedSize"))
1457             return KT_QFixedSize;
1458         if (!type.compare(qPos, 10, "QStringRef"))
1459             return KT_QStringRef;
1460         break;
1461     case 11:
1462         if (!type.compare(qPos, 11, "QStringList"))
1463             return KT_QStringList;
1464         if (!type.compare(qPos, 11, "QBasicTimer"))
1465             return KT_QBasicTimer;
1466         if (!type.compare(qPos, 11, "QMetaMethod"))
1467             return KT_QMetaMethod;
1468         if (!type.compare(qPos, 11, "QModelIndex"))
1469             return KT_QModelIndex;
1470         if (!type.compare(qPos, 11, "QQuaternion"))
1471             return KT_QQuaternion;
1472         if (!type.compare(qPos, 11, "QScriptItem"))
1473             return KT_QScriptItem;
1474         if (!type.compare(qPos, 11, "QFixedPoint"))
1475             return KT_QFixedPoint;
1476         if (!type.compare(qPos, 11, "QScriptLine"))
1477             return KT_QScriptLine;
1478         if (!type.compare(qPos, 11, "QTextCursor"))
1479             return KT_QTextCursor;
1480         break;
1481     case 12:
1482         if (!type.compare(qPos, 12, "QKeySequence"))
1483             return KT_QKeySequence;
1484         if (!type.compare(qPos, 12, "QHostAddress"))
1485             return KT_QHostAddress;
1486         if (!type.compare(qPos, 12, "QIPv6Address"))
1487             return KT_QIPv6Address;
1488         if (!type.compare(qPos, 12, "QScriptValue"))
1489             return KT_QScriptValue;
1490         break;
1491     case 13:
1492         if (!type.compare(qPos, 13, "QTextFragment"))
1493             return KT_QTextFragment;
1494         if (!type.compare(qPos, 13, "QTreeViewItem"))
1495             return KT_QTreeViewItem;
1496         break;
1497     case 14:
1498         if (!type.compare(qPos, 14, "QMetaClassInfo"))
1499             return KT_QMetaClassInfo;
1500         if (!type.compare(qPos, 14, "QNetworkCookie"))
1501             return KT_QNetworkCookie;
1502         break;
1503     case 15:
1504         if (!type.compare(qPos, 15, "QBasicAtomicInt"))
1505             return KT_QBasicAtomicInt;
1506         if (!type.compare(qPos, 15, "QHashDummyValue"))
1507             return KT_QHashDummyValue;
1508         if (!type.compare(qPos, 15, "QSourceLocation"))
1509             return KT_QSourceLocation;
1510         if (!type.compare(qPos, 15, "QScriptAnalysis"))
1511             return KT_QScriptAnalysis;
1512         break;
1513     case 16:
1514         if (!type.compare(qPos, 16, "QTextUndoCommand"))
1515             return KT_QTextUndoCommand;
1516         break;
1517     case 18:
1518         if (!type.compare(qPos, 18, "QNetworkProxyQuery"))
1519             return KT_QNetworkProxyQuery;
1520         if (!type.compare(qPos, 18, "QXmlNodeModelIndex"))
1521             return KT_QXmlNodeModelIndex;
1522         break;
1523     case 19:
1524         if (!type.compare(qPos, 19, "QItemSelectionRange"))
1525             return KT_QItemSelectionRange;
1526         if (!type.compare(qPos, 19, "QPaintBufferCommand"))
1527             return KT_QPaintBufferCommand;
1528         if (!type.compare(qPos, 19, "QTextHtmlParserNode"))
1529             return KT_QTextHtmlParserNode;
1530         if (!type.compare(qPos, 19, "QXmlStreamAttribute"))
1531             return KT_QXmlStreamAttribute;
1532         if (!type.compare(qPos, 19, "QGlyphJustification"))
1533             return KT_QGlyphJustification;
1534         break;
1535     case 20:
1536         if (!type.compare(qPos, 20, "QTextBlock::iterator"))
1537             return KT_QTextBlock_iterator;
1538         if (!type.compare(qPos, 20, "QTextFrame::iterator"))
1539             return KT_QTextFrame_iterator;
1540         break;
1541     case 21:
1542         if (!type.compare(qPos, 21, "QPersistentModelIndex"))
1543             return KT_QPersistentModelIndex;
1544         if (!type.compare(qPos, 21, "QPainterPath::Element"))
1545             return KT_QPainterPath_Element;
1546         break;
1547     case 22:
1548         if (!type.compare(qPos, 22, "QObjectPrivate::Sender"))
1549             return KT_QObjectPrivate_Sender;
1550         break;
1551     case 24:
1552         if (!type.compare(qPos, 24, "QPatternist::AtomicValue"))
1553             return KT_QPatternist_AtomicValue;
1554         if (!type.compare(qPos, 24, "QPatternist::Cardinality"))
1555             return KT_QPatternist_Cardinality;
1556         break;
1557     case 26:
1558         if (!type.compare(qPos, 26, "QObjectPrivate::Connection"))
1559             return KT_QObjectPrivate_Connection;
1560         if (!type.compare(qPos, 26, "QPatternist::ItemCacheCell"))
1561             return KT_QPatternist_ItemCacheCell;
1562         if (!type.compare(qPos, 26, "QPatternist::ItemType::Ptr"))
1563             return KT_QPatternist_ItemType_Ptr;
1564         if (!type.compare(qPos, 26, "QPatternist::NamePool::Ptr"))
1565             return KT_QPatternist_NamePool_Ptr;
1566         break;
1567     case 27:
1568         if (!type.compare(qPos, 27, "QXmlStreamEntityDeclaration"))
1569             return KT_QXmlStreamEntityDeclaration;
1570         break;
1571     case 28:
1572         if (!type.compare(qPos, 28, "QPatternist::Expression::Ptr"))
1573             return KT_QPatternist_Expression_Ptr;
1574         break;
1575     case 29:
1576         if (!type.compare(qPos, 29, "QXmlStreamNotationDeclaration"))
1577             return KT_QXmlStreamNotationDeclaration;
1578         break;
1579     case 30:
1580         if (!type.compare(qPos, 30, "QPatternist::SequenceType::Ptr"))
1581             return KT_QPatternist_SequenceType_Ptr;
1582         if (!type.compare(qPos, 30, "QXmlStreamNamespaceDeclaration"))
1583             return KT_QXmlStreamNamespaceDeclaration;
1584         break;
1585     case 32:
1586         break;
1587         if (!type.compare(qPos, 32, "QPatternist::Item::Iterator::Ptr"))
1588             return KT_QPatternist_Item_Iterator_Ptr;
1589     case 34:
1590         break;
1591         if (!type.compare(qPos, 34, "QPatternist::ItemSequenceCacheCell"))
1592             return KT_QPatternist_ItemSequenceCacheCell;
1593     case 37:
1594         break;
1595         if (!type.compare(qPos, 37, "QNetworkHeadersPrivate::RawHeaderPair"))
1596             return KT_QNetworkHeadersPrivate_RawHeaderPair;
1597         if (!type.compare(qPos, 37, "QPatternist::AccelTree::BasicNodeData"))
1598             return KT_QPatternist_AccelTree_BasicNodeData;
1599         break;
1600     }
1601     return KT_Unknown;
1602 }
1603 
knownType(const std::string & type,unsigned flags)1604 KnownType knownType(const std::string &type, unsigned flags)
1605 {
1606     if (type.empty())
1607         return KT_Unknown;
1608     // Autostrip one pointer if desired
1609     const std::string::size_type endPos = (flags & KnownTypeAutoStripPointer) ?
1610                 type.size() - SymbolGroupValue::isPointerType(type) :
1611                 type.size();
1612 
1613     // PODs first
1614     const KnownType podType = knownPODTypeHelper(type, endPos);
1615     if (podType != KT_Unknown)
1616         return podType;
1617 
1618     if (flags & KnownTypeHasClassPrefix) {
1619         switch (type.at(0)) { // Check 'class X' or 'struct X'
1620         case 'c':
1621             if (!type.compare(0, 6, "class "))
1622                 return knownClassTypeHelper(type, 6, endPos);
1623             break;
1624         case 's':
1625             if (!type.compare(0, 7, "struct "))
1626                 return knownClassTypeHelper(type, 7, endPos);
1627             break;
1628         }
1629     } else {
1630         // No prefix, full check
1631         return knownClassTypeHelper(type, 0, endPos);
1632     }
1633     return KT_Unknown;
1634 }
1635 
formatKnownTypeFlags(std::ostream & os,KnownType kt)1636 void formatKnownTypeFlags(std::ostream &os, KnownType kt)
1637 {
1638     switch (kt) {
1639     case KT_Unknown:
1640         os << "<unknown>";
1641         return;
1642     case KT_POD_PointerType:
1643         os << " pod_pointer";
1644         break;
1645     case KT_PointerType:
1646         os << " pointer";
1647         break;
1648     default:
1649         break;
1650     }
1651     if (kt & KT_POD_Type)
1652         os << " pod";
1653     if (kt & KT_Qt_Type)
1654         os << " qt";
1655     if (kt & KT_Qt_PrimitiveType)
1656         os << " qt_primitive";
1657     if (kt & KT_Qt_MovableType)
1658         os << " qt_movable";
1659     if (kt & KT_ContainerType)
1660         os << " container";
1661     if (kt & KT_STL_Type)
1662         os << " stl";
1663     if (kt & KT_HasSimpleDumper)
1664         os << " simple_dumper";
1665 }
1666 
isMovable(const std::string & t,const SymbolGroupValue & v)1667 unsigned SymbolGroupValue::isMovable(const std::string &t, const SymbolGroupValue &v)
1668 {
1669     KnownType kt = knownType(t, false);
1670     if (kt & (KT_POD_Type | KT_Qt_MovableType | KT_Qt_PrimitiveType))
1671         return true;
1672     return kt == KT_QStringList && QtInfo::get(v.context()).version >= 5;
1673 }
1674 
1675 static inline DumpParameterRecodeResult
checkCharArrayRecode(const SymbolGroupValue & v)1676     checkCharArrayRecode(const SymbolGroupValue &v)
1677 {
1678     return DumpParameters::checkRecode(v.type(), std::string(),
1679                                        v.value(), v.context(), v.address());
1680 }
1681 
1682 // Helper struct containing data Address and size/alloc information
1683 // from Qt's QString/QByteArray.
1684 struct QtStringAddressData
1685 {
QtStringAddressDataQtStringAddressData1686     QtStringAddressData() : address(0), size(0), allocated(0) {}
1687 
1688     ULONG64 address;
1689     unsigned size; // Size and allocated are in ushort for QString's.
1690     unsigned allocated;
1691 };
1692 
1693 /* Helper to determine the location and size of the data of
1694  * QStrings/QByteArrays for versions 4,5. In Qt 4, 'd' has a 'data'
1695  * pointer. In Qt 5, the d-elements and the data are in a storage pool
1696  * and the data are at an offset behind the d-structures (QString,
1697  * QByteArray, QVector). */
readQtStringAddressData(const SymbolGroupValue & dV,const QtInfo & qtInfo)1698 QtStringAddressData readQtStringAddressData(const SymbolGroupValue &dV, const QtInfo &qtInfo)
1699 {
1700     QtStringAddressData result;
1701     const std::string &arrayData =
1702             qtInfo.prependModuleAndNameSpace("QArrayData", std::string(), qtInfo.nameSpace);
1703     const SymbolGroupValue adV = qtInfo.version < 5 ? dV : dV[arrayData.c_str()];
1704     if (!adV)
1705         return QtStringAddressData();
1706     const SymbolGroupValue sizeV = adV["size"];
1707     const SymbolGroupValue allocV = adV["alloc"];
1708     if (!sizeV || !allocV)
1709         return QtStringAddressData();
1710     result.size = sizeV.intValue();
1711     result.allocated = allocV.intValue();
1712     if (qtInfo.version < 5) {
1713         // Qt 4: Simple 'data' pointer.
1714         result.address = adV["data"].pointerValue();
1715     } else {
1716         // Qt 5: Memory pool after the data element.
1717         const SymbolGroupValue offsetV = adV["offset"];
1718         if (!offsetV)
1719             return QtStringAddressData();
1720         // Take the address for QTypeArrayData of QByteArray, else
1721         // pointer value of D-pointer.
1722         const ULONG64 baseAddress = SymbolGroupValue::isPointerType(adV.type()) ?
1723                                     adV.pointerValue() : adV.address();
1724         result.address = baseAddress + offsetV.intValue();
1725     }
1726     return result;
1727 }
1728 
1729 // Retrieve data from a QByteArrayData(char)/QStringData(wchar_t)
1730 // in desired type. For empty arrays, no data are allocated.
1731 // All sizes are in CharType units. zeroTerminated means data are 0-terminated
1732 // in the data type, but "size" does not contain it.
1733 template <typename CharType>
readQt5StringData(const SymbolGroupValue & dV,const QtInfo & qtInfo,bool zeroTerminated,unsigned position,unsigned sizeLimit,unsigned * fullSize,unsigned * arraySize,CharType ** array)1734 bool readQt5StringData(const SymbolGroupValue &dV, const QtInfo &qtInfo,
1735                        bool zeroTerminated, unsigned position, unsigned sizeLimit,
1736                        unsigned *fullSize, unsigned *arraySize,
1737                        CharType **array)
1738 {
1739     *array = 0;
1740     const QtStringAddressData data = readQtStringAddressData(dV, qtInfo);
1741     if (!data.address || position > data.size)
1742         return false;
1743     const ULONG64 address = data.address + sizeof(CharType) * position;
1744     *fullSize = data.size - position;
1745     *arraySize = std::min(*fullSize, sizeLimit);
1746     if (!*fullSize)
1747         return true;
1748     const unsigned memorySize =
1749             sizeof(CharType) * (*arraySize + (zeroTerminated ? 1 : 0));
1750     unsigned char *memory =
1751             SymbolGroupValue::readMemory(dV.context().dataspaces,
1752                                          address, memorySize);
1753     if (!memory)
1754         return false;
1755     *array = reinterpret_cast<CharType *>(memory);
1756     if ((*arraySize <= *fullSize) && zeroTerminated)
1757         *(*array + *arraySize) = CharType(0);
1758     return true;
1759 }
1760 
dumpQString(const SymbolGroupValue & v,std::wostream & str,MemoryHandle ** memoryHandle=0,unsigned position=0,unsigned length=std::numeric_limits<unsigned>::max ())1761 static inline bool dumpQString(const SymbolGroupValue &v, std::wostream &str,
1762                                MemoryHandle **memoryHandle = 0,
1763                                unsigned position = 0,
1764                                unsigned length = std::numeric_limits<unsigned>::max())
1765 {
1766     const QtInfo &qtInfo = QtInfo::get(v.context());
1767     const SymbolGroupValue dV = v["d"];
1768     if (!dV)
1769         return false;
1770     // Qt 4.
1771     if (qtInfo.version < 5) {
1772         if (const SymbolGroupValue sizeValue = dV["size"]) {
1773             const int size = sizeValue.intValue();
1774             if (size >= 0) {
1775                 std::wstring stringData = dV["data"].wcharPointerData(size);
1776                 if (position && position < stringData.size())
1777                     stringData.erase(0, position);
1778                 if (length < stringData.size())
1779                     stringData.erase(length, stringData.size() - length);
1780                 str << L'"' << stringData << L'"';
1781                 if (memoryHandle)
1782                     *memoryHandle = MemoryHandle::fromStdWString(stringData);
1783                 return true;
1784             }
1785         }
1786         return false;
1787 
1788     } // Qt 4.
1789 
1790     wchar_t *memory;
1791     unsigned fullSize;
1792     unsigned size;
1793     if (!readQt5StringData(dV, qtInfo, true, position,
1794                            std::min(length, ExtensionContext::instance().parameters().maxStringLength),
1795                            &fullSize, &size, &memory))
1796         return false;
1797     if (size) {
1798         str << L'"' << memory;
1799         if (std::min(fullSize, length) > size)
1800             str << L"...";
1801         str << L'"';
1802     } else {
1803         str << L"\"\"";
1804     }
1805     if (memoryHandle)
1806         *memoryHandle = new MemoryHandle(memory, size);
1807     else
1808         delete [] memory;
1809     return true;
1810 }
1811 
dumpQStringRef(const SymbolGroupValue & v,std::wostream & str,MemoryHandle ** memoryHandle=0)1812 static inline bool dumpQStringRef(const SymbolGroupValue &v, std::wostream &str,
1813                                   MemoryHandle **memoryHandle = 0)
1814 {
1815     const int position = v["m_position"].intValue();
1816     const int size = v["m_size"].intValue();
1817     if (position < 0 || size < 0)
1818         return false;
1819     const SymbolGroupValue string = v["m_string"];
1820     if (!string || !dumpQString(string, str, memoryHandle, position, size))
1821         return false;
1822     str << L" (" << position << ',' << size << L')';
1823     return true;
1824 }
1825 
1826 /* Pad a memory offset to align with pointer size */
padOffset(unsigned offset)1827 static inline unsigned padOffset(unsigned offset)
1828 {
1829     const unsigned ps = SymbolGroupValue::pointerSize();
1830     return (offset % ps) ? (1 + offset / ps) * ps : offset;
1831 }
1832 
1833 /* Return the offset to be accounted for "QSharedData" to access
1834  * the first member of a QSharedData-derived class */
qSharedDataSize(const SymbolGroupValueContext & ctx)1835 static unsigned qSharedDataSize(const SymbolGroupValueContext &ctx)
1836 {
1837     unsigned offset = 0;
1838     if (!offset) {
1839         // As of 4.X, a QAtomicInt, which will be padded to 8 on a 64bit system.
1840         const std::string qSharedData = QtInfo::get(ctx).prependQtCoreModule("QSharedData");
1841         offset = SymbolGroupValue::sizeOf(qSharedData.c_str());
1842     }
1843     return offset;
1844 }
1845 
1846 /* Return the size of a QString */
qStringSize(const SymbolGroupValueContext &)1847 static unsigned qStringSize(const SymbolGroupValueContext &/*ctx*/)
1848 {
1849 //    static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QString").c_str());
1850 // FIXME: Workaround the issue that GetTypeSize returns strange values;
1851     static const unsigned size = SymbolGroupValue::pointerSize();
1852     return size;
1853 }
1854 
1855 /* Return the size of a QList via QStringList. */
qListSize(const SymbolGroupValueContext & ctx)1856 static unsigned qListSize(const SymbolGroupValueContext &ctx)
1857 {
1858     static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QStringList").c_str());
1859     return size;
1860 }
1861 
1862 /* Return the size of a QByteArray */
qByteArraySize(const SymbolGroupValueContext &)1863 static unsigned qByteArraySize(const SymbolGroupValueContext &/*ctx*/)
1864 {
1865 //    static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QByteArray").c_str());
1866 // FIXME: Workaround the issue that GetTypeSize returns strange values;
1867     static const unsigned size = SymbolGroupValue::pointerSize();
1868     return size;
1869 }
1870 
1871 /* Return the size of a QAtomicInt */
qAtomicIntSize(const SymbolGroupValueContext & ctx)1872 static unsigned qAtomicIntSize(const SymbolGroupValueContext &ctx)
1873 {
1874     static const unsigned size = SymbolGroupValue::sizeOf(QtInfo::get(ctx).prependQtCoreModule("QAtomicInt").c_str());
1875     return size;
1876 }
1877 
1878 // Dump a QByteArray
dumpQByteArray(const SymbolGroupValue & v,std::wostream & str,std::string * encoding,MemoryHandle ** memoryHandle=0)1879 static inline bool dumpQByteArray(const SymbolGroupValue &v, std::wostream &str, std::string *encoding,
1880                                   MemoryHandle **memoryHandle = 0)
1881 {
1882     const QtInfo &qtInfo = QtInfo::get(v.context());
1883     const SymbolGroupValue dV = v["d"];
1884     if (!dV)
1885         return false;
1886     // Qt 4.
1887     if (qtInfo.version < 5) {
1888         if (const SymbolGroupValue data = dV["data"]) {
1889             const DumpParameterRecodeResult check =
1890                 checkCharArrayRecode(data);
1891             if (check.buffer) {
1892                 str << quotedWStringFromCharData(check.buffer, check.size);
1893                 delete [] check.buffer;
1894             } else {
1895                 str << data.value();
1896             }
1897             return true;
1898         }
1899         return false;
1900     }
1901 
1902     // Qt 5: Data start at offset past the 'd' of type QByteArrayData.
1903     if (encoding)
1904         *encoding = "latin1";
1905     wchar_t oldFill = str.fill(wchar_t('0'));
1906     str << std::hex;
1907     char *memory;
1908     unsigned fullSize;
1909     unsigned size;
1910     const unsigned &maxStringSize = ExtensionContext::instance().parameters().maxStringLength;
1911     if (!readQt5StringData(dV, qtInfo, false, 0, maxStringSize, &fullSize, &size, &memory))
1912         return false;
1913     if (size) {
1914         char *memEnd = memory + size;
1915         for (char *p = memory; p < memEnd; p++) {
1916             const unsigned char c = *p;
1917             str << std::setw(2) << c;
1918         }
1919         if (fullSize > size)
1920             str << L"2e2e2e"; // ...
1921     }
1922     if (memoryHandle)
1923         *memoryHandle = new MemoryHandle(reinterpret_cast<unsigned char *>(memory), size);
1924     else
1925         delete [] memory;
1926     str << std::dec;
1927     str.fill(oldFill);
1928     return true;
1929 }
1930 
1931 /* Below are some helpers for simple dumpers for some Qt classes accessing their
1932  * private classes without the debugger's symbolic information (applicable to non-exported
1933  * private classes such as QFileInfoPrivate, etc). This is done by dereferencing the
1934  * d-ptr and obtaining the address of the variable (considering some offsets depending on type)
1935  * and adding a symbol for that QString or QByteArray (basically using raw memory).
1936  */
1937 
1938 enum QPrivateDumpMode // Enumeration determining the offsets to be taken into consideration
1939 {
1940     QPDM_None,
1941     QPDM_qVirtual, // For classes with virtual functions (QObject-based): Skip vtable for d-address
1942     QPDM_qSharedData, // Private class is based on QSharedData, non-padded type
1943     QPDM_qSharedDataPadded // Private class is based on QSharedData, padded type (class)
1944 };
1945 
1946 // Determine the address of private class member by dereferencing the d-ptr and using offsets.
addressOfQPrivateMember(const SymbolGroupValue & v,QPrivateDumpMode mode,unsigned additionalOffset=0)1947 static ULONG64 addressOfQPrivateMember(const SymbolGroupValue &v, QPrivateDumpMode mode,
1948                                        unsigned additionalOffset = 0)
1949 {
1950     std::string errorMessage;
1951     // Dererence d-Ptr (Pointer/Value duality: Value or object address).
1952     ULONG64 dAddress = SymbolGroupValue::isPointerType(v.type()) ? v.pointerValue() : v.address();
1953     if (!dAddress)
1954         return 0;
1955     if (mode == QPDM_qVirtual) // Skip vtable.
1956         dAddress += SymbolGroupValue::pointerSize();
1957     const ULONG64 dptr = SymbolGroupValue::readPointerValue(v.context().dataspaces,
1958                                                             dAddress, &errorMessage);
1959     if (!dptr)
1960         return 0;
1961     // Get address of type to be dumped.
1962     ULONG64 dumpAddress = dptr  + additionalOffset;
1963     if (mode == QPDM_qSharedData) { // Simple type following QSharedData
1964         dumpAddress += qSharedDataSize(v.context());
1965     } else if (mode == QPDM_qSharedDataPadded) {
1966         dumpAddress += padOffset(qSharedDataSize(v.context()));
1967     }
1968     return dumpAddress;
1969 }
1970 
1971 // Convenience to dump a QString from the unexported private class of a Qt class.
dumpQStringFromQPrivateClass(const SymbolGroupValue & v,QPrivateDumpMode mode,unsigned additionalOffset,std::wostream & str)1972 static bool dumpQStringFromQPrivateClass(const SymbolGroupValue &v,
1973                                          QPrivateDumpMode mode,
1974                                          unsigned additionalOffset,
1975                                          std::wostream &str)
1976 {
1977     std::string errorMessage;
1978     const ULONG64 stringAddress = addressOfQPrivateMember(v, mode, additionalOffset);
1979     if (!stringAddress)
1980         return false;
1981     std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QString");
1982     std::string symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType);
1983     if (SymbolGroupValue::verbose > 1)
1984         DebugPrint() <<  "dumpQStringFromQPrivateClass of " << v.name() << '/'
1985                      << v.type() << " mode=" << mode
1986                      << " offset=" << additionalOffset << " address=0x" << std::hex << stringAddress
1987                      << std::dec << " expr=" << symbolName;
1988     SymbolGroupNode *stringNode =
1989             v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
1990     if (!stringNode && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) {
1991         // HACK:
1992         // In some rare cases the AddSymbol can't create a node with a given module name,
1993         // but is able to add the symbol without any modulename.
1994         dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QString", "", QtInfo::get(v.context()).nameSpace);
1995         symbolName = SymbolGroupValue::pointedToSymbolName(stringAddress , dumpType);
1996         stringNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
1997         if (!stringNode)
1998             return false;
1999     }
2000     return dumpQString(SymbolGroupValue(stringNode, v.context()), str);
2001 }
2002 
2003 // Convenience to dump a QByteArray from the unexported private class of a Qt class.
dumpQByteArrayFromQPrivateClass(const SymbolGroupValue & v,QPrivateDumpMode mode,unsigned additionalOffset,std::wostream & str,std::string * encoding)2004 static bool dumpQByteArrayFromQPrivateClass(const SymbolGroupValue &v,
2005                                             QPrivateDumpMode mode,
2006                                             unsigned additionalOffset,
2007                                             std::wostream &str,
2008                                             std::string *encoding)
2009 {
2010     std::string errorMessage;
2011     const ULONG64 byteArrayAddress = addressOfQPrivateMember(v, mode, additionalOffset);
2012     if (!byteArrayAddress)
2013         return false;
2014     std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QByteArray");
2015     std::string symbolName = SymbolGroupValue::pointedToSymbolName(byteArrayAddress , dumpType);
2016     if (SymbolGroupValue::verbose > 1)
2017         DebugPrint() <<  "dumpQByteArrayFromQPrivateClass of " << v.name() << '/'
2018                      << v.type() << " mode=" << mode
2019                      << " offset=" << additionalOffset << " address=0x" << std::hex << byteArrayAddress
2020                      << std::dec << " expr=" << symbolName;
2021     SymbolGroupNode *byteArrayNode =
2022             v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
2023     if (!byteArrayNode && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) {
2024         // HACK:
2025         // In some rare cases the AddSymbol can't create a node with a given module name,
2026         // but is able to add the symbol without any modulename.
2027         dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QByteArray", "", QtInfo::get(v.context()).nameSpace);
2028         symbolName = SymbolGroupValue::pointedToSymbolName(byteArrayAddress , dumpType);
2029         byteArrayNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
2030         if (!byteArrayNode)
2031             return false;
2032     }
2033     return dumpQByteArray(SymbolGroupValue(byteArrayNode, v.context()), str, encoding);
2034 }
2035 
2036 /* Dump QFileInfo, for whose private class no debugging information is available.
2037  * Dump 2nd string past its QSharedData base class. */
dumpQFileInfo(const SymbolGroupValue & v,std::wostream & str)2038 static inline bool dumpQFileInfo(const SymbolGroupValue &v, std::wostream &str)
2039 {
2040     return dumpQStringFromQPrivateClass(v, QPDM_qSharedDataPadded, 0,  str);
2041 }
2042 
2043 /* Dump QDir, for whose private class no debugging information is available.
2044  * Dump 1st string past its QSharedData base class. */
dumpQDir(const SymbolGroupValue & v,std::wostream & str)2045 static bool inline dumpQDir(const SymbolGroupValue &v, std::wostream &str)
2046 {
2047     const unsigned offset =
2048             v.fieldOffset(QtInfo::get(v.context()).prependQtCoreModule("QDirPrivate").c_str(),
2049                           "dirEntry.m_filePath");
2050     return dumpQStringFromQPrivateClass(v, QPDM_None, offset,  str);
2051 }
2052 
2053 /* Dump QRegExp, for whose private class no debugging information is available.
2054  * Dump 1st string past of its base class. */
dumpQRegExp(const SymbolGroupValue & v,std::wostream & str)2055 static inline bool dumpQRegExp(const SymbolGroupValue &v, std::wostream &str)
2056 {
2057     return dumpQStringFromQPrivateClass(v, QPDM_qSharedDataPadded, 0,  str);
2058 }
2059 
dumpQRegion(const SymbolGroupValue & v,std::wostream & str,void ** specialInfo)2060 static inline bool dumpQRegion(const SymbolGroupValue &v, std::wostream &str, void **specialInfo)
2061 {
2062     const QtInfo info = QtInfo::get(v.context());
2063     SymbolGroupValue d = v["d"]["qt_rgn"];
2064     std::ostringstream namestr;
2065     namestr << "(" << info.prependQtGuiModule("QRegionPrivate *") << ")("
2066             << std::showbase << std::hex << d.pointerValue() << ')';
2067 
2068     std::string tmp;
2069     SymbolGroupNode *qRegionPrivateNode
2070             = v.node()->symbolGroup()->addSymbol(v.module(), namestr.str(), std::string(), &tmp);
2071     if (!qRegionPrivateNode)
2072         return false;
2073 
2074     const SymbolGroupValue qRegionPrivateValue = SymbolGroupValue(qRegionPrivateNode, v.context());
2075     if (!qRegionPrivateValue)
2076         return false;
2077 
2078     const int size = containerSize(KT_QVector, qRegionPrivateValue["rects"]);
2079     if (size == -1)
2080         return false;
2081 
2082     str << L'<' << size << L" items>";
2083     if (specialInfo)
2084         *specialInfo = qRegionPrivateNode;
2085     return true;
2086 }
2087 
2088 /* Dump QFile, for whose private class no debugging information is available.
2089  * Dump the 1st string first past its QIODevicePrivate base class. */
dumpQFile(const SymbolGroupValue & v,std::wostream & str)2090 static inline bool dumpQFile(const SymbolGroupValue &v, std::wostream &str)
2091 {
2092     // Get address of the file name string, obtain value by dumping a QString at address
2093     static unsigned qFileBasePrivateSize = 0;
2094     if (!qFileBasePrivateSize) {
2095         const QtInfo info = QtInfo::get(v.context());
2096         const std::string qIoDevicePrivateType =info.prependQtCoreModule(
2097                     info.version < 5 ? "QIODevicePrivate" : "QFileDevicePrivate");
2098         qFileBasePrivateSize = padOffset(SymbolGroupValue::sizeOf(qIoDevicePrivateType.c_str()));
2099     }
2100     if (!qFileBasePrivateSize)
2101         return false;
2102     return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, qFileBasePrivateSize,  str);
2103 }
2104 
dumpQIPv6Address(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2105 static inline bool dumpQIPv6Address(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2106 {
2107     unsigned char *p = SymbolGroupValue::readMemory( v.context().dataspaces, v["c"].address(), 16);
2108     if (!p || !encoding)
2109         return false;
2110 
2111     const wchar_t oldFill = str.fill('0');
2112     str << std::hex;
2113     for (unsigned char *it = p; it < p + 16; ++it) {
2114         if ((p - it) % 2 == 0 && it != p)
2115             str << ':';
2116         str << std::setw(2) << *it;
2117     }
2118     str << std::dec;
2119     str.fill(oldFill);
2120     *encoding = "ipv6addressandhexscopeid";
2121     return true;
2122 }
2123 /* Dump QHostAddress, for whose private class no debugging information is available.
2124  * Dump string 'ipString' past of its private class. Does not currently work? */
dumpQHostAddress(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2125 static inline bool dumpQHostAddress(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2126 {
2127     // Determine offset in private struct: qIPv6AddressType (array, unaligned) +  uint32 + enum.
2128     const QtInfo info = QtInfo::get(v.context());
2129     SymbolGroupValue d = v["d"]["d"];
2130     std::ostringstream namestr;
2131     namestr << '(' << info.prependQtNetworkModule("QHostAddressPrivate *") << ")("
2132             << std::showbase << std::hex << d.pointerValue() << ')';
2133     std::string tmp;
2134     SymbolGroupNode *qHostAddressPrivateNode
2135             = v.node()->symbolGroup()->addSymbol(v.module(), namestr.str(), std::string(), &tmp);
2136     if (!qHostAddressPrivateNode)
2137         return false;
2138     const SymbolGroupValue qHostAddressPrivateValue = SymbolGroupValue(qHostAddressPrivateNode, v.context());
2139     const bool parsed = readPODFromMemory<bool>(qHostAddressPrivateValue.context().dataspaces,
2140                                                 qHostAddressPrivateValue["isParsed"].address(),
2141                                                 sizeof(bool), false, &tmp);
2142     if (parsed) {
2143         const int protocol = qHostAddressPrivateValue["protocol"].intValue(-1);
2144         if (protocol < 0) {
2145             str << L"Uninitialized/Unknown protocol";
2146             return true;
2147         }
2148         if (protocol == 1) {
2149             if (dumpQIPv6Address(qHostAddressPrivateValue["a6"], str, encoding))
2150                 return true;
2151             str << L"IPv6 protocol";
2152             return true;
2153         }
2154         DebugPrint() << v.name().c_str() << ": " <<  parsed;
2155         const SymbolGroupValue a = qHostAddressPrivateValue["a"];
2156         const unsigned int address = static_cast<unsigned int>(SymbolGroupValue::readIntValue(v.context().dataspaces, a.address()));
2157         str << (address >> 24) << '.'
2158             << (address << 8 >> 24) << '.'
2159             << (address << 16 >> 24) << '.'
2160             << (address << 24 >> 24);
2161         return true;
2162     }
2163     unsigned offset = 0;
2164     if (info.version < 5) {
2165         static unsigned qIPv6AddressSize = 0;
2166         if (!qIPv6AddressSize) {
2167             const std::string qIPv6AddressType = QtInfo::get(v.context()).prependQtNetworkModule("QIPv6Address");
2168             qIPv6AddressSize = SymbolGroupValue::sizeOf(qIPv6AddressType.c_str());
2169         }
2170         if (!qIPv6AddressSize)
2171             return false;
2172         offset = padOffset(8 + qIPv6AddressSize);
2173     }
2174     return dumpQStringFromQPrivateClass(v, QPDM_None, offset, str);
2175 }
2176 
2177 /* Dump QProcess, for whose private class no debugging information is available.
2178  * Dump string 'program' string with empirical offset. */
dumpQProcess(const SymbolGroupValue & v,std::wostream & str)2179 static inline bool dumpQProcess(const SymbolGroupValue &v, std::wostream &str)
2180 {
2181     const unsigned offset = SymbolGroupValue::pointerSize() == 8 ? 424 : 260;
2182     return dumpQStringFromQPrivateClass(v, QPDM_qVirtual, offset,  str);
2183 }
2184 
2185 /* Dump QScriptValue, for whose private class no debugging information is available.
2186  * Private class has a pointer to engine, type enumeration and a JSC:JValue and double/QString
2187  * for respective types. */
dumpQScriptValue(const SymbolGroupValue & v,std::wostream & str)2188 static inline bool dumpQScriptValue(const SymbolGroupValue &v, std::wostream &str)
2189 {
2190     std::string errorMessage;
2191     // Read out type
2192     const ULONG64 privateAddress = addressOfQPrivateMember(v, QPDM_None, 0);
2193     if (!privateAddress) { // Can actually be 0 for default-constructed
2194         str << L"<Invalid>";
2195         return true;
2196     }
2197     const unsigned ps = SymbolGroupValue::pointerSize();
2198     // Offsets of QScriptValuePrivate
2199     const unsigned jscScriptValueSize = 8; // Union of double and rest.
2200     const unsigned doubleValueOffset = 2 * ps + jscScriptValueSize;
2201     const unsigned stringValueOffset = doubleValueOffset + sizeof(double);
2202     const ULONG64 type =
2203         SymbolGroupValue::readUnsignedValue(v.context().dataspaces,
2204                                             privateAddress + ps, 4, 0, &errorMessage);
2205     switch (type) {
2206     case 1:
2207         str << SymbolGroupValue::readDouble(v.context().dataspaces, privateAddress + doubleValueOffset);
2208         break;
2209     case 2:
2210         return dumpQStringFromQPrivateClass(v, QPDM_None, stringValueOffset, str);
2211     default:
2212         str << L"<JavaScriptCore>";
2213         break;
2214     }
2215     return true;
2216 }
2217 
2218 /* Dump QUrl for whose private class no debugging information is available.
2219  * Dump the 'originally encoded' byte array of its private class. */
dumpQUrl(const SymbolGroupValue & v,std::wostream & str)2220 static inline bool dumpQUrl(const SymbolGroupValue &v, std::wostream &str)
2221 {
2222     // Get address of the original-encoded byte array, obtain value by dumping at address
2223     if (QtInfo::get(v.context()).version < 5) {
2224         const ULONG offset = padOffset(qAtomicIntSize(v.context()))
2225                          + 6 * qStringSize(v.context()) + qByteArraySize(v.context());
2226         return dumpQByteArrayFromQPrivateClass(v, QPDM_None, offset, str, 0);
2227     }
2228     ULONG offset = qAtomicIntSize(v.context()) +
2229                    SymbolGroupValue::intSize();
2230     const unsigned stringSize = qStringSize(v.context());
2231     str << L"Scheme: ";
2232     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2233         return false;
2234     offset += stringSize;
2235     str << L" User: ";
2236     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2237         return false;
2238     offset += stringSize;
2239     str << L" Password: ";
2240     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2241         return false;
2242     offset += stringSize;
2243     str << L" Host: ";
2244     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2245         return false;
2246     offset += stringSize;
2247     str << L" Path: ";
2248     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2249         return false;
2250     offset += stringSize;
2251     str << L" Query: ";
2252     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2253         return false;
2254     offset += stringSize;
2255     str << L" Fragment: ";
2256     if (!dumpQStringFromQPrivateClass(v, QPDM_None, offset, str))
2257         return false;
2258     return true;
2259 }
2260 
2261 // Dump QColor
dumpQColor(const SymbolGroupValue & v,std::wostream & str)2262 static bool dumpQColor(const SymbolGroupValue &v, std::wostream &str)
2263 {
2264     const SymbolGroupValue specV = v["cspec"];
2265     if (!specV)
2266         return false;
2267     const int spec = specV.intValue();
2268     if (spec == 0) {
2269         str << L"<Invalid color>";
2270         return true;
2271     }
2272     if (spec < 1 || spec > 4)
2273         return false;
2274     const SymbolGroupValue arrayV = v["ct"]["array"];
2275     if (!arrayV)
2276         return false;
2277     const int a0 = arrayV["0"].intValue();
2278     const int a1 = arrayV["1"].intValue();
2279     const int a2 = arrayV["2"].intValue();
2280     const int a3 = arrayV["3"].intValue();
2281     const int a4 = arrayV["4"].intValue();
2282     if (a0 < 0 || a1 < 0 || a2 < 0 || a3 < 0 || a4 < 0)
2283         return false;
2284     switch (spec) {
2285     case 1: // Rgb
2286         str << L"RGB alpha=" << (a0 / 0x101) << L", red=" << (a1 / 0x101)
2287             << L", green=" << (a2 / 0x101) << ", blue=" << (a3 / 0x101);
2288         break;
2289     case 2: // Hsv
2290         str << L"HSV alpha=" << (a0 / 0x101) << L", hue=" << (a1 / 100)
2291             << L", sat=" << (a2 / 0x101) << ", value=" << (a3 / 0x101);
2292         break;
2293     case 3: // Cmyk
2294         str << L"CMYK alpha=" << (a0 / 0x101) << L", cyan=" << (a1 / 100)
2295             << L", magenta=" << (a2 / 0x101) << ", yellow=" << (a3 / 0x101)
2296             << ", black=" << (a4 / 0x101);
2297         break;
2298     case 4: // Hsl
2299         str << L"HSL alpha=" << (a0 / 0x101) << L", hue=" << (a1 / 100)
2300             << L", sat=" << (a2 / 0x101) << ", lightness=" << (a3 / 0x101);
2301         break;
2302     }
2303     return true;
2304 }
2305 
2306 // Dump Qt's core types
2307 
dumpQBasicAtomicInt(const SymbolGroupValue & v,std::wostream & str)2308 static inline bool dumpQBasicAtomicInt(const SymbolGroupValue &v, std::wostream &str)
2309 {
2310     if (const SymbolGroupValue iValue = v["_q_value"]) {
2311         str <<  iValue.value();
2312         return true;
2313     }
2314     return false;
2315 }
2316 
dumpQAtomicInt(const SymbolGroupValue & v,std::wostream & str)2317 static inline bool dumpQAtomicInt(const SymbolGroupValue &v, std::wostream &str)
2318 {
2319     if (const SymbolGroupValue base = v[unsigned(0)])
2320         return dumpQBasicAtomicInt(base, str);
2321     return false;
2322 }
2323 
dumpQChar(const SymbolGroupValue & v,std::wostream & str)2324 static bool dumpQChar(const SymbolGroupValue &v, std::wostream &str)
2325 {
2326     if (SymbolGroupValue cValue = v["ucs"]) {
2327         const int utf16 = cValue.intValue();
2328         if (utf16 >= 0) {
2329             // Print code = character,
2330             // exclude control characters and Pair indicator
2331             if (utf16 >= 32 && (utf16 < 0xD800 || utf16 > 0xDBFF))
2332                 str << '\'' << wchar_t(utf16) << "' ";
2333             str << '(' << utf16 << ')';
2334         }
2335         return true;
2336     }
2337     return false;
2338 }
2339 
dumpQFlags(const SymbolGroupValue & v,std::wostream & str)2340 static inline bool dumpQFlags(const SymbolGroupValue &v, std::wostream &str)
2341 {
2342     if (SymbolGroupValue iV = v["i"]) {
2343         const int i = iV.intValue();
2344         if (i >= 0) {
2345             str << i;
2346             return true;
2347         }
2348     }
2349     return false;
2350 }
2351 
dumpQDate(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2352 static bool dumpQDate(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2353 {
2354     if (const SymbolGroupValue julianDayV = v["jd"]) {
2355         if (julianDayV.intValue() > 0) {
2356             str << julianDayV.intValue();
2357             if (encoding)
2358                 *encoding = "juliandate";
2359         } else {
2360             str << L"(invalid)";
2361         }
2362         return true;
2363     }
2364     return false;
2365 }
2366 
dumpQTime(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2367 static bool dumpQTime(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2368 {
2369     if (const SymbolGroupValue milliSecsV = v["mds"]) {
2370         const int milliSecs = milliSecsV.intValue();
2371         str << milliSecs;
2372         if (encoding)
2373             *encoding = "millisecondssincemidnight";
2374         return true;
2375     }
2376     return false;
2377 }
2378 
dumpQTimeZone(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2379 static bool dumpQTimeZone(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2380 {
2381     if (!dumpQByteArrayFromQPrivateClass(v, QPDM_qSharedDataPadded, SymbolGroupValue::pointerSize(), str, encoding))
2382         str << L"(null)";
2383     return true;
2384 }
2385 
2386 // Convenience to dump a QTimeZone from the unexported private class of a Qt class.
dumpQTimeZoneFromQPrivateClass(const SymbolGroupValue & v,QPrivateDumpMode mode,unsigned additionalOffset,std::wostream & str,std::string * encoding)2387 static bool dumpQTimeZoneFromQPrivateClass(const SymbolGroupValue &v,
2388                                            QPrivateDumpMode mode,
2389                                            unsigned additionalOffset,
2390                                            std::wostream &str,
2391                                            std::string *encoding)
2392 {
2393     std::string errorMessage;
2394     const ULONG64 timeZoneAddress = addressOfQPrivateMember(v, mode, additionalOffset);
2395     if (!timeZoneAddress)
2396         return false;
2397     std::string dumpType = QtInfo::get(v.context()).prependQtCoreModule("QTimeZone");
2398     std::string symbolName = SymbolGroupValue::pointedToSymbolName(timeZoneAddress , dumpType);
2399     if (SymbolGroupValue::verbose > 1)
2400         DebugPrint() <<  "dumpQTimeZoneFromQPrivateClass of " << v.name() << '/'
2401                      << v.type() << " mode=" << mode
2402                      << " offset=" << additionalOffset << " address=0x" << std::hex << timeZoneAddress
2403                      << std::dec << " expr=" << symbolName;
2404     SymbolGroupNode *timeZoneNode =
2405             v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
2406     if (!timeZoneNode && errorMessage.find("DEBUG_ANY_ID") != std::string::npos) {
2407         // HACK:
2408         // In some rare cases the AddSymbol can't create a node with a given module name,
2409         // but is able to add the symbol without any modulename.
2410         dumpType = QtInfo::get(v.context()).prependModuleAndNameSpace("QTimeZone", "", QtInfo::get(v.context()).nameSpace);
2411         symbolName = SymbolGroupValue::pointedToSymbolName(timeZoneAddress , dumpType);
2412         timeZoneNode = v.node()->symbolGroup()->addSymbol(v.module(), symbolName, std::string(), &errorMessage);
2413         if (!timeZoneNode)
2414             return false;
2415     }
2416     return dumpQTimeZone(SymbolGroupValue(timeZoneNode, v.context()), str, encoding);
2417 }
2418 
2419 // QDateTime has an unexported private class. Obtain date and time
2420 // from memory.
dumpQDateTime(const SymbolGroupValue & v,std::wostream & str,std::string * encoding)2421 static bool dumpQDateTime(const SymbolGroupValue &v, std::wostream &str, std::string *encoding)
2422 {
2423     // QDate is 64bit starting from Qt 5 which is always aligned 64bit.
2424     if (QtInfo::get(v.context()).version == 5) {
2425         // the dumper on the creator side expects msecs/spec/offset/tz/status/type info version
2426         const char separator = '/';
2427         LONG64 msecs = 0;
2428         int spec = 0;
2429         int offset  = 0;
2430         std::wstring timeZoneString;
2431         int status = 0;
2432         int tiVersion = QtInfo::qtTypeInfoVersion(v.context());
2433         if (tiVersion > 10) {
2434             const ULONG64 address =
2435                     SymbolGroupValue::isPointerType(v.type()) ? v.pointerValue() : v.address();
2436             const ULONG64 data = SymbolGroupValue::readUnsignedValue(
2437                         v.context().dataspaces, address, 8, 0);
2438             status = data & 0xFF;
2439             ULONG64 timeZone = 0;
2440             if (status & 0x01) {
2441                 msecs = data >> 8;
2442                 spec = (status & 0x30) >> 4;
2443             } else {
2444                 ULONG64 addr = SymbolGroupValue::readPointerValue(v.context().dataspaces, address);
2445                 msecs = SymbolGroupValue::readSignedValue(v.context().dataspaces, addr, 8, 0);
2446 
2447                 addr += 8 /*int64*/;
2448                 status = SymbolGroupValue::readIntValue(v.context().dataspaces, addr);
2449 
2450                 addr += SymbolGroupValue::intSize();
2451                 offset = SymbolGroupValue::readIntValue(v.context().dataspaces, addr);
2452 
2453                 addr += 2 * SymbolGroupValue::intSize();
2454                 timeZone = SymbolGroupValue::readPointerValue(v.context().dataspaces, addr);
2455             }
2456             timeZoneString = std::to_wstring(timeZone);
2457         } else {
2458             const ULONG64 msecsAddr = addressOfQPrivateMember(v, QPDM_None, 0);
2459             if (!msecsAddr)
2460                 return false;
2461 
2462             int addrOffset = 8 /*QSharedData + padded*/;
2463             msecs = SymbolGroupValue::readSignedValue(
2464                         v.context().dataspaces, msecsAddr + addrOffset, 8, 0);
2465 
2466             addrOffset += 8 /*int64*/;
2467             spec = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset);
2468 
2469             addrOffset += SymbolGroupValue::sizeOf("Qt::TimeSpec");
2470             offset = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset);
2471 
2472             addrOffset += SymbolGroupValue::intSize();
2473             std::wostringstream timeZoneStream;
2474             dumpQTimeZoneFromQPrivateClass(v, QPDM_None, addrOffset, timeZoneStream, 0);
2475             timeZoneString = timeZoneStream.str();
2476 
2477             addrOffset += SymbolGroupValue::sizeOf("QTimeZone");
2478             status = SymbolGroupValue::readIntValue(v.context().dataspaces, msecsAddr + addrOffset);
2479         }
2480         enum StatusFlag {
2481             ValidDate = 0x04,
2482             ValidTime = 0x08,
2483             ValidDateTime = 0x10
2484         };
2485         if (!(status & ValidDateTime || ((status & ValidDate) && (status & ValidTime)))) {
2486             str << L"(invalid)";
2487             return true;
2488         }
2489         str << msecs << separator
2490             << spec << separator
2491             << offset << separator
2492             << timeZoneString << separator
2493             << status << separator
2494             << tiVersion;
2495 
2496         if (encoding)
2497             *encoding = "datetimeinternal";
2498 
2499         return  true;
2500     }
2501 
2502     const ULONG64 dateAddr = addressOfQPrivateMember(v, QPDM_qSharedData, 0);
2503     if (!dateAddr)
2504         return false;
2505     const int date =
2506         SymbolGroupValue::readIntValue(v.context().dataspaces,
2507                                        dateAddr, SymbolGroupValue::intSize(), 0);
2508     if (!date) {
2509         str << L"<null>";
2510         return true;
2511     }
2512     const ULONG64 timeAddr = dateAddr + SymbolGroupValue::intSize();
2513     const int time =
2514         SymbolGroupValue::readIntValue(v.context().dataspaces,
2515                                        timeAddr, SymbolGroupValue::intSize(), 0);
2516     str << date << '/' << time;
2517     if (encoding)
2518         *encoding = "juliandateandmillisecondssincemidnight";
2519     return true;
2520 }
2521 
dumpQPixmap(const SymbolGroupValue & v,std::wostream & str)2522 static bool dumpQPixmap(const SymbolGroupValue &v, std::wostream &str)
2523 {
2524     const SymbolGroupValue pixmapSharedData = v["data"]["d"];
2525     if (!pixmapSharedData.isValid())
2526         return false;
2527     ULONG64 addr = pixmapSharedData.pointerValue();
2528 
2529     if (addr) {
2530         const unsigned int width =
2531                 SymbolGroupValue::readIntValue(v.context().dataspaces,
2532                                                addr += SymbolGroupValue::pointerSize(),
2533                                                SymbolGroupValue::intSize(), 0);
2534         const unsigned int height =
2535                 SymbolGroupValue::readIntValue(v.context().dataspaces,
2536                                                addr += SymbolGroupValue::intSize(),
2537                                                SymbolGroupValue::intSize(), 0);
2538         const unsigned int depth =
2539                 SymbolGroupValue::readIntValue(v.context().dataspaces,
2540                                                addr += SymbolGroupValue::intSize(),
2541                                                SymbolGroupValue::intSize(), 0);
2542 
2543         if (width && height) {
2544             str << width << L'x' << height << L", depth: " << depth;
2545             return true;
2546         }
2547     }
2548     str << L"<null>";
2549     return true;
2550 }
2551 
dumpQImage(const SymbolGroupValue & v,std::wostream & str,MemoryHandle ** memoryHandle)2552 static bool dumpQImage(const SymbolGroupValue &v, std::wostream &str, MemoryHandle **memoryHandle)
2553 {
2554     struct CreatorImageHeader { // Header for image display as edit format, followed by data.
2555         int width;
2556         int height;
2557         int format;
2558     };
2559     const QtInfo &qtInfo(QtInfo::get(v.context()));
2560     // Fetch data of unexported private class
2561     const ULONG64 address = v["d"].pointerValue();
2562     if (!address) {
2563         str << L"<null>";
2564         return true;
2565     }
2566     const std::string qImageDataType = qtInfo.prependQtGuiModule("QImageData");
2567     const unsigned long size = SymbolGroupValue::sizeOf(qImageDataType.c_str());
2568     if (!size)
2569         return false;
2570     unsigned char *qImageData = SymbolGroupValue::readMemory(v.context().dataspaces, address, size);
2571     if (!qImageData)
2572         return false;
2573     // read size data
2574     unsigned char *ptr = qImageData + qAtomicIntSize(v.context());
2575     CreatorImageHeader header;
2576     header.width = *(reinterpret_cast<int *>(ptr));
2577     ptr += SymbolGroupValue::intSize();
2578     header.height = *(reinterpret_cast<int *>(ptr));
2579     ptr += SymbolGroupValue::intSize();
2580     const int depth = *(reinterpret_cast<int *>(ptr));
2581     ptr += SymbolGroupValue::intSize();
2582     const int nbytes = *(reinterpret_cast<int *>(ptr));
2583     const unsigned dataOffset = SymbolGroupValue::fieldOffset(qImageDataType.c_str(), "data");
2584     // Qt 4 has a Qt 3 support pointer member between 'data' and 'format'.
2585     const unsigned formatOffset = SymbolGroupValue::fieldOffset(qImageDataType.c_str(), "format");
2586     if (!dataOffset || !formatOffset)
2587         return false;
2588     ptr = qImageData + dataOffset;
2589     // read data pointer
2590     ULONG64 data = 0;
2591     memcpy(&data, ptr, SymbolGroupValue::pointerSize());
2592     // read format
2593     ptr = qImageData + formatOffset;
2594     header.format = *(reinterpret_cast<int *>(ptr));
2595     if (header.width < 0 || header.height < 0 || header.format < 0 || header.format > 255
2596         || nbytes < 0 || depth < 0) {
2597         return false;
2598     }
2599     str << header.width << L'x' << header.height << L", depth: " << depth
2600         << L", format: " << header.format << L", "
2601         << nbytes << L" bytes";
2602     delete [] qImageData;
2603     // Create Creator Image data for display if reasonable size
2604     if (memoryHandle && data && nbytes > 0 && nbytes < 205824) {
2605         if (unsigned char *imageData = SymbolGroupValue::readMemory(v.context().dataspaces, data, nbytes)) {
2606             unsigned char *creatorImageData = new unsigned char[sizeof(CreatorImageHeader) + nbytes];
2607             memcpy(creatorImageData, &header, sizeof(CreatorImageHeader));
2608             memcpy(creatorImageData + sizeof(CreatorImageHeader), imageData, nbytes);
2609             delete [] imageData;
2610             *memoryHandle = new MemoryHandle(creatorImageData, sizeof(CreatorImageHeader) + nbytes);
2611             // cppcheck: don't delete[] creatorImageData here, it's taken over by MemoryHandle
2612         }
2613     }
2614     return true;
2615 }
2616 
2617 // Dump a rectangle in X11 syntax
2618 template <class T>
dumpRect(std::wostream & str,T x,T y,T width,T height)2619 inline void dumpRect(std::wostream &str, T x, T y, T width, T height)
2620 {
2621     str << width << 'x' << height;
2622     if (x >= 0)
2623         str << '+';
2624     str << x;
2625     if (y >= 0)
2626         str << '+';
2627     str << y;
2628 }
2629 
2630 // Dump Qt's simple geometrical types
dumpQSize_F(const SymbolGroupValue & v,std::wostream & str)2631 static inline bool dumpQSize_F(const SymbolGroupValue &v, std::wostream &str)
2632 {
2633     str << '(' << v["wd"].value() << ", " << v["ht"].value() << ')';
2634     return true;
2635 }
2636 
dumpQPoint_F(const SymbolGroupValue & v,std::wostream & str)2637 static inline bool dumpQPoint_F(const SymbolGroupValue &v, std::wostream &str)
2638 {
2639     str << '(' << v["xp"].value() << ", " << v["yp"].value() << ')';
2640     return true;
2641 }
2642 
dumpQLine_F(const SymbolGroupValue & v,std::wostream & str)2643 static inline bool dumpQLine_F(const SymbolGroupValue &v, std::wostream &str)
2644 {
2645     const SymbolGroupValue p1 = v["pt1"];
2646     const SymbolGroupValue p2 = v["pt2"];
2647     if (p1 && p2) {
2648         str << '(' << p1["xp"].value() << ", " << p1["yp"].value() << ") ("
2649             << p2["xp"].value() << ", " << p2["yp"].value() << ')';
2650         return true;
2651     }
2652     return false;
2653 }
2654 
dumpQRect(const SymbolGroupValue & v,std::wostream & str)2655 static inline bool dumpQRect(const SymbolGroupValue &v, std::wostream &str)
2656 {
2657     const int x1 = v["x1"].intValue();
2658     const int y1 = v["y1"].intValue();
2659     const int x2 = v["x2"].intValue();
2660     const int y2 = v["y2"].intValue();
2661     dumpRect(str, x1, y1, (x2 - x1 + 1), (y2 - y1 + 1));
2662     return true;
2663 }
2664 
dumpQRectF(const SymbolGroupValue & v,std::wostream & str)2665 static inline bool dumpQRectF(const SymbolGroupValue &v, std::wostream &str)
2666 {
2667     dumpRect(str, v["xp"].floatValue(), v["yp"].floatValue(), v["w"].floatValue(), v["h"].floatValue());
2668     return true;
2669 }
2670 
2671 /* Return a SymbolGroupValue containing the private class of
2672  * a type (multiple) derived from QObject and something else
2673  * (QWidget: QObject,QPaintDevice or QWindow: QObject,QSurface).
2674  * We get differing behaviour for pointers and values on stack.
2675  * For 'QWidget *', the base class QObject usually can be accessed
2676  * by name (past the vtable). When browsing class hierarchies (stack),
2677  * typically only the uninteresting QPaintDevice is seen. */
2678 
qobjectDerivedPrivate(const SymbolGroupValue & v,const std::string & qwPrivateType,const QtInfo & qtInfo)2679 SymbolGroupValue qobjectDerivedPrivate(const SymbolGroupValue &v,
2680                                        const std::string &qwPrivateType,
2681                                        const QtInfo &qtInfo)
2682 {
2683     if (const SymbolGroupValue base = v[SymbolGroupValue::stripModuleFromType(qtInfo.qObjectType).c_str()])
2684         if (const SymbolGroupValue qwPrivate = base["d_ptr"]["d"].pointerTypeCast(qwPrivateType.c_str()))
2685             return qwPrivate;
2686     if (!SymbolGroupValue::isPointerType(v.type()))
2687         return SymbolGroupValue();
2688     // Class hierarchy: Using brute force, add new symbol based on that
2689     // QScopedPointer<Private> is basically a 'X *' (first member).
2690     std::string errorMessage;
2691     std::ostringstream str;
2692     str << '(' << qwPrivateType << "*)(" << std::showbase << std::hex << v.address() << ')';
2693     const std::string name = str.str();
2694     SymbolGroupNode *qwPrivateNode
2695         = v.node()->symbolGroup()->addSymbol(v.module(), name, std::string(), &errorMessage);
2696     return SymbolGroupValue(qwPrivateNode, v.context());
2697 }
2698 
dumpQObjectName(const SymbolGroupValue & qoPrivate,std::wostream & str)2699 static bool dumpQObjectName(const SymbolGroupValue &qoPrivate, std::wostream &str)
2700 {
2701     // Qt 4: plain member.
2702     if (QtInfo::get(qoPrivate.context()).version < 5) {
2703         if (const SymbolGroupValue oName = qoPrivate["objectName"])
2704             return dumpQString(oName, str);
2705     }
2706     // Qt 5: member of allocated extraData.
2707     if (const SymbolGroupValue extraData = qoPrivate["extraData"])
2708         if (extraData.pointerValue())
2709             if (const SymbolGroupValue oName = extraData["objectName"])
2710                 return dumpQString(oName, str);
2711     return false;
2712 }
2713 
2714 // Dump the object name
dumpQWidget(const SymbolGroupValue & v,std::wostream & str,void ** specialInfoIn=0)2715 static inline bool dumpQWidget(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0)
2716 {
2717     const QtInfo &qtInfo = QtInfo::get(v.context());
2718     const SymbolGroupValue qwPrivate =
2719         qobjectDerivedPrivate(v, qtInfo.qWidgetPrivateType, qtInfo);
2720     // QWidgetPrivate inherits QObjectPrivate
2721     if (!qwPrivate || !dumpQObjectName(qwPrivate[unsigned(0)], str))
2722         return false;
2723     if (specialInfoIn)
2724         *specialInfoIn = qwPrivate.node();
2725     return true;
2726 }
2727 
2728 // Dump the object name
dumpQObject(const SymbolGroupValue & v,std::wostream & str,void ** specialInfoIn=0)2729 static inline bool dumpQObject(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0)
2730 {
2731     const std::string &qoPrivateType = QtInfo::get(v.context()).qObjectPrivateType;
2732     const SymbolGroupValue qoPrivate = v["d_ptr"]["d"].pointerTypeCast(qoPrivateType.c_str());
2733     if (!qoPrivate || !dumpQObjectName(qoPrivate, str))
2734         return false;
2735     if (specialInfoIn)
2736         *specialInfoIn = qoPrivate.node();
2737     return true;
2738 }
2739 
2740 // Dump the object name
dumpQWindow(const SymbolGroupValue & v,std::wostream & str,void ** specialInfoIn=0)2741 static inline bool dumpQWindow(const SymbolGroupValue &v, std::wostream &str, void **specialInfoIn = 0)
2742 {
2743     const QtInfo &qtInfo = QtInfo::get(v.context());
2744     const SymbolGroupValue qwPrivate =
2745         qobjectDerivedPrivate(v, qtInfo.qWindowPrivateType, qtInfo);
2746     // QWindowPrivate inherits QObjectPrivate
2747     if (!qwPrivate || !dumpQObjectName(qwPrivate[unsigned(0)], str))
2748         return false;
2749     if (specialInfoIn)
2750         *specialInfoIn = qwPrivate.node();
2751     return true;
2752 }
2753 
2754 //Dump a QTextCursor
dumpQTextCursor(const SymbolGroupValue & v,std::wostream & str)2755 static inline bool dumpQTextCursor(const SymbolGroupValue &v, std::wostream &str)
2756 {
2757     const unsigned offset = SymbolGroupValue::pointerSize() + SymbolGroupValue::sizeOf("double");
2758     const ULONG64 posAddr = addressOfQPrivateMember(v, QPDM_qSharedDataPadded, offset);
2759     if (!posAddr)
2760         return false;
2761     const int position = SymbolGroupValue::readIntValue(v.context().dataspaces, posAddr);
2762     str << position;
2763     return true;
2764 }
2765 
2766 // Dump a std::string.
dumpStd_W_String(const SymbolGroupValue & v,int type,std::wostream & str,MemoryHandle ** memoryHandle=0)2767 static bool dumpStd_W_String(const SymbolGroupValue &v, int type, std::wostream &str,
2768                              MemoryHandle **memoryHandle = 0)
2769 {
2770     // Find 'bx'. MSVC 2012 has 2 base classes, MSVC 2010 1,
2771     // and MSVC2008 none
2772     const SymbolGroupValue bx = SymbolGroupValue::findMember(v, "_Bx");
2773     const int reserved = bx.parent()["_Myres"].intValue();
2774     int size = bx.parent()["_Mysize"].intValue();
2775     if (!bx || reserved < 0 || size < 0)
2776         return false;
2777     const bool truncated = unsigned(size) > ExtensionContext::instance().parameters().maxStringLength;
2778     if (truncated)
2779         size = ExtensionContext::instance().parameters().maxStringLength;
2780     // 'Buf' array for small strings, else pointer 'Ptr'.
2781     const int bufSize = type == KT_StdString ? 16 : 8; // see basic_string.
2782     const unsigned long memSize = type == KT_StdString ? size : 2 * size;
2783     const ULONG64 address = reserved >= bufSize ? bx["_Ptr"].pointerValue() : bx["_Buf"].address();
2784     if (!address)
2785         return false;
2786     unsigned char *memory = SymbolGroupValue::readMemory(v.context().dataspaces, address, memSize);
2787     if (!memory)
2788         return false;
2789     str << (type == KT_StdString ?
2790         quotedWStringFromCharData(memory, memSize, truncated) :
2791         quotedWStringFromWCharData(memory, memSize, truncated));
2792     if (memoryHandle)
2793         *memoryHandle = new MemoryHandle(memory, memSize);
2794     else
2795         delete [] memory;
2796     return true;
2797 }
2798 
2799 // Dump a std::complex.
dumpStd_Complex(const SymbolGroupValue & v,std::wostream & str)2800 static bool dumpStd_Complex(const SymbolGroupValue &v, std::wostream &str)
2801 {
2802     if (const SymbolGroupValue &valArray = v[0u][0u]["_Val"]) {
2803         if (const SymbolGroupValue &val0 = valArray["0"]) {
2804             str << L'(' << val0.value();
2805             if (const SymbolGroupValue &val1 = valArray["1"]) {
2806                 str << L", " << val1.value() << L')';
2807                 return true;
2808             }
2809         }
2810     }
2811     return false;
2812 }
2813 
2814 // QVariant employs a template for storage where anything bigger than the data union
2815 // is pointed to by data.shared.ptr, else it is put into the data struct (pointer size)
2816 // itself (notably Qt types consisting of a d-ptr only).
2817 // The layout can vary between 32 /64 bit for some types: QPoint/QSize (of 2 ints) is bigger
2818 // as a pointer only on 32 bit.
2819 
qVariantCast(const SymbolGroupValue & variantData,const char * type)2820 static inline SymbolGroupValue qVariantCast(const SymbolGroupValue &variantData, const char *type)
2821 {
2822     const ULONG typeSize = SymbolGroupValue::sizeOf(type);
2823     const std::string ptrType = std::string(type) + " *";
2824     if (typeSize > variantData.size())
2825         return variantData["shared"]["ptr"].pointerTypeCast(ptrType.c_str());
2826     return variantData.typeCast(ptrType.c_str());
2827 }
2828 
2829 // Qualify a local container template of Qt Types for QVariant
2830 // as 'QList' of 'QVariant' -> 'localModule!qtnamespace::QList<qtnamespace::QVariant> *'
2831 static inline std::string
variantContainerType(const std::string & containerType,const std::string & innerType1,const std::string & innerType2,const QtInfo & qtInfo,const SymbolGroupValue & contextHelper)2832     variantContainerType(const std::string &containerType,
2833                          const std::string &innerType1,
2834                          const std::string &innerType2 /* = "" */,
2835                          const QtInfo &qtInfo,
2836                          const SymbolGroupValue &contextHelper)
2837 {
2838     std::string rc = QtInfo::prependModuleAndNameSpace(containerType, contextHelper.module(),
2839                                                        qtInfo.nameSpace);
2840     rc.push_back('<');
2841     rc += QtInfo::prependModuleAndNameSpace(innerType1, std::string(), qtInfo.nameSpace);
2842     if (!innerType2.empty()) {
2843         rc.push_back(',');
2844         rc += QtInfo::prependModuleAndNameSpace(innerType2, std::string(), qtInfo.nameSpace);
2845     }
2846     rc += "> *";
2847     return rc;
2848 }
2849 
dumpQVariant(const SymbolGroupValue & v,std::wostream & str,std::string * encoding,void ** specialInfoIn=0)2850 static bool dumpQVariant(const SymbolGroupValue &v, std::wostream &str, std::string *encoding,
2851                          void **specialInfoIn = 0)
2852 {
2853     const QtInfo &qtInfo = QtInfo::get(v.context());
2854     const SymbolGroupValue dV = v["d"];
2855     if (!dV)
2856         return false;
2857     const SymbolGroupValue typeV = dV["type"];
2858     const SymbolGroupValue dataV = dV["data"];
2859     if (!typeV || !dataV)
2860         return false;
2861     const int typeId = typeV.intValue();
2862     if (typeId <= 0) {
2863         str <<  L"<Invalid>";
2864         return true;
2865     }
2866     switch (typeId) {
2867     case 1: // Bool
2868         str << L"(bool) " << dataV["b"].value();
2869         break;
2870     case 2: // Int
2871         str << L"(int) " << dataV["i"].value();
2872         break;
2873     case 3: // UInt
2874         str << L"(unsigned) " << dataV["u"].value();
2875         break;
2876     case 4: // LongLong
2877         str << L"(long long) " << dataV["ll"].value();
2878         break;
2879     case 5: // LongLong
2880         str << L"(unsigned long long) " << dataV["ull"].value();
2881         break;
2882     case 6: // Double
2883         str << L"(double) " << dataV["d"].value();
2884         break;
2885     case 7: // Char
2886         str << L"(char) " << dataV["c"].value();
2887         break;
2888     case 8: {
2889         str << L"(QVariantMap) ";
2890         const std::string vmType = variantContainerType("QMap", "QString", "QVariant", qtInfo, dataV);
2891         if (const SymbolGroupValue mv = dataV.typeCast(vmType.c_str())) {
2892             SymbolGroupNode *mapNode = mv.node();
2893             std::wstring value;
2894             std::string tmp;
2895             if (dumpSimpleType(mapNode, dataV.context(), &value, &tmp)
2896                     == SymbolGroupNode::SimpleDumperOk) {
2897                 str << value;
2898                 if (specialInfoIn)
2899                     *specialInfoIn = mapNode;
2900             }
2901         }
2902     }
2903         break;
2904     case 9: { // QVariantList
2905         str << L"(QVariantList) ";
2906         const std::string vLType = variantContainerType("QList", "QVariant", std::string(), qtInfo, dataV);
2907         if (const SymbolGroupValue vl = dataV.typeCast(vLType.c_str())) {
2908             SymbolGroupNode *vListNode = vl.node();
2909             std::wstring value;
2910             std::string tmp;
2911             if (dumpSimpleType(vListNode, dataV.context(), &value, &tmp)
2912                     == SymbolGroupNode::SimpleDumperOk) {
2913                 str << value;
2914                 if (specialInfoIn)
2915                     *specialInfoIn = vListNode;
2916             }
2917         }
2918     }
2919         break;
2920     case 10: // String
2921         str << L"(QString) ";
2922         if (const SymbolGroupValue sv = dataV.typeCast(qtInfo.prependQtCoreModule("QString *").c_str())) {
2923             if (!dumpQString(sv, str)) {
2924                 // HACK:
2925                 // In some rare cases the AddSymbol can't create a node with a given module name,
2926                 // but is able to add the symbol without any modulename.
2927                 if (const SymbolGroupValue svc = dataV.typeCast("QString *"))
2928                     dumpQString(svc, str);
2929             }
2930         }
2931         break;
2932     case 11: //StringList: Dump container size
2933         str << L"(QStringList) ";
2934         if (const SymbolGroupValue sl = dataV.typeCast(qtInfo.prependQtCoreModule("QStringList *").c_str())) {
2935             SymbolGroupNode *listNode = sl.node();
2936             std::wstring value;
2937             std::string tmp;
2938             if (dumpSimpleType(listNode, dataV.context(), &value, &tmp)
2939                     == SymbolGroupNode::SimpleDumperOk) {
2940                 str << value;
2941                 if (specialInfoIn)
2942                     *specialInfoIn = listNode;
2943             }
2944         }
2945         break;
2946     case 12: //ByteArray
2947         str << L"(QByteArray) ";
2948         if (const SymbolGroupValue sv = dataV.typeCast(qtInfo.prependQtCoreModule("QByteArray *").c_str()))
2949             dumpQByteArray(sv, str, encoding);
2950         break;
2951     case 13: // BitArray
2952         str << L"(QBitArray)";
2953         break;
2954     case 14: // Date: Do not qualify - fails non-deterministically with QtCored4!QDate
2955         str << L"(QDate) ";
2956         if (const SymbolGroupValue sv = dataV.typeCast("QDate *"))
2957             dumpQDate(sv, str, encoding);
2958         break;
2959     case 15: // Time: Do not qualify - fails non-deterministically with QtCored4!QTime
2960         str << L"(QTime) ";
2961         if (const SymbolGroupValue sv = dataV.typeCast("QTime *"))
2962             dumpQTime(sv, str, encoding);
2963         break;
2964     case 16: // DateTime
2965         str << L"(QDateTime)";
2966         break;
2967     case 17: // Url
2968         str << L"(QUrl)";
2969         break;
2970     case 18: // Locale
2971         str << L"(QLocale)";
2972         break;
2973     case 19: // Rect:
2974         str << L"(QRect) ";
2975         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QRect *").c_str()))
2976             dumpQRect(sv, str);
2977         break;
2978     case 20: // RectF
2979         str << L"(QRectF) ";
2980         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QRectF *").c_str()))
2981             dumpQRectF(sv, str);
2982         break;
2983     case 21: // Size
2984         // Anything bigger than the data union is a pointer, else the data union is used
2985         str << L"(QSize) ";
2986         if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtCoreModule("QSize").c_str()))
2987             dumpQSize_F(sv, str);
2988         break;
2989     case 22: // SizeF
2990         str << L"(QSizeF) ";
2991         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QSizeF *").c_str()))
2992             dumpQSize_F(sv, str);
2993         break;
2994     case 23: // Line
2995         str << L"(QLine) ";
2996         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QLine *").c_str()))
2997             dumpQLine_F(sv, str);
2998         break;
2999     case 24: // LineF
3000         str << L"(QLineF) ";
3001         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QLineF *").c_str()))
3002             dumpQLine_F(sv, str);
3003         break;
3004     case 25: // Point
3005         str << L"(QPoint) ";
3006         if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtCoreModule("QPoint").c_str()))
3007             dumpQPoint_F(sv, str);
3008         break;
3009     case 26: // PointF
3010         str << L"(QPointF) ";
3011         if (const SymbolGroupValue sv = dataV["shared"]["ptr"].pointerTypeCast(qtInfo.prependQtCoreModule("QPointF *").c_str()))
3012             dumpQPoint_F(sv, str);
3013         break;
3014     case 65: // QPixmap
3015         str << L"(QPixmap) ";
3016         if (const SymbolGroupValue sv = qVariantCast(dataV, qtInfo.prependQtGuiModule("QPixmap").c_str()))
3017             dumpQPixmap(sv, str);
3018         break;
3019     default:
3020         str << L"Type " << typeId;
3021         break;
3022     }
3023     return true;
3024 }
3025 
dumpQSharedPointer(const SymbolGroupValue & v,std::wostream & str,std::string * encoding,void ** specialInfoIn=0)3026 static inline bool dumpQSharedPointer(const SymbolGroupValue &v, std::wostream &str, std::string *encoding, void **specialInfoIn = 0)
3027 {
3028     const SymbolGroupValue externalRefCountV = v[unsigned(0)];
3029     const QtInfo qtInfo = QtInfo::get(v.context());
3030     if (qtInfo.version < 5) {
3031         if (!externalRefCountV)
3032             return false;
3033         const SymbolGroupValue dV = externalRefCountV["d"];
3034         if (!dV)
3035             return false;
3036         // Get value element from base and store in special info.
3037         const SymbolGroupValue valueV = externalRefCountV[unsigned(0)]["value"];
3038         if (!valueV)
3039             return false;
3040         // Format references.
3041         const int strongRef = dV["strongref"]["_q_value"].intValue();
3042         const int weakRef = dV["weakref"]["_q_value"].intValue();
3043         if (strongRef < 0 || weakRef < 0)
3044             return false;
3045         str << L"References: " << strongRef << '/' << weakRef;
3046         if (specialInfoIn)
3047             *specialInfoIn = valueV.node();
3048         return true;
3049     } else { // Qt 5
3050         SymbolGroupValue value = v["value"];
3051         if (value.pointerValue(0) == 0) {
3052             str << L"(null)";
3053             return true;
3054         }
3055 
3056         if (knownType(value.type(), KnownTypeAutoStripPointer | KnownTypeHasClassPrefix)
3057                 & KT_HasSimpleDumper) {
3058             str << value.node()->simpleDumpValue(v.context(), encoding);
3059             return true;
3060         }
3061 
3062         return false;
3063     }
3064 }
3065 
3066 // Dump builtin simple types using SymbolGroupValue expressions.
dumpSimpleType(SymbolGroupNode * n,const SymbolGroupValueContext & ctx,std::wstring * s,std::string * encoding,int * knownTypeIn,int * containerSizeIn,void ** specialInfoIn,MemoryHandle ** memoryHandleIn)3067 unsigned dumpSimpleType(SymbolGroupNode  *n, const SymbolGroupValueContext &ctx,
3068                         std::wstring *s, std::string *encoding, int *knownTypeIn /* = 0 */,
3069                         int *containerSizeIn /* = 0 */,
3070                         void **specialInfoIn /* = 0 */,
3071                         MemoryHandle **memoryHandleIn /* = 0 */)
3072 {
3073     QTC_TRACE_IN
3074     if (containerSizeIn)
3075         *containerSizeIn = -1;
3076     if (specialInfoIn)
3077         *specialInfoIn  = 0;
3078     // Check for class types and strip pointer types (references appear as pointers as well)
3079     s->clear();
3080     const KnownType kt = knownType(n->type(), KnownTypeHasClassPrefix|KnownTypeAutoStripPointer);
3081     if (knownTypeIn)
3082         *knownTypeIn = kt;
3083 
3084     if (kt == KT_Unknown || !(kt & KT_HasSimpleDumper)) {
3085         if (SymbolGroupValue::verbose > 1)
3086             DebugPrint() << "dumpSimpleType N/A " << n->name() << '/' << n->type();
3087         QTC_TRACE_OUT
3088         return SymbolGroupNode::SimpleDumperNotApplicable;
3089     }
3090 
3091     std::wostringstream str;
3092 
3093     // Prefix by pointer value
3094     const SymbolGroupValue v(n, ctx);
3095     if (!v) // Value as such has memory read error?
3096         return SymbolGroupNode::SimpleDumperFailed;
3097 
3098     unsigned rc = SymbolGroupNode::SimpleDumperNotApplicable;
3099     // Simple dump of size for containers
3100     if (kt & KT_ContainerType) {
3101         const int size = containerSize(kt, v);
3102         if (SymbolGroupValue::verbose > 1)
3103             DebugPrint() << "dumpSimpleType Container " << n->name() << '/' << n->type() << " size=" << size;
3104         if (containerSizeIn)
3105             *containerSizeIn = size;
3106         if (size >= 0) {
3107             str << L'<' << size << L" items>";
3108             rc = SymbolGroupNode::SimpleDumperOk;
3109         } else {
3110             rc = SymbolGroupNode::SimpleDumperFailed;
3111         }
3112     } else {
3113         switch (kt) {
3114         case KT_QChar:
3115             rc = dumpQChar(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3116             break;
3117         case KT_QByteArray:
3118             rc = dumpQByteArray(v, str, encoding, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3119             break;
3120         case KT_QFileInfo:
3121             rc = dumpQFileInfo(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3122             break;
3123         case KT_QFile:
3124             rc = dumpQFile(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3125             break;
3126         case KT_QDir:
3127             rc = dumpQDir(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3128             break;
3129         case KT_QRegExp:
3130             rc = dumpQRegExp(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3131             break;
3132         case KT_QRegion:
3133             rc = dumpQRegion(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3134             break;
3135         case KT_QUrl:
3136             rc = dumpQUrl(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3137             break;
3138         case KT_QHostAddress:
3139             rc = dumpQHostAddress(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3140             break;
3141         case KT_QIPv6Address:
3142             rc = dumpQIPv6Address(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3143             break;
3144         case KT_QProcess:
3145             rc = dumpQProcess(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3146             break;
3147         case KT_QScriptValue:
3148             rc = dumpQScriptValue(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3149             break;
3150         case KT_QString:
3151             rc = dumpQString(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3152             break;
3153         case KT_QStringRef:
3154             rc = dumpQStringRef(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3155             break;
3156         case KT_QColor:
3157             rc = dumpQColor(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3158             break;
3159         case KT_QFlags:
3160             rc = dumpQFlags(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3161             break;
3162         case KT_QDate:
3163             rc = dumpQDate(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3164             break;
3165         case KT_QTime:
3166             rc = dumpQTime(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3167             break;
3168         case KT_QDateTime:
3169             rc = dumpQDateTime(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3170             break;
3171         case KT_QTimeZone:
3172             rc = dumpQTimeZone(v, str, encoding) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3173             break;
3174         case KT_QPoint:
3175         case KT_QPointF:
3176             rc = dumpQPoint_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3177             break;
3178         case KT_QSize:
3179         case KT_QSizeF:
3180             rc = dumpQSize_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3181             break;
3182         case KT_QLine:
3183         case KT_QLineF:
3184             rc = dumpQLine_F(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3185             break;
3186         case KT_QPixmap:
3187             rc = dumpQPixmap(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3188             break;
3189         case KT_QImage:
3190             rc = dumpQImage(v, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3191             break;
3192         case KT_QRect:
3193             rc = dumpQRect(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3194             break;
3195         case KT_QRectF:
3196             rc = dumpQRectF(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3197             break;
3198         case KT_QVariant:
3199             rc = dumpQVariant(v, str, encoding, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3200             break;
3201         case KT_QAtomicInt:
3202             rc = dumpQAtomicInt(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3203             break;
3204         case KT_QBasicAtomicInt:
3205             rc = dumpQBasicAtomicInt(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3206             break;
3207         case KT_QObject:
3208             rc = dumpQObject(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3209             break;
3210         case KT_QWidget:
3211             rc = dumpQWidget(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3212             break;
3213         case KT_QWindow:
3214             rc = dumpQWindow(v, str, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3215             break;
3216         case KT_QSharedPointer:
3217         case KT_QWeakPointer:
3218             rc = dumpQSharedPointer(v, str, encoding, specialInfoIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3219             break;
3220         case KT_StdString:
3221         case KT_StdWString:
3222             rc = dumpStd_W_String(v, kt, str, memoryHandleIn) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3223             break;
3224         case KT_StdComplex:
3225             rc = dumpStd_Complex(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3226             break;
3227         case KT_QTextCursor:
3228             rc = dumpQTextCursor(v, str) ? SymbolGroupNode::SimpleDumperOk : SymbolGroupNode::SimpleDumperFailed;
3229             break;
3230         default:
3231             break;
3232         }
3233     }
3234     if (rc != SymbolGroupNode::SimpleDumperFailed && SymbolGroupValue::isPointerType(v.type()) && encoding->empty())
3235         str << L" @" << std::showbase << std::hex << v.pointerValue() << std::dec << std::noshowbase;
3236 
3237     if (rc == SymbolGroupNode::SimpleDumperOk)
3238         *s = str.str();
3239     QTC_TRACE_OUT
3240 
3241     if (SymbolGroupValue::verbose > 1) {
3242         DebugPrint dp;
3243         dp << "dumpSimpleType " << n->name() << '/' << n->type() << " knowntype= " << kt << " [";
3244         formatKnownTypeFlags(dp, kt);
3245         dp << "] returns " << rc;
3246     }
3247     return rc;
3248 }
3249 
formatEditValue(const std::string & displayFormat,const MemoryHandle * mh,std::ostream & str)3250 static inline void formatEditValue(const std::string &displayFormat, const MemoryHandle *mh, std::ostream &str)
3251 {
3252     str << "editformat=\"" << displayFormat << "\",editvalue=\""
3253         << mh->toHex() << "\",";
3254 }
3255 
dumpEditValue(const SymbolGroupNode * n,const SymbolGroupValueContext &,const std::string & desiredFormat,std::ostream & str)3256 void dumpEditValue(const SymbolGroupNode *n, const SymbolGroupValueContext &,
3257                    const std::string &desiredFormat, std::ostream &str)
3258 {
3259     if (SymbolGroupValue::verbose)
3260         DebugPrint() << __FUNCTION__ << ' ' << n->name() << '/' << desiredFormat;
3261 
3262     auto separatorPos = desiredFormat.find(':');
3263     if (separatorPos == std::string::npos)
3264         return;
3265 
3266     if (desiredFormat.substr(separatorPos) != "separate")
3267         return;
3268 
3269     if (const MemoryHandle *mh = n->memory())
3270         formatEditValue(desiredFormat, mh, str);
3271 }
3272 
3273 // Dump of QByteArray: Display as an array of unsigned chars.
3274 static inline std::vector<AbstractSymbolGroupNode *>
complexDumpQByteArray(SymbolGroupNode * n,const SymbolGroupValueContext & ctx)3275     complexDumpQByteArray(SymbolGroupNode *n, const SymbolGroupValueContext &ctx)
3276 {
3277     std::vector<AbstractSymbolGroupNode *> rc;
3278 
3279     const SymbolGroupValue baV(n, ctx);
3280     const SymbolGroupValue dV = baV["d"];
3281     if (!dV)
3282         return rc;
3283     // Determine memory area.
3284 
3285     unsigned size = 0;
3286     ULONG64 address = 0;
3287     const QtStringAddressData data = readQtStringAddressData(dV, QtInfo::get(ctx));
3288     size = data.size;
3289     address = data.address;
3290 
3291     if (size <= 0 || !address)
3292         return rc;
3293 
3294     if (size > 200)
3295         size = 200;
3296     rc.reserve(size);
3297     const std::string charType = "char";
3298     std::string errorMessage;
3299     SymbolGroup *sg = n->symbolGroup();
3300     for (int i = 0; i < (int)size; ++i, ++address) {
3301         SymbolGroupNode *en = sg->addSymbol(std::string(), SymbolGroupValue::pointedToSymbolName(address, charType),
3302                                             std::string(), &errorMessage);
3303         if (!en) {
3304             rc.clear();
3305             return rc;
3306         }
3307         rc.push_back(ReferenceSymbolGroupNode::createArrayNode(i, en));
3308     }
3309     return rc;
3310 }
3311 
3312 // Assignment helpers
msgAssignStringFailed(const std::string & value,int errorCode)3313 static inline std::string msgAssignStringFailed(const std::string &value, int errorCode)
3314 {
3315     std::ostringstream estr;
3316     estr << "Unable to assign a string of " << value.size() << " bytes: Error " << errorCode;
3317     return estr.str();
3318 }
3319 
3320 /* QString assign helper: If QString instance has sufficiently allocated,
3321  * memory, write the data. Else invoke 'QString::resize' and
3322  * recurse (since 'd' might become invalid). This works for QString with UTF16
3323  * data and for QByteArray with ASCII data due to the similar member
3324  * names and both using a terminating '\0' w_char/byte. */
3325 template <typename string>
assignQStringI(SymbolGroupNode * n,const char * className,const string & data,const SymbolGroupValueContext & ctx,bool doAlloc=true)3326 static int assignQStringI(SymbolGroupNode  *n, const char *className,
3327                           const string &data,
3328                           const SymbolGroupValueContext &ctx,
3329                           bool doAlloc = true)
3330 {
3331     const SymbolGroupValue v(n, ctx);
3332     SymbolGroupValue d = v["d"];
3333     if (!d)
3334         return 1;
3335     const QtInfo &qtInfo = QtInfo::get(ctx);
3336     // Check the size, re-allocate if required.
3337     const QtStringAddressData addressData = readQtStringAddressData(d, qtInfo);
3338     if (!addressData.address)
3339         return 9;
3340     const bool needRealloc = addressData.allocated < data.size();
3341     if (needRealloc) {
3342         if (!doAlloc) // Calling re-alloc failed somehow.
3343             return 3;
3344         std::ostringstream callStr;
3345         const std::string funcName
3346             = qtInfo.prependQtCoreModule(std::string(className) + "::resize");
3347         callStr << funcName << '(' << std::hex << std::showbase
3348                 << v.address() << ',' << data.size() << ')';
3349         std::wstring wOutput;
3350         std::string errorMessage;
3351         return ExtensionContext::instance().call(callStr.str(), 0, &wOutput, &errorMessage) ?
3352             assignQStringI(n, className, data, ctx, false) : 5;
3353     }
3354     // Write data.
3355     if (!SymbolGroupValue::writeMemory(v.context().dataspaces,
3356                                        addressData.address, (const unsigned char *)(data.c_str()),
3357                                        ULONG(data.empty() ? 0 : sizeof(data.front()) * data.size())))
3358         return 11;
3359     // Correct size unless we re-allocated
3360     if (!needRealloc) {
3361         const std::string &arrayData =
3362                 qtInfo.prependModuleAndNameSpace("QArrayData", std::string(), qtInfo.nameSpace);
3363         const SymbolGroupValue dV = qtInfo.version < 5 ? d : d[arrayData.c_str()];
3364         if (!dV)
3365             return 14;
3366         const SymbolGroupValue size = dV["size"];
3367         if (!size)
3368             return 16;
3369         if (!size.node()->assign(toString(data.size())))
3370             return 17;
3371     }
3372     return 0;
3373 }
3374 
3375 // QString assignment
assignQString(SymbolGroupNode * n,const std::string & value,const SymbolGroupValueContext & ctx,std::string * errorMessage)3376 static inline bool assignQString(SymbolGroupNode  *n,
3377                                  const std::string &value,
3378                                  const SymbolGroupValueContext &ctx,
3379                                  std::string *errorMessage)
3380 {
3381     const int errorCode = assignQStringI(n, "QString", utf8ToUtf16(value), ctx);
3382     if (errorCode) {
3383         *errorMessage = msgAssignStringFailed(value, errorCode);
3384         return false;
3385     }
3386     return true;
3387 }
3388 
3389 // QByteArray assignment
assignQByteArray(SymbolGroupNode * n,const std::string & value,const SymbolGroupValueContext & ctx,std::string * errorMessage)3390 static inline bool assignQByteArray(SymbolGroupNode  *n,
3391                                     const std::string &value,
3392                                     const SymbolGroupValueContext &ctx,
3393                                     std::string *errorMessage)
3394 {
3395     const int errorCode = assignQStringI(n, "QByteArray", value, ctx);
3396     if (errorCode) {
3397         *errorMessage = msgAssignStringFailed(value, errorCode);
3398         return false;
3399     }
3400     return true;
3401 }
3402 
3403 // Helper to assign character data to std::string or std::wstring.
3404 template <typename string>
assignStdStringI(SymbolGroupNode * n,int type,const string & data,const SymbolGroupValueContext & ctx)3405 static inline int assignStdStringI(SymbolGroupNode *n, int type,
3406                                    const string &data,
3407                                    const SymbolGroupValueContext &ctx)
3408 {
3409     /* We do not reallocate and just write to the allocated buffer
3410      * (internal small buffer or _Ptr depending on reserved) provided sufficient
3411      * memory is there since it is apparently not possible to call the template
3412      * function std::string::resize().
3413      * See the dumpStd_W_String() how to figure out if the internal buffer
3414      * or an allocated array is used. */
3415 
3416     const SymbolGroupValue v(n, ctx);
3417     SymbolGroupValue base = v;
3418     SymbolGroupValue bx = base["_Bx"];
3419     if (!bx) {
3420         base = base[unsigned(0)];
3421         bx = base["_Bx"];
3422     }
3423     if (!bx) {
3424         base = base[unsigned(0)][unsigned(1)];
3425         bx = base["_Bx"];
3426     }
3427     if (!bx)
3428         return 24;
3429     SymbolGroupValue size = base["_Mysize"];
3430     int reserved = base["_Myres"].intValue();
3431     if (reserved < 0 || !size || !bx)
3432         return 42;
3433     if (reserved <= (int)data.size())
3434         return 1; // Insufficient memory.
3435     // Copy data: 'Buf' array for small strings, else pointer 'Ptr'.
3436     const int bufSize = type == KT_StdString ? 16 : 8; // see basic_string.
3437     const ULONG64 address = (bufSize <= reserved) ?
3438                             bx["_Ptr"].pointerValue() : bx["_Buf"].address();
3439     if (!address)
3440         return 3;
3441     if (!SymbolGroupValue::writeMemory(v.context().dataspaces,
3442                                        address,
3443                                        (const unsigned char *)(data.c_str()),
3444                                        ULONG(data.empty() ? 0 : sizeof(data.front()) * data.size())))
3445         return 7;
3446     // Correct size
3447     if (!size.node()->assign(toString(data.size())))
3448         return 13;
3449     return 0;
3450 }
3451 
3452 // assignment of std::string assign via ASCII, std::wstring via UTF16
assignStdString(SymbolGroupNode * n,int type,const std::string & value,const SymbolGroupValueContext & ctx,std::string * errorMessage)3453 static inline bool assignStdString(SymbolGroupNode  *n,
3454                                    int type, const std::string &value,
3455                                    const SymbolGroupValueContext &ctx,
3456                                    std::string *errorMessage)
3457 {
3458     const int errorCode = type == KT_StdString
3459                               ? assignStdStringI(n, type, value, ctx)
3460                               : assignStdStringI(n, type, utf8ToUtf16(value), ctx);
3461     if (errorCode) {
3462         *errorMessage = msgAssignStringFailed(value, errorCode);
3463         return false;
3464     }
3465     return true;
3466 }
3467 
assignType(SymbolGroupNode * n,int knownType,const std::string & value,const SymbolGroupValueContext & ctx,std::string * errorMessage)3468 bool assignType(SymbolGroupNode *n, int knownType, const std::string &value,
3469                 const SymbolGroupValueContext &ctx, std::string *errorMessage)
3470 {
3471     switch (knownType) {
3472     case KT_QString:
3473         return assignQString(n, value, ctx, errorMessage);
3474     case KT_QByteArray:
3475         return assignQByteArray(n,value, ctx, errorMessage);
3476     case KT_StdString:
3477     case KT_StdWString:
3478         return assignStdString(n, knownType, value, ctx, errorMessage);
3479     default:
3480         break;
3481     }
3482     return false;
3483 }
3484 
3485 std::vector<AbstractSymbolGroupNode *>
dumpComplexType(SymbolGroupNode * n,int type,void * specialInfo,const SymbolGroupValueContext & ctx)3486     dumpComplexType(SymbolGroupNode *n, int type, void *specialInfo,
3487                     const SymbolGroupValueContext &ctx)
3488 {
3489     std::vector<AbstractSymbolGroupNode *> rc;
3490     if (!(type & KT_HasComplexDumper))
3491         return rc;
3492     switch (type) {
3493     case KT_QByteArray:
3494         rc = complexDumpQByteArray(n, ctx);
3495         break;
3496     case KT_QRegion:
3497         if (specialInfo) {
3498             typedef AbstractSymbolGroupNode::AbstractSymbolGroupNodePtrVector NodeVector;
3499             NodeVector children =
3500                     reinterpret_cast<SymbolGroupNode *>(specialInfo)->children();
3501             for (NodeVector::iterator it = children.begin(); it != children.end(); ++it) {
3502                 if (SymbolGroupNode *node = (*it)->asSymbolGroupNode())
3503                     rc.push_back(new ReferenceSymbolGroupNode(node->name(), node->iName(), node));
3504             }
3505         }
3506         break;
3507     case KT_QWidget: // Special info by simple dumper is the QWidgetPrivate node
3508     case KT_QWindow: // Special info by simple dumper is the QWindowPrivate node
3509     case KT_QObject: // Special info by simple dumper is the QObjectPrivate node
3510         if (specialInfo) {
3511             SymbolGroupNode *qObjectPrivateNode = reinterpret_cast<SymbolGroupNode *>(specialInfo);
3512             rc.push_back(new ReferenceSymbolGroupNode("d", "d", qObjectPrivateNode));
3513         }
3514         break;
3515     case KT_QVariant: // Special info by simple dumper is the container (stringlist, map,etc)
3516         if (specialInfo) {
3517             SymbolGroupNode *containerNode = reinterpret_cast<SymbolGroupNode *>(specialInfo);
3518             rc.push_back(new ReferenceSymbolGroupNode("children", "children", containerNode));
3519         }
3520     case KT_QWeakPointer:
3521     case KT_QSharedPointer: // Special info by simple dumper is the value
3522         if (specialInfo) {
3523             SymbolGroupNode *valueNode = reinterpret_cast<SymbolGroupNode *>(specialInfo);
3524             rc.push_back(new ReferenceSymbolGroupNode("value", "value", valueNode));
3525         }
3526         break;
3527     default:
3528         break;
3529     }
3530     if (SymbolGroupValue::verbose) {
3531         DebugPrint dp;
3532         dp << "<dumpComplexType" << rc.size() << ' ' << specialInfo << ' ';
3533         for (VectorIndexType i = 0; i < rc.size() ; ++i)
3534             dp << i << ' ' << rc.at(i)->name();
3535     }
3536     return rc;
3537 }
3538