diff options
Diffstat (limited to 'lldb/source/Core/ValueObject.cpp')
-rw-r--r-- | lldb/source/Core/ValueObject.cpp | 678 |
1 files changed, 678 insertions, 0 deletions
diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp new file mode 100644 index 00000000000..93511febe8a --- /dev/null +++ b/lldb/source/Core/ValueObject.cpp @@ -0,0 +1,678 @@ +//===-- ValueObject.cpp -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Core/ValueObject.h" + +// C Includes +// C++ Includes +// Other libraries and framework includes +#include "clang/AST/Type.h" +#include "llvm/Support/raw_ostream.h" + +// Project includes +#include "lldb/Core/DataBufferHeap.h" +#include "lldb/Core/StreamString.h" +#include "lldb/Core/ValueObjectChild.h" +#include "lldb/Core/ValueObjectList.h" + +#include "lldb/Symbol/ClangASTContext.h" +#include "lldb/Symbol/Type.h" + +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/Thread.h" + +using namespace lldb; +using namespace lldb_private; + +static lldb::user_id_t g_value_obj_uid = 0; + +//---------------------------------------------------------------------- +// ValueObject constructor +//---------------------------------------------------------------------- +ValueObject::ValueObject () : + UserID (++g_value_obj_uid), // Unique identifier for every value object + m_update_id (0), // Value object lists always start at 1, value objects start at zero + m_name (), + m_data (), + m_value (), + m_error (), + m_flags (), + m_value_str(), + m_location_str(), + m_summary_str(), + m_children(), + m_synthetic_children() +{ +} + +//---------------------------------------------------------------------- +// Destructor +//---------------------------------------------------------------------- +ValueObject::~ValueObject () +{ +} + +user_id_t +ValueObject::GetUpdateID() const +{ + return m_update_id; +} + +bool +ValueObject::UpdateValueIfNeeded (ExecutionContextScope *exe_scope) +{ + if (exe_scope) + { + Process *process = exe_scope->CalculateProcess(); + if (process) + { + const user_id_t stop_id = process->GetStopID(); + if (m_update_id != stop_id) + { + m_value_str.clear(); + m_location_str.clear(); + m_summary_str.clear(); + + UpdateValue (exe_scope); + if (m_error.Success()) + m_update_id = stop_id; + } + } + } + return m_error.Success(); +} + +const DataExtractor & +ValueObject::GetDataExtractor () const +{ + return m_data; +} + +DataExtractor & +ValueObject::GetDataExtractor () +{ + return m_data; +} + +const Error & +ValueObject::GetError() const +{ + return m_error; +} + +const ConstString & +ValueObject::GetName() const +{ + return m_name; +} + +const char * +ValueObject::GetLocationAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_location_str.empty()) + { + StreamString sstr; + + switch (m_value.GetValueType()) + { + default: + break; + + case Value::eValueTypeScalar: + if (m_value.GetContextType() == Value::eContextTypeDCRegisterInfo) + { + RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + if (reg_info->name) + m_location_str = reg_info->name; + else if (reg_info->alt_name) + m_location_str = reg_info->alt_name; + break; + } + } + m_location_str = "scalar"; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t addr_nibble_size = m_data.GetAddressByteSize() * 2; + sstr.Printf("0x%*.*llx", addr_nibble_size, addr_nibble_size, m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS)); + m_location_str.swap(sstr.GetString()); + } + break; + } + } + } + return m_location_str.c_str(); +} + +Value & +ValueObject::GetValue() +{ + return m_value; +} + +const Value & +ValueObject::GetValue() const +{ + return m_value; +} + +bool +ValueObject::GetValueIsValid () +{ + return m_flags.IsSet(eValueIsValid); +} + + +void +ValueObject::SetValueIsValid (bool b) +{ + if (b) + m_flags.Set(eValueIsValid); + else + m_flags.Clear(eValueIsValid); +} + +bool +ValueObject::GetValueDidChange () const +{ + return m_flags.IsSet(eValueChanged); +} + +void +ValueObject::SetValueDidChange (bool value_changed) +{ + m_flags.Set(eValueChanged); +} + +ValueObjectSP +ValueObject::GetChildAtIndex (uint32_t idx, bool can_create) +{ + ValueObjectSP child_sp; + if (idx < GetNumChildren()) + { + // Check if we have already made the child value object? + if (can_create && m_children[idx].get() == NULL) + { + // No we haven't created the child at this index, so lets have our + // subclass do it and cache the result for quick future access. + m_children[idx] = CreateChildAtIndex (idx, false, 0); + } + + child_sp = m_children[idx]; + } + return child_sp; +} + +uint32_t +ValueObject::GetIndexOfChildWithName (const ConstString &name) +{ + bool omit_empty_base_classes = true; + return ClangASTContext::GetIndexOfChildWithName (GetClangAST(), + GetOpaqueClangQualType(), + name.AsCString(), + omit_empty_base_classes); +} + +ValueObjectSP +ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create) +{ + // when getting a child by name, it could be burried inside some base + // classes (which really aren't part of the expression path), so we + // need a vector of indexes that can get us down to the correct child + std::vector<uint32_t> child_indexes; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + bool omit_empty_base_classes = true; + const size_t num_child_indexes = ClangASTContext::GetIndexOfChildMemberWithName (clang_ast, + clang_type, + name.AsCString(), + omit_empty_base_classes, + child_indexes); + ValueObjectSP child_sp; + if (num_child_indexes > 0) + { + std::vector<uint32_t>::const_iterator pos = child_indexes.begin (); + std::vector<uint32_t>::const_iterator end = child_indexes.end (); + + child_sp = GetChildAtIndex(*pos, can_create); + for (++pos; pos != end; ++pos) + { + if (child_sp) + { + ValueObjectSP new_child_sp(child_sp->GetChildAtIndex (*pos, can_create)); + child_sp = new_child_sp; + } + else + { + child_sp.reset(); + } + + } + } + return child_sp; +} + + +uint32_t +ValueObject::GetNumChildren () +{ + if (m_flags.IsClear(eNumChildrenHasBeenSet)) + { + SetNumChildren (CalculateNumChildren()); + } + return m_children.size(); +} +void +ValueObject::SetNumChildren (uint32_t num_children) +{ + m_flags.Set(eNumChildrenHasBeenSet); + m_children.resize(num_children); +} + +void +ValueObject::SetName (const char *name) +{ + m_name.SetCString(name); +} + +void +ValueObject::SetName (const ConstString &name) +{ + m_name = name; +} + +ValueObjectSP +ValueObject::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int32_t synthetic_index) +{ + ValueObjectSP valobj_sp; + bool omit_empty_base_classes = true; + + std::string child_name_str; + uint32_t child_byte_size = 0; + int32_t child_byte_offset = 0; + uint32_t child_bitfield_bit_size = 0; + uint32_t child_bitfield_bit_offset = 0; + const bool transparent_pointers = synthetic_array_member == false; + clang::ASTContext *clang_ast = GetClangAST(); + void *clang_type = GetOpaqueClangQualType(); + void *child_clang_type; + child_clang_type = ClangASTContext::GetChildClangTypeAtIndex (clang_ast, + GetName().AsCString(), + clang_type, + idx, + transparent_pointers, + omit_empty_base_classes, + child_name_str, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset); + if (child_clang_type) + { + if (synthetic_index) + child_byte_offset += child_byte_size * synthetic_index; + + ConstString child_name; + if (!child_name_str.empty()) + child_name.SetCString (child_name_str.c_str()); + + valobj_sp.reset (new ValueObjectChild (this, + clang_ast, + child_clang_type, + child_name, + child_byte_size, + child_byte_offset, + child_bitfield_bit_size, + child_bitfield_bit_offset)); + } + return valobj_sp; +} + +const char * +ValueObject::GetSummaryAsCString (ExecutionContextScope *exe_scope) +{ + if (UpdateValueIfNeeded (exe_scope)) + { + if (m_summary_str.empty()) + { + void *clang_type = GetOpaqueClangQualType(); + + // See if this is a pointer to a C string? + uint32_t fixed_length = 0; + if (clang_type && ClangASTContext::IsCStringType (clang_type, fixed_length)) + { + Process *process = exe_scope->CalculateProcess(); + if (process != NULL) + { + StreamString sstr; + lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS; + lldb::AddressType cstr_address_type = eAddressTypeInvalid; + switch (GetValue().GetValueType()) + { + case Value::eValueTypeScalar: + cstr_address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + cstr_address_type = eAddressTypeLoad; + break; + + case Value::eValueTypeLoadAddress: + case Value::eValueTypeFileAddress: + case Value::eValueTypeHostAddress: + { + uint32_t data_offset = 0; + cstr_address = m_data.GetPointer(&data_offset); + cstr_address_type = m_value.GetValueAddressType(); + if (cstr_address_type == eAddressTypeInvalid) + cstr_address_type = eAddressTypeLoad; + } + break; + } + + if (cstr_address != LLDB_INVALID_ADDRESS) + { + DataExtractor data; + size_t bytes_read = 0; + std::vector<char> data_buffer; + std::vector<char> cstr_buffer; + size_t cstr_length; + Error error; + if (fixed_length > 0) + { + data_buffer.resize(fixed_length); + // Resize the formatted buffer in case every character + // uses the "\xXX" format and one extra byte for a NULL + cstr_buffer.resize(data_buffer.size() * 4 + 1); + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), fixed_length, error); + if (bytes_read > 0) + { + sstr << '"'; + cstr_length = data.Dump (&sstr, + 0, // Start offset in "data" + eFormatChar, // Print as characters + 1, // Size of item (1 byte for a char!) + bytes_read, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + sstr << '"'; + } + } + else + { + const size_t k_max_buf_size = 256; + data_buffer.resize (k_max_buf_size + 1); + // NULL terminate in case we don't get the entire C string + data_buffer.back() = '\0'; + // Make a formatted buffer that can contain take 4 + // bytes per character in case each byte uses the + // "\xXX" format and one extra byte for a NULL + cstr_buffer.resize (k_max_buf_size * 4 + 1); + + data.SetData (data_buffer.data(), data_buffer.size(), eByteOrderHost); + size_t total_cstr_len = 0; + while ((bytes_read = process->ReadMemory (cstr_address, data_buffer.data(), k_max_buf_size, error)) > 0) + { + size_t len = strlen(data_buffer.data()); + if (len == 0) + break; + if (len > bytes_read) + len = bytes_read; + if (sstr.GetSize() == 0) + sstr << '"'; + + cstr_length = data.Dump (&sstr, + 0, // Start offset in "data" + eFormatChar, // Print as characters + 1, // Size of item (1 byte for a char!) + len, // How many bytes to print? + UINT32_MAX, // num per line + LLDB_INVALID_ADDRESS,// base address + 0, // bitfield bit size + 0); // bitfield bit offset + + if (len < k_max_buf_size) + break; + cstr_address += total_cstr_len; + } + if (sstr.GetSize() > 0) + sstr << '"'; + } + + if (sstr.GetSize() > 0) + m_summary_str.assign (sstr.GetData(), sstr.GetSize()); + } + } + } + } + } + if (m_summary_str.empty()) + return NULL; + return m_summary_str.c_str(); +} + + +const char * +ValueObject::GetValueAsCString (ExecutionContextScope *exe_scope) +{ + // If our byte size is zero this is an aggregate type that has children + if (ClangASTContext::IsAggregateType (GetOpaqueClangQualType()) == false) + { + if (UpdateValueIfNeeded(exe_scope)) + { + if (m_value_str.empty()) + { + const Value::ContextType context_type = m_value.GetContextType(); + + switch (context_type) + { + case Value::eContextTypeOpaqueClangQualType: + case Value::eContextTypeDCType: + case Value::eContextTypeDCVariable: + { + void *clang_type = GetOpaqueClangQualType (); + if (clang_type) + { + StreamString sstr; + lldb::Format format = Type::GetFormat(clang_type); + if (Type::DumpTypeValue(&sstr, + GetClangAST(), // The clang AST + clang_type, // The clang type to display + format, // Format to display this type with + m_data, // Data to extract from + 0, // Byte offset into "m_data" + GetByteSize(), // Byte size of item in "m_data" + GetBitfieldBitSize(), // Bitfield bit size + GetBitfieldBitOffset())) // Bitfield bit offset + m_value_str.swap(sstr.GetString()); + else + m_value_str.clear(); + } + } + break; + + case Value::eContextTypeDCRegisterInfo: + { + const RegisterInfo *reg_info = m_value.GetRegisterInfo(); + if (reg_info) + { + StreamString reg_sstr; + m_data.Dump(®_sstr, 0, reg_info->format, reg_info->byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); + m_value_str.swap(reg_sstr.GetString()); + } + } + break; + } + } + } + } + if (m_value_str.empty()) + return NULL; + return m_value_str.c_str(); +} + +bool +ValueObject::SetValueFromCString (ExecutionContextScope *exe_scope, const char *value_str) +{ + // Make sure our value is up to date first so that our location and location + // type is valid. + if (!UpdateValueIfNeeded(exe_scope)) + return false; + + uint32_t count = 0; + lldb::Encoding encoding = Type::GetEncoding (GetOpaqueClangQualType(), count); + + char *end = NULL; + size_t byte_size = GetByteSize(); + switch (encoding) + { + case eEncodingInvalid: + return false; + + case eEncodingUint: + if (byte_size > sizeof(unsigned long long)) + { + return false; + } + else + { + unsigned long long ull_val = strtoull(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = ull_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingSint: + if (byte_size > sizeof(long long)) + { + return false; + } + else + { + long long sll_val = strtoll(value_str, &end, 0); + if (end && *end != '\0') + return false; + m_value = sll_val; + // Limit the bytes in our m_data appropriately. + m_value.GetScalar().GetData (m_data, byte_size); + } + break; + + case eEncodingIEEE754: + { + const size_t byte_size = GetByteSize(); + const off_t byte_offset = GetByteOffset(); + uint8_t *dst = (uint8_t *)m_data.PeekData(byte_offset, byte_size); + if (dst != NULL) + { + // We are decoding a float into host byte order below, so make + // sure m_data knows what it contains. + m_data.SetByteOrder(eByteOrderHost); + const size_t converted_byte_size = ClangASTContext::ConvertStringToFloatValue ( + GetClangAST(), + GetOpaqueClangQualType(), + value_str, + dst, + byte_size); + + if (converted_byte_size == byte_size) + { + } + } + } + break; + + case eEncodingVector: + return false; + + default: + return false; + } + + // If we have made it here the value is in m_data and we should write it + // out to the target + return Write (); +} + +bool +ValueObject::Write () +{ + // Clear the update ID so the next time we try and read the value + // we try and read it again. + m_update_id = 0; + + // TODO: when Value has a method to write a value back, call it from here. + return false; + +} + +void +ValueObject::AddSyntheticChild (const ConstString &key, ValueObjectSP& valobj_sp) +{ + m_synthetic_children[key] = valobj_sp; +} + +ValueObjectSP +ValueObject::GetSyntheticChild (const ConstString &key) const +{ + ValueObjectSP synthetic_child_sp; + std::map<ConstString, ValueObjectSP>::const_iterator pos = m_synthetic_children.find (key); + if (pos != m_synthetic_children.end()) + synthetic_child_sp = pos->second; + return synthetic_child_sp; +} + +bool +ValueObject::IsPointerType () +{ + return ClangASTContext::IsPointerType (GetOpaqueClangQualType()); +} + +bool +ValueObject::IsPointerOrReferenceType () +{ + return ClangASTContext::IsPointerOrReferenceType(GetOpaqueClangQualType()); +} + +ValueObjectSP +ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create) +{ + ValueObjectSP synthetic_child_sp; + if (IsPointerType ()) + { + char index_str[64]; + snprintf(index_str, sizeof(index_str), "[%i]", index); + ConstString index_const_str(index_str); + // Check if we have already created a synthetic array member in this + // valid object. If we have we will re-use it. + synthetic_child_sp = GetSyntheticChild (index_const_str); + if (!synthetic_child_sp) + { + // We haven't made a synthetic array member for INDEX yet, so + // lets make one and cache it for any future reference. + synthetic_child_sp = CreateChildAtIndex(0, true, index); + + // Cache the value if we got one back... + if (synthetic_child_sp) + AddSyntheticChild(index_const_str, synthetic_child_sp); + } + } + return synthetic_child_sp; +} |