| //===-- ValueObjectChild.cpp ------------------------------------*- C++ -*-===// | |
| // | |
| // 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/Core/ValueObjectChild.h" | |
| #include "lldb/Core/Value.h" | |
| #include "lldb/Symbol/CompilerType.h" | |
| #include "lldb/Target/ExecutionContext.h" | |
| #include "lldb/Target/Process.h" | |
| #include "lldb/Utility/Flags.h" | |
| #include "lldb/Utility/Scalar.h" | |
| #include "lldb/Utility/Status.h" | |
| #include "lldb/lldb-forward.h" | |
| #include <functional> | |
| #include <memory> | |
| #include <vector> | |
| #include <stdio.h> | |
| #include <string.h> | |
| using namespace lldb_private; | |
| ValueObjectChild::ValueObjectChild( | |
| ValueObject &parent, const CompilerType &compiler_type, | |
| ConstString name, uint64_t byte_size, int32_t byte_offset, | |
| uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, | |
| bool is_base_class, bool is_deref_of_parent, | |
| AddressType child_ptr_or_ref_addr_type, uint64_t language_flags) | |
| : ValueObject(parent), m_compiler_type(compiler_type), | |
| m_byte_size(byte_size), m_byte_offset(byte_offset), | |
| m_bitfield_bit_size(bitfield_bit_size), | |
| m_bitfield_bit_offset(bitfield_bit_offset), | |
| m_is_base_class(is_base_class), m_is_deref_of_parent(is_deref_of_parent), | |
| m_can_update_with_invalid_exe_ctx() { | |
| m_name = name; | |
| SetAddressTypeOfChildren(child_ptr_or_ref_addr_type); | |
| SetLanguageFlags(language_flags); | |
| } | |
| ValueObjectChild::~ValueObjectChild() {} | |
| lldb::ValueType ValueObjectChild::GetValueType() const { | |
| return m_parent->GetValueType(); | |
| } | |
| size_t ValueObjectChild::CalculateNumChildren(uint32_t max) { | |
| ExecutionContext exe_ctx(GetExecutionContextRef()); | |
| auto children_count = GetCompilerType().GetNumChildren(true, &exe_ctx); | |
| return children_count <= max ? children_count : max; | |
| } | |
| static void AdjustForBitfieldness(ConstString &name, | |
| uint8_t bitfield_bit_size) { | |
| if (name && bitfield_bit_size) { | |
| const char *compiler_type_name = name.AsCString(); | |
| if (compiler_type_name) { | |
| std::vector<char> bitfield_type_name(strlen(compiler_type_name) + 32, 0); | |
| ::snprintf(&bitfield_type_name.front(), bitfield_type_name.size(), | |
| "%s:%u", compiler_type_name, bitfield_bit_size); | |
| name.SetCString(&bitfield_type_name.front()); | |
| } | |
| } | |
| } | |
| ConstString ValueObjectChild::GetTypeName() { | |
| if (m_type_name.IsEmpty()) { | |
| m_type_name = GetCompilerType().GetConstTypeName(); | |
| AdjustForBitfieldness(m_type_name, m_bitfield_bit_size); | |
| } | |
| return m_type_name; | |
| } | |
| ConstString ValueObjectChild::GetQualifiedTypeName() { | |
| ConstString qualified_name = GetCompilerType().GetConstTypeName(); | |
| AdjustForBitfieldness(qualified_name, m_bitfield_bit_size); | |
| return qualified_name; | |
| } | |
| ConstString ValueObjectChild::GetDisplayTypeName() { | |
| ConstString display_name = GetCompilerType().GetDisplayTypeName(); | |
| AdjustForBitfieldness(display_name, m_bitfield_bit_size); | |
| return display_name; | |
| } | |
| LazyBool ValueObjectChild::CanUpdateWithInvalidExecutionContext() { | |
| if (m_can_update_with_invalid_exe_ctx.hasValue()) | |
| return m_can_update_with_invalid_exe_ctx.getValue(); | |
| if (m_parent) { | |
| ValueObject *opinionated_parent = | |
| m_parent->FollowParentChain([](ValueObject *valobj) -> bool { | |
| return (valobj->CanUpdateWithInvalidExecutionContext() == | |
| eLazyBoolCalculate); | |
| }); | |
| if (opinionated_parent) | |
| return (m_can_update_with_invalid_exe_ctx = | |
| opinionated_parent->CanUpdateWithInvalidExecutionContext()) | |
| .getValue(); | |
| } | |
| return (m_can_update_with_invalid_exe_ctx = | |
| this->ValueObject::CanUpdateWithInvalidExecutionContext()) | |
| .getValue(); | |
| } | |
| bool ValueObjectChild::UpdateValue() { | |
| m_error.Clear(); | |
| SetValueIsValid(false); | |
| ValueObject *parent = m_parent; | |
| if (parent) { | |
| if (parent->UpdateValueIfNeeded(false)) { | |
| m_value.SetCompilerType(GetCompilerType()); | |
| CompilerType parent_type(parent->GetCompilerType()); | |
| // Copy the parent scalar value and the scalar value type | |
| m_value.GetScalar() = parent->GetValue().GetScalar(); | |
| Value::ValueType value_type = parent->GetValue().GetValueType(); | |
| m_value.SetValueType(value_type); | |
| Flags parent_type_flags(parent_type.GetTypeInfo()); | |
| const bool is_instance_ptr_base = | |
| ((m_is_base_class) && | |
| (parent_type_flags.AnySet(lldb::eTypeInstanceIsPointer))); | |
| if (parent->GetCompilerType().ShouldTreatScalarValueAsAddress()) { | |
| lldb::addr_t addr = parent->GetPointerValue(); | |
| m_value.GetScalar() = addr; | |
| if (addr == LLDB_INVALID_ADDRESS) { | |
| m_error.SetErrorString("parent address is invalid."); | |
| } else if (addr == 0) { | |
| m_error.SetErrorString("parent is NULL"); | |
| } else { | |
| m_value.GetScalar() += m_byte_offset; | |
| AddressType addr_type = parent->GetAddressTypeOfChildren(); | |
| switch (addr_type) { | |
| case eAddressTypeFile: { | |
| lldb::ProcessSP process_sp(GetProcessSP()); | |
| if (process_sp && process_sp->IsAlive()) | |
| m_value.SetValueType(Value::eValueTypeLoadAddress); | |
| else | |
| m_value.SetValueType(Value::eValueTypeFileAddress); | |
| } break; | |
| case eAddressTypeLoad: | |
| m_value.SetValueType(is_instance_ptr_base | |
| ? Value::eValueTypeScalar | |
| : Value::eValueTypeLoadAddress); | |
| break; | |
| case eAddressTypeHost: | |
| m_value.SetValueType(Value::eValueTypeHostAddress); | |
| break; | |
| case eAddressTypeInvalid: | |
| // TODO: does this make sense? | |
| m_value.SetValueType(Value::eValueTypeScalar); | |
| break; | |
| } | |
| } | |
| } else { | |
| switch (value_type) { | |
| case Value::eValueTypeLoadAddress: | |
| case Value::eValueTypeFileAddress: | |
| case Value::eValueTypeHostAddress: { | |
| lldb::addr_t addr = | |
| m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); | |
| if (addr == LLDB_INVALID_ADDRESS) { | |
| m_error.SetErrorString("parent address is invalid."); | |
| } else if (addr == 0) { | |
| m_error.SetErrorString("parent is NULL"); | |
| } else { | |
| // Set this object's scalar value to the address of its value by | |
| // adding its byte offset to the parent address | |
| m_value.GetScalar() += GetByteOffset(); | |
| // If a bitfield doesn't fit into the child_byte_size'd | |
| // window at child_byte_offset, move the window forward | |
| // until it fits. The problem here is that Value has no | |
| // notion of bitfields and thus the Value's DataExtractor | |
| // is sized like the bitfields CompilerType; a sequence of | |
| // bitfields, however, can be larger than their underlying | |
| // type. | |
| if (m_bitfield_bit_offset) { | |
| const bool thread_and_frame_only_if_stopped = true; | |
| ExecutionContext exe_ctx(GetExecutionContextRef().Lock( | |
| thread_and_frame_only_if_stopped)); | |
| if (auto type_bit_size = GetCompilerType().GetBitSize( | |
| exe_ctx.GetBestExecutionContextScope())) { | |
| uint64_t bitfield_end = | |
| m_bitfield_bit_size + m_bitfield_bit_offset; | |
| if (bitfield_end > *type_bit_size) { | |
| uint64_t overhang_bytes = | |
| (bitfield_end - *type_bit_size + 7) / 8; | |
| m_value.GetScalar() += overhang_bytes; | |
| m_bitfield_bit_offset -= overhang_bytes * 8; | |
| } | |
| } | |
| } | |
| } | |
| } break; | |
| case Value::eValueTypeScalar: | |
| // try to extract the child value from the parent's scalar value | |
| { | |
| Scalar scalar(m_value.GetScalar()); | |
| if (m_bitfield_bit_size) | |
| scalar.ExtractBitfield(m_bitfield_bit_size, | |
| m_bitfield_bit_offset); | |
| else | |
| scalar.ExtractBitfield(8 * m_byte_size, 8 * m_byte_offset); | |
| m_value.GetScalar() = scalar; | |
| } | |
| break; | |
| default: | |
| m_error.SetErrorString("parent has invalid value."); | |
| break; | |
| } | |
| } | |
| if (m_error.Success()) { | |
| const bool thread_and_frame_only_if_stopped = true; | |
| ExecutionContext exe_ctx( | |
| GetExecutionContextRef().Lock(thread_and_frame_only_if_stopped)); | |
| if (GetCompilerType().GetTypeInfo() & lldb::eTypeHasValue) { | |
| Value &value = is_instance_ptr_base ? m_parent->GetValue() : m_value; | |
| m_error = | |
| value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); | |
| } else { | |
| m_error.Clear(); // No value so nothing to read... | |
| } | |
| } | |
| } else { | |
| m_error.SetErrorStringWithFormat("parent failed to evaluate: %s", | |
| parent->GetError().AsCString()); | |
| } | |
| } else { | |
| m_error.SetErrorString("ValueObjectChild has a NULL parent ValueObject."); | |
| } | |
| return m_error.Success(); | |
| } | |
| bool ValueObjectChild::IsInScope() { | |
| ValueObject *root(GetRoot()); | |
| if (root) | |
| return root->IsInScope(); | |
| return false; | |
| } |