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 ¤tModule /* = "" */)
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