diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 58a05352537f30544fac4657b3940c34d8faa11f..f13edc3496cc2162fbba06d31df455c69ba02c0f 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -597,6 +597,16 @@ public: virtual bool IsSynthetic() { return false; } + // OHOS_LOCAL begin + void SetSyntheticFrontend(SyntheticChildrenFrontEnd* synth_front) { + m_synthetic_frontend = synth_front; + } + + SyntheticChildrenFrontEnd* GetSyntheticFrontend() const { + return m_synthetic_frontend; + } + // OHOS_LOCAL end + lldb::ValueObjectSP GetQualifiedRepresentationIfAvailable(lldb::DynamicValueType dynValue, bool synthValue); @@ -904,6 +914,11 @@ protected: /// Unique identifier for every value object. UserID m_id; + // OHOS_LOCAL begin + // If frontend exist - we may try to update our value through it + SyntheticChildrenFrontEnd* m_synthetic_frontend = nullptr; + // OHOS_LOCAL end + // Utility class for initializing all bitfields in ValueObject's constructors. // FIXME: This could be done via default initializers once we have C++20. struct Bitflags { diff --git a/lldb/include/lldb/DataFormatters/TypeSynthetic.h b/lldb/include/lldb/DataFormatters/TypeSynthetic.h index 890a6eb4f448763ac62b8a9ff3852babe3eb7203..8ec26f783f2c185324f1aaef58723a9ebf1ece2b 100644 --- a/lldb/include/lldb/DataFormatters/TypeSynthetic.h +++ b/lldb/include/lldb/DataFormatters/TypeSynthetic.h @@ -34,7 +34,11 @@ protected: public: SyntheticChildrenFrontEnd(ValueObject &backend) - : m_backend(backend), m_valid(true) {} + : m_backend(backend), m_valid(true) { + // OHOS_LOCAL begin + backend.SetSyntheticFrontend(this); + // OHOS_LOCAL end + } virtual ~SyntheticChildrenFrontEnd() = default; @@ -75,6 +79,12 @@ public: // display purposes virtual ConstString GetSyntheticTypeName() { return ConstString(); } + // OHOS_LOCAL begin + virtual bool SetValueFromCString(const char *value_str, Status &error) { + return false; + } + // OHOS_LOCAL end + typedef std::shared_ptr SharedPointer; typedef std::unique_ptr AutoPointer; diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index d80139ab184005c0dcac7056f97131b5bac809a5..a1bd1145ec6414d862fca946879cb941a70807fe 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -1481,7 +1481,7 @@ bool ValueObject::SetValueFromCString(const char *value_str, Status &error) { if (value_type == Value::ValueType::Scalar) { // If the value is already a scalar, then let the scalar change itself: m_value.GetScalar().SetValueFromCString(value_str, encoding, byte_size); - } else if (byte_size <= 16) { + } else if (byte_size <= 16 && encoding != eEncodingInvalid) { // OHOS_LOCAL // If the value fits in a scalar, then make a new scalar and again let the // scalar code do the conversion, then figure out where to put the new // value. @@ -1536,8 +1536,14 @@ bool ValueObject::SetValueFromCString(const char *value_str, Status &error) { return false; } } else { + // OHOS_LOCAL begin // We don't support setting things bigger than a scalar at present. - error.SetErrorString("unable to write aggregate data type"); + // But maybe our frontend knows how to update the value. + SyntheticChildrenFrontEnd* frontend = GetSyntheticFrontend(); + if (frontend) { + return frontend->SetValueFromCString(value_str, error); + } + // OHOS_LOCAL end return false; } diff --git a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt index 80135daf4cfd71f6fe88f0bb8bb85a817b57fc29..65c04b3ef77dd0eda123c9250ed7487543e9f3d9 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt +++ b/lldb/source/Plugins/Language/CPlusPlus/CMakeLists.txt @@ -12,6 +12,9 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN LibCxxMap.cpp LibCxxQueue.cpp LibCxxSpan.cpp + # OHOS_LOCAL begin + LibCxxString.cpp + # OHOS_LOCAL begin LibCxxTuple.cpp LibCxxUnorderedMap.cpp LibCxxVariant.cpp diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp index 23ce1654fb83fabd3b07d919adfffdfce35d9064..ccf0eff45a4e4aa2d91c947e4c9302ade5a82aa8 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp @@ -573,53 +573,62 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { .SetShowMembersOneLiner(false) .SetHideItemNames(false); + // OHOS_LOCAL begin + static ConstString std_string_regex { + "^std::__[[:alnum:]]+::string$" + "|" + "^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$" + "|" + "^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$" + }; + + static ConstString std_u16string_regex { + "^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$" + }; + + static ConstString std_u32string_regex { + "^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$" + }; + + static ConstString std_wstring_regex { + "^std::__[[:alnum:]]+::wstring$" + "|" + "^std::__[[:alnum:]]+::basic_string, " + "std::__[[:alnum:]]+::allocator >$" + }; + // OHOS_LOCAL end + AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderASCII, "std::string summary provider", - ConstString("^std::__[[:alnum:]]+::string$"), stl_summary_flags, + std_string_regex, stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, - lldb_private::formatters::LibcxxStringSummaryProviderASCII, - "std::string summary provider", - ConstString("^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), - stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, - lldb_private::formatters::LibcxxStringSummaryProviderASCII, - "std::string summary provider", - ConstString("^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), - stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF16, "std::u16string summary provider", - ConstString("^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), + std_u16string_regex, stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringSummaryProviderUTF32, "std::u32string summary provider", - ConstString("^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), + std_u32string_regex, stl_summary_flags, true); AddCXXSummary( cpp_category_sp, lldb_private::formatters::LibcxxWStringSummaryProvider, "std::wstring summary provider", - ConstString("^std::__[[:alnum:]]+::wstring$"), stl_summary_flags, true); - AddCXXSummary(cpp_category_sp, - lldb_private::formatters::LibcxxWStringSummaryProvider, - "std::wstring summary provider", - ConstString("^std::__[[:alnum:]]+::basic_string, " - "std::__[[:alnum:]]+::allocator >$"), - stl_summary_flags, true); + std_wstring_regex, stl_summary_flags, true); AddCXXSummary(cpp_category_sp, lldb_private::formatters::LibcxxStringViewSummaryProviderASCII, @@ -919,6 +928,36 @@ static void LoadLibCxxFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { "std::unordered_map iterator synthetic children", ConstString("^std::__[[:alnum:]]+::__hash_map_(const_)?iterator<.+>$"), stl_synth_flags, true); + + // OHOS_LOCAL begin + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters:: + LibcxxStdStringSyntheticFrontEndCreator, + "std::string synthetic children", + std_string_regex, stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters:: + LibcxxStdU16StringSyntheticFrontEndCreator, + "std::u16string synthetic children", + std_u16string_regex, stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters:: + LibcxxStdU32StringSyntheticFrontEndCreator, + "std::u32string synthetic children", + std_u32string_regex, stl_synth_flags, true); + + AddCXXSynthetic( + cpp_category_sp, + lldb_private::formatters:: + LibcxxStdWStringSyntheticFrontEndCreator, + "std::wstring synthetic children", + std_wstring_regex, stl_synth_flags, true); + // OHOS_LOCAL end } static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) { diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp index 3b04b3a1b2acc96d93088a524474240330e73441..5e7b5a2c7df4c8994a11210cf07378652f965a76 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "LibCxx.h" +#include "LibCxxStringInfoExtractor.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/FormatEntity.h" @@ -713,111 +714,6 @@ bool lldb_private::formatters::LibcxxContainerSummaryProvider( nullptr, nullptr, &valobj, false, false); } -/// The field layout in a libc++ string (cap, side, data or data, size, cap). -namespace { -enum class StringLayout { CSD, DSC }; -} - -/// Determine the size in bytes of \p valobj (a libc++ std::string object) and -/// extract its data payload. Return the size + payload pair. -// TODO: Support big-endian architectures. -static llvm::Optional> -ExtractLibcxxStringInfo(ValueObject &valobj) { - ValueObjectSP valobj_r_sp = - valobj.GetChildMemberWithName(ConstString("__r_"), /*can_create=*/true); - if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) - return {}; - - // __r_ is a compressed_pair of the actual data and the allocator. The data we - // want is in the first base class. - ValueObjectSP valobj_r_base_sp = - valobj_r_sp->GetChildAtIndex(0, /*can_create=*/true); - if (!valobj_r_base_sp) - return {}; - - ValueObjectSP valobj_rep_sp = valobj_r_base_sp->GetChildMemberWithName( - ConstString("__value_"), /*can_create=*/true); - if (!valobj_rep_sp) - return {}; - - ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(ConstString("__l"), - /*can_create=*/true); - if (!l) - return {}; - - StringLayout layout = l->GetIndexOfChildWithName(ConstString("__data_")) == 0 - ? StringLayout::DSC - : StringLayout::CSD; - - bool short_mode = false; // this means the string is in short-mode and the - // data is stored inline - bool using_bitmasks = true; // Whether the class uses bitmasks for the mode - // flag (pre-D123580). - uint64_t size; - uint64_t size_mode_value = 0; - - ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName( - ConstString("__s"), /*can_create=*/true); - if (!short_sp) - return {}; - - ValueObjectSP is_long = - short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); - ValueObjectSP size_sp = - short_sp->GetChildAtNamePath({ConstString("__size_")}); - if (!size_sp) - return {}; - - if (is_long) { - using_bitmasks = false; - short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); - size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); - } else { - // The string mode is encoded in the size field. - size_mode_value = size_sp->GetValueAsUnsigned(0); - uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; - short_mode = (size_mode_value & mode_mask) == 0; - } - - if (short_mode) { - ValueObjectSP location_sp = - short_sp->GetChildMemberWithName(ConstString("__data_"), true); - if (using_bitmasks) - size = (layout == StringLayout::DSC) ? size_mode_value - : ((size_mode_value >> 1) % 256); - - // When the small-string optimization takes place, the data must fit in the - // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's - // likely that the string isn't initialized and we're reading garbage. - ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); - const llvm::Optional max_bytes = - location_sp->GetCompilerType().GetByteSize( - exe_ctx.GetBestExecutionContextScope()); - if (!max_bytes || size > *max_bytes || !location_sp) - return {}; - - return std::make_pair(size, location_sp); - } - - // we can use the layout_decider object as the data pointer - ValueObjectSP location_sp = - l->GetChildMemberWithName(ConstString("__data_"), /*can_create=*/true); - ValueObjectSP size_vo = - l->GetChildMemberWithName(ConstString("__size_"), /*can_create=*/true); - ValueObjectSP capacity_vo = - l->GetChildMemberWithName(ConstString("__cap_"), /*can_create=*/true); - if (!size_vo || !location_sp || !capacity_vo) - return {}; - size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); - uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); - if (!using_bitmasks && layout == StringLayout::CSD) - capacity *= 2; - if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || - capacity < size) - return {}; - return std::make_pair(size, location_sp); -} - static bool LibcxxWStringSummaryProvider(ValueObject &valobj, Stream &stream, const TypeSummaryOptions &summary_options, diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h index b5ade4af8574ad51e503050b102d6963086cf2b9..f4489c179dcabc613b2d67049abaa251d22a104e 100644 --- a/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxx.h @@ -251,6 +251,23 @@ SyntheticChildrenFrontEnd * LibcxxStdSpanSyntheticFrontEndCreator(CXXSyntheticChildren *, lldb::ValueObjectSP); +// OHOS_LOCAL begin +SyntheticChildrenFrontEnd +*LibcxxStdStringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd +*LibcxxStdWStringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd +*LibcxxStdU16StringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); + +SyntheticChildrenFrontEnd +*LibcxxStdU32StringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP); +// OHOS_LOCAL end } // namespace formatters } // namespace lldb_private diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxString.cpp b/lldb/source/Plugins/Language/CPlusPlus/LibCxxString.cpp new file mode 100644 index 0000000000000000000000000000000000000000..44373146d19b5e019c8148d32e15a13a0521e7f6 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxString.cpp @@ -0,0 +1,177 @@ +#include "LibCxx.h" +#include "LibCxxStringInfoExtractor.h" + +#include "lldb/DataFormatters/FormattersHelpers.h" +#include "llvm/ADT/Optional.h" +#include + +using namespace lldb; +using namespace lldb_private; + +namespace { + +class StringFrontend: public SyntheticChildrenFrontEnd { + +public: + StringFrontend(ValueObject& valobj, const char* prefix=""): + SyntheticChildrenFrontEnd(valobj), m_prefix(prefix) {} + + size_t CalculateNumChildren() override { + return m_size + m_special_members_count; + } + + lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { + + if (idx < m_special_members_count) { + return m_backend.GetChildMemberWithName( + ConstString("__r_"), /*can_create=*/true); + } + + idx -= m_special_members_count; + + if (!m_str_data_ptr || idx > m_size || !m_element_size) { + return {}; + } + + auto char_it = m_chars.find(idx); + if (char_it != m_chars.end()) { + return char_it->second; + } + + uint64_t offset = idx * m_element_size; + uint64_t address = m_str_data_ptr->GetValueAsUnsigned(0); + + if (!address) { + return {}; + } + + StreamString name; + name.Printf("[%" PRIu64 "]", (uint64_t)idx); + + m_chars[idx] = CreateValueObjectFromAddress(name.GetString(), address + offset, + m_backend.GetExecutionContextRef(), + m_element_type); + + return m_chars[idx]; + } + + size_t GetIndexOfChildWithName(ConstString name) override { + if (name == "__r_") { + return 0; + } + return formatters::ExtractIndexFromString(name.GetCString()) + + m_special_members_count; + } + + bool Update() override { + + clear(); + + auto string_info = ExtractLibcxxStringInfo(m_backend); + if (!string_info) + return false; + std::tie(m_size, m_str_data_ptr) = *string_info; + + m_element_type = m_backend.GetCompilerType().GetTypeTemplateArgument(0); + m_element_size = m_element_type.GetByteSize(nullptr).value_or(0); + + if (m_str_data_ptr->IsArrayType()) { + // this means the string is in short-mode and the + // data is stored inline in array, + // so we need address of this array + Status status; + m_str_data_ptr = m_str_data_ptr->AddressOf(status); + } + + return false; + } + + bool MightHaveChildren() override { + return true; + } + + bool SetValueFromCString(const char *value_str, Status &error) override { + + ValueObjectSP expr_value_sp; + + std::unique_lock lock; + ExecutionContext exe_ctx(m_backend.GetExecutionContextRef(), lock); + + Target *target = exe_ctx.GetTargetPtr(); + StackFrame *frame = exe_ctx.GetFramePtr(); + + if (target && frame) { + EvaluateExpressionOptions options; + options.SetUseDynamic(frame->CalculateTarget()->GetPreferDynamicValue()); + options.SetIgnoreBreakpoints(true); + + if (target->GetLanguage() != eLanguageTypeUnknown) + options.SetLanguage(target->GetLanguage()); + else + options.SetLanguage(frame->GetLanguage()); + StreamString expr; + expr.Printf("%s = %s\"%s\"", + m_backend.GetName().AsCString(), + m_prefix, + value_str); + ExpressionResults result = + target->EvaluateExpression(expr.GetString(), frame, expr_value_sp, options); + if (result != eExpressionCompleted) + error.SetErrorStringWithFormat("Expression (%s) can't be evaluated.", + expr.GetData()); + } + + return error.Success(); + } + +private: + + void clear() { + m_size = 0; + m_element_size = 0; + m_str_data_ptr = nullptr; + m_element_type.Clear(); + m_chars.clear(); + } + + std::unordered_map m_chars; + ValueObjectSP m_str_data_ptr; + CompilerType m_element_type; + size_t m_size = 0; + size_t m_element_size = 0; + const char* m_prefix = ""; + static const size_t m_special_members_count = 1; // __r_ member needed for correct summaries +}; + +} // namespace + +SyntheticChildrenFrontEnd *formatters::LibcxxStdStringSyntheticFrontEndCreator( + CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new StringFrontend(*valobj_sp); + return nullptr; +} + +SyntheticChildrenFrontEnd +*formatters::LibcxxStdWStringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new StringFrontend(*valobj_sp, "L"); + return nullptr; +} + +SyntheticChildrenFrontEnd +*formatters::LibcxxStdU16StringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new StringFrontend(*valobj_sp, "u"); + return nullptr; +} + +SyntheticChildrenFrontEnd +*formatters::LibcxxStdU32StringSyntheticFrontEndCreator(CXXSyntheticChildren *, + lldb::ValueObjectSP valobj_sp) { + if (valobj_sp) + return new StringFrontend(*valobj_sp, "U"); + return nullptr; +} diff --git a/lldb/source/Plugins/Language/CPlusPlus/LibCxxStringInfoExtractor.h b/lldb/source/Plugins/Language/CPlusPlus/LibCxxStringInfoExtractor.h new file mode 100644 index 0000000000000000000000000000000000000000..495b9d8ef0cdcfbd543049247333043d2ef97b01 --- /dev/null +++ b/lldb/source/Plugins/Language/CPlusPlus/LibCxxStringInfoExtractor.h @@ -0,0 +1,119 @@ + +#ifndef LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXSTRINGINFOEXTRACTOR_H +#define LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXSTRINGINFOEXTRACTOR_H + +#include "lldb/Core/ValueObject.h" +#include "llvm/ADT/Optional.h" +#include "lldb/lldb-forward.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +/// The field layout in a libc++ string (cap, side, data or data, size, cap). +namespace { +enum class StringLayout { CSD, DSC }; +} + +/// Determine the size in bytes of \p valobj (a libc++ std::string object) and +/// extract its data payload. Return the size + payload pair. +// TODO: Support big-endian architectures. +static llvm::Optional> +ExtractLibcxxStringInfo(ValueObject &valobj) { + ValueObjectSP valobj_r_sp = + valobj.GetChildMemberWithName(ConstString("__r_"), /*can_create=*/true); + if (!valobj_r_sp || !valobj_r_sp->GetError().Success()) + return {}; + + // __r_ is a compressed_pair of the actual data and the allocator. The data we + // want is in the first base class. + ValueObjectSP valobj_r_base_sp = + valobj_r_sp->GetChildAtIndex(0, /*can_create=*/true); + if (!valobj_r_base_sp) + return {}; + + ValueObjectSP valobj_rep_sp = valobj_r_base_sp->GetChildMemberWithName( + ConstString("__value_"), /*can_create=*/true); + if (!valobj_rep_sp) + return {}; + + ValueObjectSP l = valobj_rep_sp->GetChildMemberWithName(ConstString("__l"), + /*can_create=*/true); + if (!l) + return {}; + + StringLayout layout = l->GetIndexOfChildWithName(ConstString("__data_")) == 0 + ? StringLayout::DSC + : StringLayout::CSD; + + bool short_mode = false; // this means the string is in short-mode and the + // data is stored inline + bool using_bitmasks = true; // Whether the class uses bitmasks for the mode + // flag (pre-D123580). + uint64_t size; + uint64_t size_mode_value = 0; + + ValueObjectSP short_sp = valobj_rep_sp->GetChildMemberWithName( + ConstString("__s"), /*can_create=*/true); + if (!short_sp) + return {}; + + ValueObjectSP is_long = + short_sp->GetChildMemberWithName(ConstString("__is_long_"), true); + ValueObjectSP size_sp = + short_sp->GetChildAtNamePath({ConstString("__size_")}); + if (!size_sp) + return {}; + + if (is_long) { + using_bitmasks = false; + short_mode = !is_long->GetValueAsUnsigned(/*fail_value=*/0); + size = size_sp->GetValueAsUnsigned(/*fail_value=*/0); + } else { + // The string mode is encoded in the size field. + size_mode_value = size_sp->GetValueAsUnsigned(0); + uint8_t mode_mask = layout == StringLayout::DSC ? 0x80 : 1; + short_mode = (size_mode_value & mode_mask) == 0; + } + + if (short_mode) { + ValueObjectSP location_sp = + short_sp->GetChildMemberWithName(ConstString("__data_"), true); + if (using_bitmasks) + size = (layout == StringLayout::DSC) ? size_mode_value + : ((size_mode_value >> 1) % 256); + + // When the small-string optimization takes place, the data must fit in the + // inline string buffer (23 bytes on x86_64/Darwin). If it doesn't, it's + // likely that the string isn't initialized and we're reading garbage. + ExecutionContext exe_ctx(location_sp->GetExecutionContextRef()); + const llvm::Optional max_bytes = + location_sp->GetCompilerType().GetByteSize( + exe_ctx.GetBestExecutionContextScope()); + if (!max_bytes || size > *max_bytes || !location_sp) + return {}; + + return std::make_pair(size, location_sp); + } + + // we can use the layout_decider object as the data pointer + ValueObjectSP location_sp = + l->GetChildMemberWithName(ConstString("__data_"), /*can_create=*/true); + ValueObjectSP size_vo = + l->GetChildMemberWithName(ConstString("__size_"), /*can_create=*/true); + ValueObjectSP capacity_vo = + l->GetChildMemberWithName(ConstString("__cap_"), /*can_create=*/true); + if (!size_vo || !location_sp || !capacity_vo) + return {}; + size = size_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + uint64_t capacity = capacity_vo->GetValueAsUnsigned(LLDB_INVALID_OFFSET); + if (!using_bitmasks && layout == StringLayout::CSD) + capacity *= 2; + if (size == LLDB_INVALID_OFFSET || capacity == LLDB_INVALID_OFFSET || + capacity < size) + return {}; + return std::make_pair(size, location_sp); +} + +#endif // LLDB_SOURCE_PLUGINS_LANGUAGE_CPLUSPLUS_LIBCXXSTRINGINFOEXTRACTOR_H diff --git a/lldb/source/Utility/Scalar.cpp b/lldb/source/Utility/Scalar.cpp index 19e00e111be52321d11c46a865a48897e054167d..09a4153518bb9137e1251af32a1360491f47e25e 100644 --- a/lldb/source/Utility/Scalar.cpp +++ b/lldb/source/Utility/Scalar.cpp @@ -19,6 +19,9 @@ #include #include +// OHOS_LOCAL begin +#include // for std::isalpha +// OHOS_LOCAL end using namespace lldb; using namespace lldb_private; @@ -645,7 +648,17 @@ Status Scalar::SetValueFromCString(const char *value_str, Encoding encoding, bool is_signed = encoding == eEncodingSint; bool is_negative = is_signed && str.consume_front("-"); APInt integer; - if (str.getAsInteger(0, integer)) { + // OHOS_LOCAL begin + if (str.size() == 1 && std::isalpha(static_cast(str[0]))) { + // We can represent single character as Scalar - + // this is useful when working with symbols in string + // NOTE: it is okay to consider char size as 8-bit since we only have + // `SetValueFrom C String` api, not the `C Wstring` or something like that. + // If we can ever get wide characters here - we have to modify this behaviour somehow. + integer = APInt(8, static_cast (str[0])); + } + // OHOS_LOCAL end + else if (str.getAsInteger(0, integer)) { error.SetErrorStringWithFormatv( "'{0}' is not a valid integer string value", value_str); break; diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py index c0a21de3a469b88ee1cf7471c3db414c894fa9ce..56809704887816c7befe09c0ed51fa4e26c4ebb6 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/generic/multimap/TestDataFormatterGenericMultiMap.py @@ -174,18 +174,38 @@ class GenericMultiMapDataFormatterTestCase(TestBase): substrs=[ multimap, 'size=4', - '[0] = (first = "one", second = 1)', - '[1] = (first = "three", second = 3)', - '[2] = (first = "two", second = 2)', - '[3] = (first = "zero", second = 0)', + # OHOS_LOCAL begin + '[0] = ', + 'first = "one"', + 'second = 1', + '[1] = ', + 'first = "three"', + 'second = 3', + '[2] = ', + 'first = "two"', + 'second = 2', + '[3] = ', + 'first = "zero"', + 'second = 0', + # OHOS_LOCAL end ]) self.expect("p si", - substrs=[multimap, 'size=4', - '[0] = (first = "one", second = 1)', - '[1] = (first = "three", second = 3)', - '[2] = (first = "two", second = 2)', - '[3] = (first = "zero", second = 0)', + substrs=[multimap, 'size=4', + # OHOS_LOCAL begin + '[0] = ', + 'first = "one"', + 'second = 1', + '[1] = ', + 'first = "three"', + 'second = 3', + '[2] = ', + 'first = "two"', + 'second = 2', + '[3] = ', + 'first = "zero"', + 'second = 0', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right @@ -225,10 +245,20 @@ class GenericMultiMapDataFormatterTestCase(TestBase): substrs=[ multimap, 'size=4', - '[0] = (first = 1, second = "is")', - '[1] = (first = 2, second = "smart")', - '[2] = (first = 3, second = "!!!")', - '[3] = (first = 85, second = "goofy")', + # OHOS_LOCAL begin + '[0] = ', + 'first = 1', + 'second = "is"', + '[1] = ', + 'first = 2', + 'second = "smart"', + '[2] = ', + 'first = 3', + 'second = "!!!"', + '[3] = ', + 'first = 85', + 'second = "goofy"', + # OHOS_LOCAL end ]) self.expect( @@ -236,10 +266,20 @@ class GenericMultiMapDataFormatterTestCase(TestBase): substrs=[ multimap, 'size=4', - '[0] = (first = 1, second = "is")', - '[1] = (first = 2, second = "smart")', - '[2] = (first = 3, second = "!!!")', - '[3] = (first = 85, second = "goofy")', + # OHOS_LOCAL begin + '[0] = ', + 'first = 1', + 'second = "is"', + '[1] = ', + 'first = 2', + 'second = "smart"', + '[2] = ', + 'first = 3', + 'second = "!!!"', + '[3] = ', + 'first = 85', + 'second = "goofy"', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right @@ -283,9 +323,17 @@ class GenericMultiMapDataFormatterTestCase(TestBase): substrs=[ multimap, 'size=3', - '[0] = (first = "casa", second = "house")', - '[1] = (first = "ciao", second = "hello")', - '[2] = (first = "gatto", second = "cat")', + # OHOS_LOCAL begin + '[0] = ', + 'first = "casa"', + 'second = "house"', + '[1] = ', + 'first = "ciao"', + 'second = "hello"', + '[2] = ', + 'first = "gatto"', + 'second = "cat"', + # OHOS_LOCAL end ]) self.check("ss", 3) @@ -295,9 +343,17 @@ class GenericMultiMapDataFormatterTestCase(TestBase): substrs=[ multimap, 'size=3', - '[0] = (first = "casa", second = "house")', - '[1] = (first = "ciao", second = "hello")', - '[2] = (first = "gatto", second = "cat")', + # OHOS_LOCAL begin + '[0] = ', + 'first = "casa"', + 'second = "house"', + '[1] = ', + 'first = "ciao"', + 'second = "hello"', + '[2] = ', + 'first = "gatto"', + 'second = "cat"', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py index 097a1e649d43a26913cd962f1a34269258a812ca..15599e9437c8dbced4158a571b0a25037a1a39ff 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py @@ -164,10 +164,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=4', - '[0] = (first = "one", second = 1)', - '[1] = (first = "three", second = 3)', - '[2] = (first = "two", second = 2)', - '[3] = (first = "zero", second = 0)', + # OHOS_LOCAL begin + '[0] = ', + 'first = "one"', + 'second = 1', + '[1] = ', + 'first = "three"', + 'second = 3', + '[2] = ', + 'first = "two"', + 'second = 2', + '[3] = ', + 'first = "zero"', + 'second = 0', + # OHOS_LOCAL end ]) self.expect( @@ -175,10 +185,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=4', - '[0] = (first = "one", second = 1)', - '[1] = (first = "three", second = 3)', - '[2] = (first = "two", second = 2)', - '[3] = (first = "zero", second = 0)', + # OHOS_LOCAL begin + '[0] = ', + 'first = "one"', + 'second = 1', + '[1] = ', + 'first = "three"', + 'second = 3', + '[2] = ', + 'first = "two"', + 'second = 2', + '[3] = ', + 'first = "zero"', + 'second = 0', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right @@ -218,10 +238,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=4', - '[0] = (first = 1, second = "is")', - '[1] = (first = 2, second = "smart")', - '[2] = (first = 3, second = "!!!")', - '[3] = (first = 85, second = "goofy")', + # OHOS_LOCAL begin + '[0] = ', + 'first = 1', + 'second = "is"', + '[1] = ', + 'first = 2', + 'second = "smart"', + '[2] = ', + 'first = 3', + 'second = "!!!"', + '[3] = ', + 'first = 85', + 'second = "goofy"', + # OHOS_LOCAL end ]) self.expect( @@ -229,10 +259,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=4', - '[0] = (first = 1, second = "is")', - '[1] = (first = 2, second = "smart")', - '[2] = (first = 3, second = "!!!")', - '[3] = (first = 85, second = "goofy")', + # OHOS_LOCAL begin + '[0] = ', + 'first = 1', + 'second = "is"', + '[1] = ', + 'first = 2', + 'second = "smart"', + '[2] = ', + 'first = 3', + 'second = "!!!"', + '[3] = ', + 'first = 85', + 'second = "goofy"', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right @@ -272,9 +312,17 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=3', - '[0] = (first = "casa", second = "house")', - '[1] = (first = "ciao", second = "hello")', - '[2] = (first = "gatto", second = "cat")', + # OHOS_LOCAL begin + '[0] = ', + 'first = "casa"', + 'second = "house"', + '[1] = ', + 'first = "ciao"', + 'second = "hello"', + '[2] = ', + 'first = "gatto"', + 'second = "cat"', + # OHOS_LOCAL end ]) self.expect( @@ -282,9 +330,17 @@ class LibcxxMapDataFormatterTestCase(TestBase): substrs=[ '%s::map' % ns, 'size=3', - '[0] = (first = "casa", second = "house")', - '[1] = (first = "ciao", second = "hello")', - '[2] = (first = "gatto", second = "cat")', + # OHOS_LOCAL begin + '[0] = ', + 'first = "casa"', + 'second = "house"', + '[1] = ', + 'first = "ciao"', + 'second = "hello"', + '[2] = ', + 'first = "gatto"', + 'second = "cat"', + # OHOS_LOCAL end ]) # check that MightHaveChildren() gets it right diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py index c646fccc2f5eb85dd9ff46cbf60594e5d40f78da..5ede29ea3be3efbc32268e72ccd51ca5d3542fc7 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/shared_ptr/TestDataFormatterLibcxxSharedPtr.py @@ -80,7 +80,9 @@ class TestCase(TestBase): ValueCheck(name="name", summary='"steph"'), ], ) - self.assertEqual(str(valobj), '(User) *__ptr_ = (id = 30, name = "steph")') + # OHOS_LOCAL begin + self.assertEqual(str(valobj), '(User) *__ptr_ = {\n id = 30\n name = "steph"\n}') + # OHOS_LOCAL end self.expect_var_path("sp_user->id", type="int", value="30") self.expect_var_path("sp_user->name", type="std::string", summary='"steph"') diff --git a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py index 70efdd84f242a531c0d09b2eab0773735b8e5a85..baaf54aedae953fdbe40235e36b779abbafe678b 100644 --- a/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py +++ b/lldb/test/API/functionalities/data-formatter/data-formatter-stl/libcxx/unique_ptr/TestDataFormatterLibcxxUniquePtr.py @@ -79,7 +79,9 @@ class TestCase(TestBase): ValueCheck(name="name", summary='"steph"'), ], ) - self.assertEqual(str(valobj), '(User) *__value_ = (id = 30, name = "steph")') + # OHOS_LOCAL begin + self.assertEqual(str(valobj), '(User) *__value_ = {\n id = 30\n name = "steph"\n}') + # OHOS_LOCAL end self.expect_var_path("up_user->id", type="int", value="30") self.expect_var_path("up_user->name", type="std::string", summary='"steph"') diff --git a/lldb/test/API/python_api/value/change_values/libcxx/string/Makefile b/lldb/test/API/python_api/value/change_values/libcxx/string/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e4e54adc602d296a74a4cefd19d226a570c09c20 --- /dev/null +++ b/lldb/test/API/python_api/value/change_values/libcxx/string/Makefile @@ -0,0 +1,8 @@ +# OHOS_LOCAL begin +CXX_SOURCES := main.cpp + +USE_LIBCPP := 1 + +CXXFLAGS_EXTRAS := -O0 +include Makefile.rules +# OHOS_LOCAL end diff --git a/lldb/test/API/python_api/value/change_values/libcxx/string/TestChangeStringValue.py b/lldb/test/API/python_api/value/change_values/libcxx/string/TestChangeStringValue.py new file mode 100644 index 0000000000000000000000000000000000000000..0c37a6d0b780a0b849fc0a87f013fb75da2871a0 --- /dev/null +++ b/lldb/test/API/python_api/value/change_values/libcxx/string/TestChangeStringValue.py @@ -0,0 +1,71 @@ +""" +Test change libc++ string values. +""" + +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + +class LibcxxChangeStringValueTestCase(TestBase): + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def do_test_value(self, frame, var_name, new_value, str_prefix, new_first_letter): + str_value = frame.FindVariable(var_name) + self.assertTrue(str_value.IsValid(), "Got the SBValue for {}".format(var_name)) + + # update whole string + err = lldb.SBError() + result = str_value.SetValueFromCString(new_value, err) + self.assertTrue(result, "Setting val returned error: {}".format(err)) + result = str_value.GetSummary() # str_value is a summary + expected = '{}"{}"'.format(str_prefix, new_value) + self.assertTrue(result == expected, "Got value: ({}), expected: ({})" + .format(result, expected)) + + # update only first letter + first_letter = str_value.GetChildMemberWithName("[0]") + result = first_letter.SetValueFromCString(new_first_letter, err) + self.assertTrue(result, "Setting val returned error: {}".format(err)) + + # We could use `GetValue` or `GetSummary` (only non-ascii characters) here, + # but all of them will give us different representation of the same symbol, + # e.g. `GetValue` for `wstring` will give us "X\0\0\0\0", while for u16/u32 + # strings it will give "U+0058"/"U+0x00000058", so it is easier to check symbol's code + result = first_letter.GetValueAsUnsigned() + expected = ord(new_first_letter) + self.assertTrue(result == expected, "Got value: ({}), expected: ({})" + .format(result, expected)) + self.assertTrue(first_letter.GetValueDidChange(), "LLDB noticed that value changed") + + @add_test_categories(["libc++"]) + @expectedFailureAll(oslist=["windows"], archs=["arm"], bugnumber="llvm.org/pr24772") + @expectedFailureAll(archs=["arm"]) # arm can't jit + def test(self): + """Test that we can change values of libc++ string.""" + self.build() + self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET) + bkpt = self.target().FindBreakpointByID( + lldbutil.run_break_set_by_source_regexp( + self, "Set break point at this line.")) + + self.runCmd("run", RUN_SUCCEEDED) + + # Get Frame #0. + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + self.assertState(process.GetState(), lldb.eStateStopped) + thread = lldbutil.get_stopped_thread( + process, lldb.eStopReasonBreakpoint) + self.assertTrue( + thread.IsValid(), + "There should be a thread stopped due to breakpoint condition") + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue(frame0.IsValid(), "Got a valid frame.") + + for var_name, str_prefix in zip(("s", "l", "ws", "wl", "u16s", "u32s"), + ('', '', 'L', 'L', 'u', 'U')): + self.do_test_value(frame0, var_name, "new_value", str_prefix, "X") diff --git a/lldb/test/API/python_api/value/change_values/libcxx/string/main.cpp b/lldb/test/API/python_api/value/change_values/libcxx/string/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5b355d88ca15e4b25c8ca0a93f633870f6f6ac56 --- /dev/null +++ b/lldb/test/API/python_api/value/change_values/libcxx/string/main.cpp @@ -0,0 +1,21 @@ +#include + +using namespace std; + +int main() { + // Currently changing value for string requires + // string's operator= to be in debug executable + string s; + string l; + wstring ws; + wstring wl; + u16string u16s; + u32string u32s; + s = "small"; + l = "looooooooooooooooooooooooooooooooong"; + ws = L"wsmall"; + wl = L"wlooooooooooooooooooooooooooooooooong"; + u16s = u"small"; + u32s = U"looooooooooooooooooooooooooooooooong"; + return 0; // Set break point at this line. +}