llvm-project/lldb/source/DataFormatters/FormatManager.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

803 lines
29 KiB
C++
Raw Normal View History

//===-- FormatManager.cpp -------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/DataFormatters/FormatManager.h"
#include "llvm/ADT/STLExtras.h"
#include "lldb/Core/Debugger.h"
#include "lldb/DataFormatters/FormattersHelpers.h"
#include "lldb/DataFormatters/LanguageCategory.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Language.h"
#include "lldb/Utility/Log.h"
using namespace lldb;
using namespace lldb_private;
using namespace lldb_private::formatters;
struct FormatInfo {
Format format;
const char format_char; // One or more format characters that can be used for
// this format.
const char *format_name; // Long format name that can be used to specify the
// current format
};
static constexpr FormatInfo g_format_infos[] = {
{eFormatDefault, '\0', "default"},
{eFormatBoolean, 'B', "boolean"},
{eFormatBinary, 'b', "binary"},
{eFormatBytes, 'y', "bytes"},
{eFormatBytesWithASCII, 'Y', "bytes with ASCII"},
{eFormatChar, 'c', "character"},
{eFormatCharPrintable, 'C', "printable character"},
{eFormatComplexFloat, 'F', "complex float"},
{eFormatCString, 's', "c-string"},
{eFormatDecimal, 'd', "decimal"},
{eFormatEnum, 'E', "enumeration"},
{eFormatHex, 'x', "hex"},
{eFormatHexUppercase, 'X', "uppercase hex"},
{eFormatFloat, 'f', "float"},
{eFormatOctal, 'o', "octal"},
{eFormatOSType, 'O', "OSType"},
{eFormatUnicode16, 'U', "unicode16"},
{eFormatUnicode32, '\0', "unicode32"},
{eFormatUnsigned, 'u', "unsigned decimal"},
{eFormatPointer, 'p', "pointer"},
{eFormatVectorOfChar, '\0', "char[]"},
{eFormatVectorOfSInt8, '\0', "int8_t[]"},
{eFormatVectorOfUInt8, '\0', "uint8_t[]"},
{eFormatVectorOfSInt16, '\0', "int16_t[]"},
{eFormatVectorOfUInt16, '\0', "uint16_t[]"},
{eFormatVectorOfSInt32, '\0', "int32_t[]"},
{eFormatVectorOfUInt32, '\0', "uint32_t[]"},
{eFormatVectorOfSInt64, '\0', "int64_t[]"},
{eFormatVectorOfUInt64, '\0', "uint64_t[]"},
{eFormatVectorOfFloat16, '\0', "float16[]"},
{eFormatVectorOfFloat32, '\0', "float32[]"},
{eFormatVectorOfFloat64, '\0', "float64[]"},
{eFormatVectorOfUInt128, '\0', "uint128_t[]"},
{eFormatComplexInteger, 'I', "complex integer"},
{eFormatCharArray, 'a', "character array"},
{eFormatAddressInfo, 'A', "address"},
{eFormatHexFloat, '\0', "hex float"},
{eFormatInstruction, 'i', "instruction"},
{eFormatVoid, 'v', "void"},
{eFormatUnicode8, 'u', "unicode8"},
};
static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) ==
kNumFormats,
"All formats must have a corresponding info entry.");
static uint32_t g_num_format_infos = llvm::array_lengthof(g_format_infos);
static bool GetFormatFromFormatChar(char format_char, Format &format) {
for (uint32_t i = 0; i < g_num_format_infos; ++i) {
if (g_format_infos[i].format_char == format_char) {
format = g_format_infos[i].format;
return true;
}
}
format = eFormatInvalid;
return false;
}
static bool GetFormatFromFormatName(const char *format_name,
bool partial_match_ok, Format &format) {
uint32_t i;
for (i = 0; i < g_num_format_infos; ++i) {
if (strcasecmp(g_format_infos[i].format_name, format_name) == 0) {
format = g_format_infos[i].format;
return true;
}
}
if (partial_match_ok) {
for (i = 0; i < g_num_format_infos; ++i) {
if (strcasestr(g_format_infos[i].format_name, format_name) ==
g_format_infos[i].format_name) {
format = g_format_infos[i].format;
return true;
}
}
}
format = eFormatInvalid;
return false;
}
void FormatManager::Changed() {
++m_last_revision;
m_format_cache.Clear();
std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
for (auto &iter : m_language_categories_map) {
if (iter.second)
iter.second->GetFormatCache().Clear();
}
}
bool FormatManager::GetFormatFromCString(const char *format_cstr,
bool partial_match_ok,
lldb::Format &format) {
bool success = false;
if (format_cstr && format_cstr[0]) {
if (format_cstr[1] == '\0') {
success = GetFormatFromFormatChar(format_cstr[0], format);
if (success)
return true;
}
success = GetFormatFromFormatName(format_cstr, partial_match_ok, format);
}
if (!success)
format = eFormatInvalid;
return success;
}
char FormatManager::GetFormatAsFormatChar(lldb::Format format) {
for (uint32_t i = 0; i < g_num_format_infos; ++i) {
if (g_format_infos[i].format == format)
return g_format_infos[i].format_char;
}
return '\0';
}
const char *FormatManager::GetFormatAsCString(Format format) {
if (format >= eFormatDefault && format < kNumFormats)
return g_format_infos[format].format_name;
return nullptr;
}
void FormatManager::EnableAllCategories() {
m_categories_map.EnableAllCategories();
std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
for (auto &iter : m_language_categories_map) {
if (iter.second)
iter.second->Enable();
}
}
void FormatManager::DisableAllCategories() {
m_categories_map.DisableAllCategories();
std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
for (auto &iter : m_language_categories_map) {
if (iter.second)
iter.second->Disable();
}
}
void FormatManager::GetPossibleMatches(
ValueObject &valobj, CompilerType compiler_type,
lldb::DynamicValueType use_dynamic, FormattersMatchVector &entries,
bool did_strip_ptr, bool did_strip_ref, bool did_strip_typedef,
bool root_level) {
compiler_type = compiler_type.GetTypeForFormatters();
ConstString type_name(compiler_type.GetTypeName());
if (valobj.GetBitfieldBitSize() > 0) {
StreamString sstring;
sstring.Printf("%s:%d", type_name.AsCString(), valobj.GetBitfieldBitSize());
ConstString bitfieldname(sstring.GetString());
entries.push_back(
{bitfieldname, did_strip_ptr, did_strip_ref, did_strip_typedef});
}
Introduce the concept of a "display name" for types Rationale: Pretty simply, the idea is that sometimes type names are way too long and contain way too many details for the average developer to care about. For instance, a plain ol' vector of int might be shown as std::__1::vector<int, std::__1::allocator<.... rather than the much simpler std::vector<int> form, which is what most developers would actually type in their code Proposed solution: Introduce a notion of "display name" and a corresponding API GetDisplayTypeName() to return such a crafted for visual representation type name Obviously, the display name and the fully qualified (or "true") name are not necessarily the same - that's the whole point LLDB could choose to pick the "display name" as its one true notion of a type name, and if somebody really needs the fully qualified version of it, let them deal with the problem Or, LLDB could rename what it currently calls the "type name" to be the "display name", and add new APIs for the fully qualified name, making the display name the default choice The choice that I am making here is that the type name will keep meaning the same, and people who want a type name suited for display will explicitly ask for one It is the less risky/disruptive choice - and it should eventually make it fairly obvious when someone is asking for the wrong type Caveats: - for now, GetDisplayTypeName() == GetTypeName(), there is no logic to produce customized display type names yet. - while the fully-qualified type name is still the main key to the kingdom of data formatters, if we start showing custom names to people, those should match formatters llvm-svn: 209072
2014-05-18 03:14:17 +08:00
if (!compiler_type.IsMeaninglessWithoutDynamicResolution()) {
entries.push_back(
{type_name, did_strip_ptr, did_strip_ref, did_strip_typedef});
[lldb] Let TypeSystemClang::GetDisplayTypeName remove anonymous and inline namespaces. Summary: Currently when printing data types we include implicit scopes such as inline namespaces or anonymous namespaces. This leads to command output like this (for `std::set<X>` with X being in an anonymous namespace): ``` (lldb) print my_set (std::__1::set<(anonymous namespace)::X, std::__1::less<(anonymous namespace)::X>, std::__1::allocator<(anonymous namespace)::X> >) $0 = size=0 {} ``` This patch removes all the implicit scopes when printing type names in TypeSystemClang::GetDisplayTypeName so that our output now looks like this: ``` (lldb) print my_set (std::set<X, std::less<X>, std::allocator<X> >) $0 = size=0 {} ``` As previously GetDisplayTypeName and GetTypeName had the same output we actually often used the two as if they are the same method (they were in fact using the same implementation), so this patch also fixes the places where we actually want the display type name and not the actual type name. Note that this doesn't touch the `GetTypeName` class that for example the data formatters use, so this patch is only changes the way we display types to the user. The full type name can also still be found when passing '-R' to see the raw output of a variable in case someone is somehow interested in that. Partly fixes rdar://problem/59292534 Reviewers: shafik, jingham Reviewed By: shafik Subscribers: christof, JDevlieghere, lldb-commits Tags: #lldb Differential Revision: https://reviews.llvm.org/D74478
2020-02-19 16:36:37 +08:00
ConstString display_type_name(compiler_type.GetTypeName());
if (display_type_name != type_name)
entries.push_back({display_type_name, did_strip_ptr,
did_strip_ref, did_strip_typedef});
}
Introduce the concept of a "display name" for types Rationale: Pretty simply, the idea is that sometimes type names are way too long and contain way too many details for the average developer to care about. For instance, a plain ol' vector of int might be shown as std::__1::vector<int, std::__1::allocator<.... rather than the much simpler std::vector<int> form, which is what most developers would actually type in their code Proposed solution: Introduce a notion of "display name" and a corresponding API GetDisplayTypeName() to return such a crafted for visual representation type name Obviously, the display name and the fully qualified (or "true") name are not necessarily the same - that's the whole point LLDB could choose to pick the "display name" as its one true notion of a type name, and if somebody really needs the fully qualified version of it, let them deal with the problem Or, LLDB could rename what it currently calls the "type name" to be the "display name", and add new APIs for the fully qualified name, making the display name the default choice The choice that I am making here is that the type name will keep meaning the same, and people who want a type name suited for display will explicitly ask for one It is the less risky/disruptive choice - and it should eventually make it fairly obvious when someone is asking for the wrong type Caveats: - for now, GetDisplayTypeName() == GetTypeName(), there is no logic to produce customized display type names yet. - while the fully-qualified type name is still the main key to the kingdom of data formatters, if we start showing custom names to people, those should match formatters llvm-svn: 209072
2014-05-18 03:14:17 +08:00
for (bool is_rvalue_ref = true, j = true;
j && compiler_type.IsReferenceType(nullptr, &is_rvalue_ref); j = false) {
CompilerType non_ref_type = compiler_type.GetNonReferenceType();
GetPossibleMatches(
valobj, non_ref_type,
use_dynamic, entries, did_strip_ptr, true, did_strip_typedef);
if (non_ref_type.IsTypedefType()) {
CompilerType deffed_referenced_type = non_ref_type.GetTypedefedType();
deffed_referenced_type =
is_rvalue_ref ? deffed_referenced_type.GetRValueReferenceType()
: deffed_referenced_type.GetLValueReferenceType();
GetPossibleMatches(
valobj, deffed_referenced_type,
use_dynamic, entries, did_strip_ptr, did_strip_ref,
true); // this is not exactly the usual meaning of stripping typedefs
}
}
if (compiler_type.IsPointerType()) {
CompilerType non_ptr_type = compiler_type.GetPointeeType();
GetPossibleMatches(
valobj, non_ptr_type,
use_dynamic, entries, true, did_strip_ref, did_strip_typedef);
if (non_ptr_type.IsTypedefType()) {
CompilerType deffed_pointed_type =
non_ptr_type.GetTypedefedType().GetPointerType();
const bool stripped_typedef = true;
GetPossibleMatches(
valobj, deffed_pointed_type,
use_dynamic, entries, did_strip_ptr, did_strip_ref,
stripped_typedef); // this is not exactly the usual meaning of
// stripping typedefs
}
}
// For arrays with typedef-ed elements, we add a candidate with the typedef
// stripped.
uint64_t array_size;
if (compiler_type.IsArrayType(nullptr, &array_size, nullptr)) {
ExecutionContext exe_ctx(valobj.GetExecutionContextRef());
CompilerType element_type = compiler_type.GetArrayElementType(
exe_ctx.GetBestExecutionContextScope());
if (element_type.IsTypedefType()) {
// Get the stripped element type and compute the stripped array type
// from it.
CompilerType deffed_array_type =
element_type.GetTypedefedType().GetArrayType(array_size);
const bool stripped_typedef = true;
GetPossibleMatches(
valobj, deffed_array_type,
use_dynamic, entries, did_strip_ptr, did_strip_ref,
stripped_typedef); // this is not exactly the usual meaning of
// stripping typedefs
}
}
for (lldb::LanguageType language_type :
GetCandidateLanguages(valobj.GetObjectRuntimeLanguage())) {
if (Language *language = Language::FindPlugin(language_type)) {
for (ConstString candidate :
language->GetPossibleFormattersMatches(valobj, use_dynamic)) {
entries.push_back(
{candidate,
did_strip_ptr, did_strip_ref, did_strip_typedef});
}
}
}
// try to strip typedef chains
if (compiler_type.IsTypedefType()) {
CompilerType deffed_type = compiler_type.GetTypedefedType();
GetPossibleMatches(
valobj, deffed_type,
use_dynamic, entries, did_strip_ptr, did_strip_ref, true);
}
if (root_level) {
do {
if (!compiler_type.IsValid())
break;
CompilerType unqual_compiler_ast_type =
compiler_type.GetFullyUnqualifiedType();
if (!unqual_compiler_ast_type.IsValid())
break;
if (unqual_compiler_ast_type.GetOpaqueQualType() !=
compiler_type.GetOpaqueQualType())
GetPossibleMatches(valobj, unqual_compiler_ast_type,
use_dynamic, entries, did_strip_ptr, did_strip_ref,
did_strip_typedef);
} while (false);
// if all else fails, go to static type
if (valobj.IsDynamic()) {
lldb::ValueObjectSP static_value_sp(valobj.GetStaticValue());
if (static_value_sp)
GetPossibleMatches(
Final bit of type system cleanup that abstracts declaration contexts into lldb_private::CompilerDeclContext and renames ClangType to CompilerType in many accessors and functions. Create a new "lldb_private::CompilerDeclContext" class that will replace all direct uses of "clang::DeclContext" when used in compiler agnostic code, yet still allow for conversion to clang::DeclContext subclasses by clang specific code. This completes the abstraction of type parsing by removing all "clang::" references from the SymbolFileDWARF. The new "lldb_private::CompilerDeclContext" class abstracts decl contexts found in compiler type systems so they can be used in internal API calls. The TypeSystem is required to support CompilerDeclContexts with new pure virtual functions that start with "DeclContext" in the member function names. Converted all code that used lldb_private::ClangNamespaceDecl over to use the new CompilerDeclContext class and removed the ClangNamespaceDecl.cpp and ClangNamespaceDecl.h files. Removed direct use of clang APIs from SBType and now use the abstract type systems to correctly explore types. Bulk renames for things that used to return a ClangASTType which is now CompilerType: "Type::GetClangFullType()" to "Type::GetFullCompilerType()" "Type::GetClangLayoutType()" to "Type::GetLayoutCompilerType()" "Type::GetClangForwardType()" to "Type::GetForwardCompilerType()" "Value::GetClangType()" to "Value::GetCompilerType()" "Value::SetClangType (const CompilerType &)" to "Value::SetCompilerType (const CompilerType &)" "ValueObject::GetClangType ()" to "ValueObject::GetCompilerType()" many more renames that are similar. llvm-svn: 245905
2015-08-25 07:46:31 +08:00
*static_value_sp.get(), static_value_sp->GetCompilerType(),
use_dynamic, entries, did_strip_ptr, did_strip_ref,
did_strip_typedef, true);
}
}
}
lldb::TypeFormatImplSP
FormatManager::GetFormatForType(lldb::TypeNameSpecifierImplSP type_sp) {
if (!type_sp)
return lldb::TypeFormatImplSP();
lldb::TypeFormatImplSP format_chosen_sp;
uint32_t num_categories = m_categories_map.GetCount();
lldb::TypeCategoryImplSP category_sp;
uint32_t prio_category = UINT32_MAX;
for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
category_sp = GetCategoryAtIndex(category_id);
if (!category_sp->IsEnabled())
continue;
lldb::TypeFormatImplSP format_current_sp =
category_sp->GetFormatForType(type_sp);
if (format_current_sp &&
(format_chosen_sp.get() == nullptr ||
(prio_category > category_sp->GetEnabledPosition()))) {
prio_category = category_sp->GetEnabledPosition();
format_chosen_sp = format_current_sp;
}
}
return format_chosen_sp;
}
lldb::TypeSummaryImplSP
FormatManager::GetSummaryForType(lldb::TypeNameSpecifierImplSP type_sp) {
if (!type_sp)
return lldb::TypeSummaryImplSP();
lldb::TypeSummaryImplSP summary_chosen_sp;
uint32_t num_categories = m_categories_map.GetCount();
lldb::TypeCategoryImplSP category_sp;
uint32_t prio_category = UINT32_MAX;
for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
category_sp = GetCategoryAtIndex(category_id);
if (!category_sp->IsEnabled())
continue;
lldb::TypeSummaryImplSP summary_current_sp =
category_sp->GetSummaryForType(type_sp);
if (summary_current_sp &&
(summary_chosen_sp.get() == nullptr ||
(prio_category > category_sp->GetEnabledPosition()))) {
prio_category = category_sp->GetEnabledPosition();
summary_chosen_sp = summary_current_sp;
}
}
return summary_chosen_sp;
}
lldb::TypeFilterImplSP
FormatManager::GetFilterForType(lldb::TypeNameSpecifierImplSP type_sp) {
if (!type_sp)
return lldb::TypeFilterImplSP();
lldb::TypeFilterImplSP filter_chosen_sp;
uint32_t num_categories = m_categories_map.GetCount();
lldb::TypeCategoryImplSP category_sp;
uint32_t prio_category = UINT32_MAX;
for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
category_sp = GetCategoryAtIndex(category_id);
if (!category_sp->IsEnabled())
continue;
lldb::TypeFilterImplSP filter_current_sp(
(TypeFilterImpl *)category_sp->GetFilterForType(type_sp).get());
if (filter_current_sp &&
(filter_chosen_sp.get() == nullptr ||
(prio_category > category_sp->GetEnabledPosition()))) {
prio_category = category_sp->GetEnabledPosition();
filter_chosen_sp = filter_current_sp;
}
}
return filter_chosen_sp;
}
lldb::ScriptedSyntheticChildrenSP
FormatManager::GetSyntheticForType(lldb::TypeNameSpecifierImplSP type_sp) {
if (!type_sp)
return lldb::ScriptedSyntheticChildrenSP();
lldb::ScriptedSyntheticChildrenSP synth_chosen_sp;
uint32_t num_categories = m_categories_map.GetCount();
lldb::TypeCategoryImplSP category_sp;
uint32_t prio_category = UINT32_MAX;
for (uint32_t category_id = 0; category_id < num_categories; category_id++) {
category_sp = GetCategoryAtIndex(category_id);
if (!category_sp->IsEnabled())
continue;
lldb::ScriptedSyntheticChildrenSP synth_current_sp(
(ScriptedSyntheticChildren *)category_sp->GetSyntheticForType(type_sp)
.get());
if (synth_current_sp &&
(synth_chosen_sp.get() == nullptr ||
(prio_category > category_sp->GetEnabledPosition()))) {
prio_category = category_sp->GetEnabledPosition();
synth_chosen_sp = synth_current_sp;
}
}
return synth_chosen_sp;
}
void FormatManager::ForEachCategory(TypeCategoryMap::ForEachCallback callback) {
m_categories_map.ForEach(callback);
std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
for (const auto &entry : m_language_categories_map) {
if (auto category_sp = entry.second->GetCategory()) {
if (!callback(category_sp))
break;
}
}
}
lldb::TypeCategoryImplSP
FormatManager::GetCategory(ConstString category_name, bool can_create) {
if (!category_name)
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-07 03:20:51 +08:00
return GetCategory(m_default_category_name);
lldb::TypeCategoryImplSP category;
if (m_categories_map.Get(category_name, category))
return category;
if (!can_create)
return lldb::TypeCategoryImplSP();
m_categories_map.Add(
category_name,
lldb::TypeCategoryImplSP(new TypeCategoryImpl(this, category_name)));
Redesign of the interaction between Python and frozen objects: - introduced two new classes ValueObjectConstResultChild and ValueObjectConstResultImpl: the first one is a ValueObjectChild obtained from a ValueObjectConstResult, the second is a common implementation backend for VOCR and VOCRCh of method calls meant to read through pointers stored in frozen objects ; now such reads transparently move from host to target as required - as a consequence of the above, removed code that made target-memory copies of expression results in several places throughout LLDB, and also removed code that enabled to recognize an expression result VO as such - introduced a new GetPointeeData() method in ValueObject that lets you read a given amount of objects of type T from a VO representing a T* or T[], and doing dereferences transparently in private layer it returns a DataExtractor ; in public layer it returns an instance of a newly created lldb::SBData - as GetPointeeData() does the right thing for both frozen and non-frozen ValueObject's, reimplemented ReadPointedString() to use it en lieu of doing the raw read itself - introduced a new GetData() method in ValueObject that lets you get a copy of the data that backs the ValueObject (for pointers, this returns the address without any previous dereferencing steps ; for arrays it actually reads the whole chunk of memory) in public layer this returns an SBData, just like GetPointeeData() - introduced a new CreateValueFromData() method in SBValue that lets you create a new SBValue from a chunk of data wrapped in an SBData the limitation to remember for this kind of SBValue is that they have no address: extracting the address-of for these objects (with any of GetAddress(), GetLoadAddress() and AddressOf()) will return invalid values - added several tests to check that "p"-ing objects (STL classes, char* and char[]) will do the right thing Solved a bug where global pointers to global variables were not dereferenced correctly for display New target setting "max-string-summary-length" gives the maximum number of characters to show in a string when summarizing it, instead of the hardcoded 128 Solved a bug where the summary for char[] and char* would not be shown if the ValueObject's were dumped via the "p" command Removed m_pointers_point_to_load_addrs from ValueObject. Introduced a new m_address_type_of_children, which each ValueObject can set to tell the address type of any pointers and/or references it creates. In the current codebase, this is load address most of the time (the only notable exception being file addresses that generate file address children UNLESS we have a live process) Updated help text for summary-string Fixed an issue in STL formatters where std::stlcontainer::iterator would match the container's synthetic children providers Edited the syntax and help for some commands to have proper argument types llvm-svn: 139160
2011-09-07 03:20:51 +08:00
return GetCategory(category_name);
}
lldb::Format FormatManager::GetSingleItemFormat(lldb::Format vector_format) {
switch (vector_format) {
case eFormatVectorOfChar:
return eFormatCharArray;
case eFormatVectorOfSInt8:
case eFormatVectorOfSInt16:
case eFormatVectorOfSInt32:
case eFormatVectorOfSInt64:
return eFormatDecimal;
case eFormatVectorOfUInt8:
case eFormatVectorOfUInt16:
case eFormatVectorOfUInt32:
case eFormatVectorOfUInt64:
case eFormatVectorOfUInt128:
return eFormatHex;
case eFormatVectorOfFloat16:
case eFormatVectorOfFloat32:
case eFormatVectorOfFloat64:
return eFormatFloat;
default:
return lldb::eFormatInvalid;
}
While tracking down memory consumption issue a few things were needed: the ability to dump more information about modules in "target modules list". We can now dump the shared pointer reference count for modules, the pointer to the module itself (in case performance tools can help track down who has references to said pointer), and the modification time. Added "target delete [target-idx ...]" to be able to delete targets when they are no longer needed. This will help track down memory usage issues and help to resolve when module ref counts keep getting incremented. If the command gets no arguments, the currently selected target will be deleted. If any arguments are given, they must all be valid target indexes (use the "target list" command to get the current target indexes). Took care of a bunch of "no newline at end of file" warnings. TimeValue objects can now dump their time to a lldb_private::Stream object. Modified the "target modules list --global" command to not error out if there are no targets since it doesn't require a target. Fixed an issue in the MacOSX DYLD dynamic loader plug-in where if a shared library was updated on disk, we would keep using the older one, even if it was updated. Don't allow the ModuleList::GetSharedModule(...) to return an empty module. Previously we could specify a valid path on disc to a module, and specify an architecture that wasn't contained in that module and get a shared pointer to a module that wouldn't be able to return an object file or a symbol file. We now make sure an object file can be extracted prior to adding the shared pointer to the module to get added to the shared list. llvm-svn: 137196
2011-08-10 10:10:13 +08:00
}
bool FormatManager::ShouldPrintAsOneLiner(ValueObject &valobj) {
// if settings say no oneline whatsoever
if (valobj.GetTargetSP().get() &&
!valobj.GetTargetSP()->GetDebugger().GetAutoOneLineSummaries())
return false; // then don't oneline
// if this object has a summary, then ask the summary
if (valobj.GetSummaryFormat().get() != nullptr)
return valobj.GetSummaryFormat()->IsOneLiner();
// no children, no party
if (valobj.GetNumChildren() == 0)
return false;
// ask the type if it has any opinion about this eLazyBoolCalculate == no
// opinion; other values should be self explanatory
CompilerType compiler_type(valobj.GetCompilerType());
if (compiler_type.IsValid()) {
switch (compiler_type.ShouldPrintAsOneLiner(&valobj)) {
case eLazyBoolNo:
return false;
case eLazyBoolYes:
return true;
case eLazyBoolCalculate:
break;
}
}
size_t total_children_name_len = 0;
for (size_t idx = 0; idx < valobj.GetNumChildren(); idx++) {
bool is_synth_val = false;
ValueObjectSP child_sp(valobj.GetChildAtIndex(idx, true));
// something is wrong here - bail out
if (!child_sp)
return false;
// also ask the child's type if it has any opinion
CompilerType child_compiler_type(child_sp->GetCompilerType());
if (child_compiler_type.IsValid()) {
switch (child_compiler_type.ShouldPrintAsOneLiner(child_sp.get())) {
case eLazyBoolYes:
// an opinion of yes is only binding for the child, so keep going
case eLazyBoolCalculate:
break;
case eLazyBoolNo:
// but if the child says no, then it's a veto on the whole thing
return false;
}
}
// if we decided to define synthetic children for a type, we probably care
// enough to show them, but avoid nesting children in children
if (child_sp->GetSyntheticChildren().get() != nullptr) {
ValueObjectSP synth_sp(child_sp->GetSyntheticValue());
// wait.. wat? just get out of here..
if (!synth_sp)
return false;
// but if we only have them to provide a value, keep going
if (!synth_sp->MightHaveChildren() &&
synth_sp->DoesProvideSyntheticValue())
is_synth_val = true;
else
return false;
}
total_children_name_len += child_sp->GetName().GetLength();
// 50 itself is a "randomly" chosen number - the idea is that
// overly long structs should not get this treatment
// FIXME: maybe make this a user-tweakable setting?
if (total_children_name_len > 50)
return false;
// if a summary is there..
if (child_sp->GetSummaryFormat()) {
// and it wants children, then bail out
if (child_sp->GetSummaryFormat()->DoesPrintChildren(child_sp.get()))
return false;
}
// if this child has children..
if (child_sp->GetNumChildren()) {
// ...and no summary...
// (if it had a summary and the summary wanted children, we would have
// bailed out anyway
// so this only makes us bail out if this has no summary and we would
// then print children)
if (!child_sp->GetSummaryFormat() && !is_synth_val) // but again only do
// that if not a
// synthetic valued
// child
return false; // then bail out
}
}
return true;
}
ConstString FormatManager::GetTypeForCache(ValueObject &valobj,
lldb::DynamicValueType use_dynamic) {
ValueObjectSP valobj_sp = valobj.GetQualifiedRepresentationIfAvailable(
use_dynamic, valobj.IsSynthetic());
if (valobj_sp && valobj_sp->GetCompilerType().IsValid()) {
if (!valobj_sp->GetCompilerType().IsMeaninglessWithoutDynamicResolution())
return valobj_sp->GetQualifiedTypeName();
}
return ConstString();
}
std::vector<lldb::LanguageType>
FormatManager::GetCandidateLanguages(lldb::LanguageType lang_type) {
switch (lang_type) {
case lldb::eLanguageTypeC:
case lldb::eLanguageTypeC89:
case lldb::eLanguageTypeC99:
case lldb::eLanguageTypeC11:
case lldb::eLanguageTypeC_plus_plus:
case lldb::eLanguageTypeC_plus_plus_03:
case lldb::eLanguageTypeC_plus_plus_11:
case lldb::eLanguageTypeC_plus_plus_14:
return {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC};
default:
return {lang_type};
}
llvm_unreachable("Fully covered switch");
}
LanguageCategory *
FormatManager::GetCategoryForLanguage(lldb::LanguageType lang_type) {
std::lock_guard<std::recursive_mutex> guard(m_language_categories_mutex);
auto iter = m_language_categories_map.find(lang_type),
end = m_language_categories_map.end();
if (iter != end)
return iter->second.get();
LanguageCategory *lang_category = new LanguageCategory(lang_type);
m_language_categories_map[lang_type] =
LanguageCategory::UniquePointer(lang_category);
return lang_category;
}
template <typename ImplSP>
ImplSP FormatManager::GetHardcoded(FormattersMatchData &match_data) {
ImplSP retval_sp;
for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
if (lang_category->GetHardcoded(*this, match_data, retval_sp))
return retval_sp;
}
}
return retval_sp;
}
template <typename ImplSP>
ImplSP FormatManager::Get(ValueObject &valobj,
lldb::DynamicValueType use_dynamic) {
FormattersMatchData match_data(valobj, use_dynamic);
if (ImplSP retval_sp = GetCached<ImplSP>(match_data))
return retval_sp;
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
LLDB_LOGF(log, "[%s] Search failed. Giving language a chance.", __FUNCTION__);
for (lldb::LanguageType lang_type : match_data.GetCandidateLanguages()) {
if (LanguageCategory *lang_category = GetCategoryForLanguage(lang_type)) {
ImplSP retval_sp;
if (lang_category->Get(match_data, retval_sp))
if (retval_sp) {
LLDB_LOGF(log, "[%s] Language search success. Returning.",
__FUNCTION__);
return retval_sp;
}
}
}
LLDB_LOGF(log, "[%s] Search failed. Giving hardcoded a chance.",
__FUNCTION__);
return GetHardcoded<ImplSP>(match_data);
}
template <typename ImplSP>
ImplSP FormatManager::GetCached(FormattersMatchData &match_data) {
ImplSP retval_sp;
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_DATAFORMATTERS));
if (match_data.GetTypeForCache()) {
LLDB_LOGF(log, "\n\n[%s] Looking into cache for type %s", __FUNCTION__,
match_data.GetTypeForCache().AsCString("<invalid>"));
if (m_format_cache.Get(match_data.GetTypeForCache(), retval_sp)) {
if (log) {
LLDB_LOGF(log, "[%s] Cache search success. Returning.", __FUNCTION__);
LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
m_format_cache.GetCacheHits(),
m_format_cache.GetCacheMisses());
}
return retval_sp;
}
LLDB_LOGF(log, "[%s] Cache search failed. Going normal route",
__FUNCTION__);
}
m_categories_map.Get(match_data, retval_sp);
if (match_data.GetTypeForCache() && (!retval_sp || !retval_sp->NonCacheable())) {
LLDB_LOGF(log, "[%s] Caching %p for type %s", __FUNCTION__,
static_cast<void *>(retval_sp.get()),
match_data.GetTypeForCache().AsCString("<invalid>"));
m_format_cache.Set(match_data.GetTypeForCache(), retval_sp);
}
LLDB_LOGV(log, "Cache hits: {0} - Cache Misses: {1}",
m_format_cache.GetCacheHits(), m_format_cache.GetCacheMisses());
return retval_sp;
}
lldb::TypeFormatImplSP
FormatManager::GetFormat(ValueObject &valobj,
lldb::DynamicValueType use_dynamic) {
return Get<lldb::TypeFormatImplSP>(valobj, use_dynamic);
}
lldb::TypeSummaryImplSP
FormatManager::GetSummaryFormat(ValueObject &valobj,
lldb::DynamicValueType use_dynamic) {
return Get<lldb::TypeSummaryImplSP>(valobj, use_dynamic);
}
lldb::SyntheticChildrenSP
FormatManager::GetSyntheticChildren(ValueObject &valobj,
lldb::DynamicValueType use_dynamic) {
return Get<lldb::SyntheticChildrenSP>(valobj, use_dynamic);
}
FormatManager::FormatManager()
: m_last_revision(0), m_format_cache(), m_language_categories_mutex(),
m_language_categories_map(), m_named_summaries_map(this),
m_categories_map(this), m_default_category_name(ConstString("default")),
m_system_category_name(ConstString("system")),
m_vectortypes_category_name(ConstString("VectorTypes")) {
LoadSystemFormatters();
LoadVectorFormatters();
EnableCategory(m_vectortypes_category_name, TypeCategoryMap::Last,
lldb::eLanguageTypeObjC_plus_plus);
EnableCategory(m_system_category_name, TypeCategoryMap::Last,
lldb::eLanguageTypeObjC_plus_plus);
}
void FormatManager::LoadSystemFormatters() {
TypeSummaryImpl::Flags string_flags;
string_flags.SetCascades(true)
.SetSkipPointers(true)
.SetSkipReferences(false)
.SetDontShowChildren(true)
.SetDontShowValue(false)
.SetShowMembersOneLiner(false)
.SetHideItemNames(false);
TypeSummaryImpl::Flags string_array_flags;
string_array_flags.SetCascades(true)
.SetSkipPointers(true)
.SetSkipReferences(false)
.SetDontShowChildren(true)
.SetDontShowValue(true)
.SetShowMembersOneLiner(false)
.SetHideItemNames(false);
lldb::TypeSummaryImplSP string_format(
new StringSummaryFormat(string_flags, "${var%s}"));
lldb::TypeSummaryImplSP string_array_format(
new StringSummaryFormat(string_array_flags, "${var%char[]}"));
RegularExpression any_size_char_arr(llvm::StringRef("char ?\\[[0-9]+\\]"));
TypeCategoryImpl::SharedPointer sys_category_sp =
GetCategory(m_system_category_name);
sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("char *"),
string_format);
sys_category_sp->GetTypeSummariesContainer()->Add(
ConstString("unsigned char *"), string_format);
sys_category_sp->GetRegexTypeSummariesContainer()->Add(
std::move(any_size_char_arr), string_array_format);
lldb::TypeSummaryImplSP ostype_summary(
new StringSummaryFormat(TypeSummaryImpl::Flags()
.SetCascades(false)
.SetSkipPointers(true)
.SetSkipReferences(true)
.SetDontShowChildren(true)
.SetDontShowValue(false)
.SetShowMembersOneLiner(false)
.SetHideItemNames(false),
"${var%O}"));
sys_category_sp->GetTypeSummariesContainer()->Add(ConstString("OSType"),
ostype_summary);
TypeFormatImpl::Flags fourchar_flags;
fourchar_flags.SetCascades(true).SetSkipPointers(true).SetSkipReferences(
true);
AddFormat(sys_category_sp, lldb::eFormatOSType, ConstString("FourCharCode"),
fourchar_flags);
}
void FormatManager::LoadVectorFormatters() {
TypeCategoryImpl::SharedPointer vectors_category_sp =
GetCategory(m_vectortypes_category_name);
TypeSummaryImpl::Flags vector_flags;
vector_flags.SetCascades(true)
.SetSkipPointers(true)
.SetSkipReferences(false)
.SetDontShowChildren(true)
.SetDontShowValue(false)
.SetShowMembersOneLiner(true)
.SetHideItemNames(true);
AddStringSummary(vectors_category_sp, "${var.uint128}",
ConstString("builtin_type_vec128"), vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("float[4]"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("int32_t[4]"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("int16_t[8]"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vDouble"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vFloat"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vSInt8"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vSInt16"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vSInt32"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vUInt16"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vUInt8"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vUInt16"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vUInt32"),
vector_flags);
AddStringSummary(vectors_category_sp, "", ConstString("vBool32"),
vector_flags);
}