Cdbext: lazy type lookup

Postpone the lookup until we need the type id. This potentially skips
the lookup for unresolvable types that are just to parse template
parameters. Also cache types we already checked to prevent looking up
types multiple times. Additionally traverse all modules in order to find
unresolvable types.

This combination reduces the time the debugger needs to execute all
dumper tests from ~7:30 to ~3:30 on my machine.

Task-number: QTCREATORBUG-18287
Change-Id: Ie1e9771f124096c2a9ad24ac26c9b51fcded4fed
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2023-02-10 10:20:10 +01:00
parent 72de50df83
commit e47dae643a
3 changed files with 125 additions and 47 deletions
+1 -1
View File
@@ -491,7 +491,7 @@ class Dumper(DumperBase):
raise Exception("cdb does not support calling functions")
def nameForCoreId(self, id):
for dll in ['Utilsd4', 'Utils4']:
for dll in ['Utilsd', 'Utils']:
idName = cdbext.call('%s!Utils::nameForId(%d)' % (dll, id))
if idName is not None:
break
+115 -39
View File
@@ -196,22 +196,49 @@ static std::string getModuleName(ULONG64 module)
return std::string();
}
static std::unordered_map<std::string, PyType> &typeCache()
{
static std::unordered_map<std::string, PyType> cache;
return cache;
}
PyType::PyType(ULONG64 module, unsigned long typeId, const std::string &name, int tag)
: m_module(module)
, m_typeId(typeId)
: m_typeId(typeId)
, m_module(module)
, m_resolved(true)
, m_tag(tag)
{
m_name = SymbolGroupValue::stripClassPrefixes(name);
if (m_name.compare(0, 6, "union ") == 0)
m_name.erase(0, 6);
if (m_name == "<function> *")
m_name.erase(10);
if (!name.empty()) {
m_name = SymbolGroupValue::stripClassPrefixes(name);
if (m_name.compare(0, 6, "union ") == 0)
m_name.erase(0, 6);
if (m_name == "<function> *")
m_name.erase(10);
typeCache()[m_name] = *this;
if (debuggingTypeEnabled())
DebugPrint() << "create resolved '" << m_name << "'";
}
}
PyType::PyType(const std::string &name, ULONG64 module)
: m_module(module)
, m_name(name)
{
if (!m_name.empty()) {
m_name = SymbolGroupValue::stripClassPrefixes(name);
if (m_name.compare(0, 6, "union ") == 0)
m_name.erase(0, 6);
if (m_name == "<function> *")
m_name.erase(10);
}
if (debuggingTypeEnabled())
DebugPrint() << "create unresolved '" << m_name << "'";
}
std::string PyType::name(bool withModule) const
{
if (m_name.empty()) {
if (m_name.empty() && m_resolved.value_or(false)) {
auto symbols = ExtensionCommandContext::instance()->symbols();
ULONG size = 0;
symbols->GetTypeName(m_module, m_typeId, NULL, 0, &size);
@@ -223,6 +250,7 @@ std::string PyType::name(bool withModule) const
return std::string();
m_name = typeName;
typeCache()[m_name] = *this;
}
if (withModule && !isIntegralType(m_name) && !isFloatType(m_name)) {
@@ -238,7 +266,7 @@ std::string PyType::name(bool withModule) const
ULONG64 PyType::bitsize() const
{
if (!m_resolved)
if (!resolve())
return 0;
ULONG size = 0;
@@ -250,12 +278,7 @@ ULONG64 PyType::bitsize() const
int PyType::code() const
{
if (!m_resolved)
return TypeCodeUnresolvable;
if (m_tag < 0) {
// try to parse typeName
const std::string &typeName = name();
auto parseTypeName = [this](const std::string &typeName) -> std::optional<TypeCodes> {
if (typeName.empty())
return TypeCodeUnresolvable;
if (isPointerType(typeName))
@@ -268,14 +291,22 @@ int PyType::code() const
return TypeCodeIntegral;
if (isFloatType(typeName))
return TypeCodeFloat;
if (knownType(name(), 0) != KT_Unknown)
return TypeCodeStruct;
return std::nullopt;
};
if (!resolve())
return parseTypeName(name()).value_or(TypeCodeUnresolvable);
if (m_tag < 0) {
if (const std::optional<TypeCodes> typeCode = parseTypeName(name()))
return *typeCode;
IDebugSymbolGroup2 *sg = 0;
if (FAILED(ExtensionCommandContext::instance()->symbols()->CreateSymbolGroup2(&sg)))
return TypeCodeStruct;
if (knownType(name(), 0) != KT_Unknown)
return TypeCodeStruct;
const std::string helperValueName = SymbolGroupValue::pointedToSymbolName(0, name(true));
ULONG index = DEBUG_ANY_ID;
if (SUCCEEDED(sg->AddSymbol(helperValueName.c_str(), &index)))
@@ -323,6 +354,9 @@ std::string PyType::targetName() const
PyFields PyType::fields() const
{
if (!resolve())
return {};
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
PyFields fields;
if (isArrayType(name()) || isPointerType(name()))
@@ -343,19 +377,25 @@ PyFields PyType::fields() const
std::string PyType::module() const
{
if (!resolve())
return {};
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
ULONG size;
ULONG size = 0;
symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID, m_module, NULL, 0, &size);
if (size == 0)
return {};
std::string name(size - 1, '\0');
if (SUCCEEDED(symbols->GetModuleNameString(DEBUG_MODNAME_MODULE, DEBUG_ANY_ID,
m_module, &name[0], size, NULL))) {
return name;
}
return std::string();
return {};
}
ULONG64 PyType::moduleId() const
{
resolve();
return m_module;
}
@@ -408,37 +448,73 @@ PyType PyType::lookupType(const std::string &typeNameIn, ULONG64 module)
typeName.erase(0, 7);
const static std::regex typeNameRE("^[a-zA-Z_][a-zA-Z0-9_]*!?[a-zA-Z0-9_<>:, \\*\\&\\[\\]]*$");
if (!std::regex_match(typeName, typeNameRE))
return PyType();
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
ULONG typeId;
HRESULT result = S_FALSE;
if (module != 0 && !isIntegralType(typeName) && !isFloatType(typeName))
result = symbols->GetTypeId(module, typeName.c_str(), &typeId);
if (FAILED(result) || result == S_FALSE)
result = symbols->GetSymbolTypeId(typeName.c_str(), &typeId, &module);
if (FAILED(result))
return createUnresolvedType(typeName);
return PyType(module, typeId, typeName);
if (std::regex_match(typeName, typeNameRE))
return PyType(typeName, module);
return PyType();
}
PyType PyType::createUnresolvedType(const std::string &typeName)
bool PyType::resolve() const
{
PyType unresolvedType;
unresolvedType.m_name = typeName;
return unresolvedType;
if (m_resolved)
return *m_resolved;
if (!m_name.empty()) {
auto cacheIt = typeCache().find(m_name);
if (cacheIt != typeCache().end() && cacheIt->second.m_resolved.has_value()) {
if (debuggingTypeEnabled())
DebugPrint() << "found cached '" << m_name << "'";
// found a resolved cache entry use the ids of this entry
m_typeId = cacheIt->second.m_typeId;
m_module = cacheIt->second.m_module;
m_resolved = cacheIt->second.m_resolved;
} else {
if (debuggingTypeEnabled())
DebugPrint() << "resolve '" << m_name << "'";
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
ULONG typeId;
HRESULT result = S_FALSE;
if (m_module != 0 && !isIntegralType(m_name) && !isFloatType(m_name))
result = symbols->GetTypeId(m_module, m_name.c_str(), &typeId);
if (FAILED(result) || result == S_FALSE) {
ULONG64 module;
if (isIntegralType(m_name))
result = symbols->GetSymbolTypeId(m_name.c_str(), &typeId, &module);
if (FAILED(result) || result == S_FALSE) {
ULONG loaded = 0;
ULONG unloaded = 0;
symbols->GetNumberModules(&loaded, &unloaded);
ULONG moduleCount = loaded + unloaded;
for (ULONG moduleIndex = 0;
(FAILED(result) || result == S_FALSE) && moduleIndex < moduleCount;
++moduleIndex) {
symbols->GetModuleByIndex(moduleIndex, &module);
result = symbols->GetTypeId(module, m_name.c_str(), &typeId);
}
}
m_module = SUCCEEDED(result) ? module : 0;
}
m_typeId = SUCCEEDED(result) ? typeId : 0;
m_resolved = SUCCEEDED(result);
typeCache()[m_name] = *this;
}
}
if (!m_resolved)
m_resolved = false;
return *m_resolved;
}
unsigned long PyType::getTypeId() const
{
resolve();
return m_typeId;
}
bool PyType::isValid() const
{
return m_resolved;
return !m_name.empty() || m_resolved.has_value();
}
// Python interface implementation
+9 -7
View File
@@ -3,8 +3,9 @@
#pragma once
#include <string>
#include <memory>
#include <optional>
#include <string>
#include <Python.h>
@@ -16,6 +17,7 @@ public:
PyType() = default;
PyType(ULONG64 module, unsigned long typeId,
const std::string &name = std::string(), int tag = -1);
explicit PyType(const std::string &name, ULONG64 module = 0);
PyType(const PyType &other) = default;
std::string name(bool withModule = false) const;
@@ -48,13 +50,13 @@ public:
static PyType lookupType(const std::string &typeName, ULONG64 module = 0);
private:
static PyType createUnresolvedType(const std::string &typeName);
bool resolve() const;
unsigned long m_typeId = 0;
ULONG64 m_module = 0;
bool m_resolved = false;
mutable std::string m_name;
mutable int m_tag = -1;
mutable unsigned long m_typeId = 0;
mutable ULONG64 m_module = 0;
mutable std::optional<bool> m_resolved;
mutable std::string m_name;
mutable int m_tag = -1;
};
struct TypePythonObject