2010-06-09 00:52:24 +08:00
|
|
|
//===-- 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
|
2010-09-15 07:36:40 +08:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
// C++ Includes
|
|
|
|
// Other libraries and framework includes
|
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "clang/AST/Type.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
// Project includes
|
|
|
|
#include "lldb/Core/DataBufferHeap.h"
|
|
|
|
#include "lldb/Core/StreamString.h"
|
|
|
|
#include "lldb/Core/ValueObjectChild.h"
|
2010-12-14 10:59:59 +08:00
|
|
|
#include "lldb/Core/ValueObjectConstResult.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Core/ValueObjectList.h"
|
|
|
|
|
2011-02-01 09:31:41 +08:00
|
|
|
#include "lldb/Host/Endian.h"
|
|
|
|
|
2010-07-22 06:12:05 +08:00
|
|
|
#include "lldb/Symbol/ClangASTType.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Symbol/ClangASTContext.h"
|
|
|
|
#include "lldb/Symbol/Type.h"
|
|
|
|
|
2010-09-11 07:12:17 +08:00
|
|
|
#include "lldb/Target/ExecutionContext.h"
|
2010-09-28 09:25:32 +08:00
|
|
|
#include "lldb/Target/LanguageRuntime.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Process.h"
|
|
|
|
#include "lldb/Target/RegisterContext.h"
|
2010-09-15 07:36:40 +08:00
|
|
|
#include "lldb/Target/Target.h"
|
2010-06-09 00:52:24 +08:00
|
|
|
#include "lldb/Target/Thread.h"
|
|
|
|
|
|
|
|
using namespace lldb;
|
|
|
|
using namespace lldb_private;
|
|
|
|
|
|
|
|
static lldb::user_id_t g_value_obj_uid = 0;
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// ValueObject constructor
|
|
|
|
//----------------------------------------------------------------------
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::ValueObject (ValueObject &parent) :
|
2010-06-09 00:52:24 +08:00
|
|
|
UserID (++g_value_obj_uid), // Unique identifier for every value object
|
2011-03-31 08:19:25 +08:00
|
|
|
m_parent (&parent),
|
2010-06-09 00:52:24 +08:00
|
|
|
m_name (),
|
|
|
|
m_data (),
|
|
|
|
m_value (),
|
|
|
|
m_error (),
|
2010-09-02 10:59:18 +08:00
|
|
|
m_value_str (),
|
|
|
|
m_old_value_str (),
|
|
|
|
m_location_str (),
|
|
|
|
m_summary_str (),
|
2010-09-11 07:12:17 +08:00
|
|
|
m_object_desc_str (),
|
2010-09-02 10:59:18 +08:00
|
|
|
m_children (),
|
|
|
|
m_synthetic_children (),
|
2010-10-06 11:09:11 +08:00
|
|
|
m_dynamic_value_sp (),
|
|
|
|
m_format (eFormatDefault),
|
2010-09-02 10:59:18 +08:00
|
|
|
m_value_is_valid (false),
|
|
|
|
m_value_did_change (false),
|
|
|
|
m_children_count_valid (false),
|
2010-12-14 10:59:59 +08:00
|
|
|
m_old_value_valid (false),
|
2011-01-21 09:59:00 +08:00
|
|
|
m_pointers_point_to_load_addrs (false),
|
2011-03-31 08:19:25 +08:00
|
|
|
m_is_deref_of_parent (false),
|
|
|
|
m_update_point (parent.GetUpdatePoint ())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
2011-03-31 08:19:25 +08:00
|
|
|
// ValueObject constructor
|
2010-06-09 00:52:24 +08:00
|
|
|
//----------------------------------------------------------------------
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
|
|
|
|
UserID (++g_value_obj_uid), // Unique identifier for every value object
|
|
|
|
m_parent (NULL),
|
|
|
|
m_name (),
|
|
|
|
m_data (),
|
|
|
|
m_value (),
|
|
|
|
m_error (),
|
|
|
|
m_value_str (),
|
|
|
|
m_old_value_str (),
|
|
|
|
m_location_str (),
|
|
|
|
m_summary_str (),
|
|
|
|
m_object_desc_str (),
|
|
|
|
m_children (),
|
|
|
|
m_synthetic_children (),
|
|
|
|
m_dynamic_value_sp (),
|
|
|
|
m_format (eFormatDefault),
|
|
|
|
m_value_is_valid (false),
|
|
|
|
m_value_did_change (false),
|
|
|
|
m_children_count_valid (false),
|
|
|
|
m_old_value_valid (false),
|
|
|
|
m_pointers_point_to_load_addrs (false),
|
|
|
|
m_is_deref_of_parent (false),
|
|
|
|
m_update_point (exe_scope)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Destructor
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
ValueObject::~ValueObject ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::UpdateValueIfNeeded ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-10-05 11:13:51 +08:00
|
|
|
// If this is a constant value, then our success is predicated on whether
|
|
|
|
// we have an error or not
|
|
|
|
if (GetIsConstant())
|
|
|
|
return m_error.Success();
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
bool first_update = m_update_point.IsFirstEvaluation();
|
|
|
|
|
|
|
|
if (m_update_point.NeedsUpdating())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
m_update_point.SetUpdated();
|
|
|
|
|
|
|
|
// Save the old value using swap to avoid a string copy which
|
|
|
|
// also will clear our m_value_str
|
|
|
|
if (m_value_str.empty())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
m_old_value_valid = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_old_value_valid = true;
|
|
|
|
m_old_value_str.swap (m_value_str);
|
|
|
|
m_value_str.clear();
|
|
|
|
}
|
|
|
|
m_location_str.clear();
|
|
|
|
m_summary_str.clear();
|
|
|
|
m_object_desc_str.clear();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
const bool value_was_valid = GetValueIsValid();
|
|
|
|
SetValueDidChange (false);
|
2010-08-28 08:08:07 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
m_error.Clear();
|
2010-08-28 08:08:07 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
// Call the pure virtual function to update the value
|
|
|
|
bool success = UpdateValue ();
|
|
|
|
|
|
|
|
SetValueIsValid (success);
|
|
|
|
|
|
|
|
if (first_update)
|
|
|
|
SetValueDidChange (false);
|
|
|
|
else if (!m_value_did_change && success == false)
|
|
|
|
{
|
|
|
|
// The value wasn't gotten successfully, so we mark this
|
|
|
|
// as changed if the value used to be valid and now isn't
|
|
|
|
SetValueDidChange (value_was_valid);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
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 *
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::GetLocationAsCString ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (UpdateValueIfNeeded())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
if (m_location_str.empty())
|
|
|
|
{
|
|
|
|
StreamString sstr;
|
|
|
|
|
|
|
|
switch (m_value.GetValueType())
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Value::eValueTypeScalar:
|
2010-11-13 11:52:47 +08:00
|
|
|
if (m_value.GetContextType() == Value::eContextTypeRegisterInfo)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-11-04 09:54:29 +08:00
|
|
|
bool
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::ResolveValue (Scalar &scalar)
|
2010-11-04 09:54:29 +08:00
|
|
|
{
|
|
|
|
ExecutionContext exe_ctx;
|
2011-03-31 08:19:25 +08:00
|
|
|
ExecutionContextScope *exe_scope = GetExecutionContextScope();
|
|
|
|
if (exe_scope)
|
|
|
|
exe_scope->CalculateExecutionContext(exe_ctx);
|
2010-11-04 09:54:29 +08:00
|
|
|
scalar = m_value.ResolveValue(&exe_ctx, GetClangAST ());
|
|
|
|
return scalar.IsValid();
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
bool
|
2010-09-02 10:59:18 +08:00
|
|
|
ValueObject::GetValueIsValid () const
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-09-02 10:59:18 +08:00
|
|
|
return m_value_is_valid;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
ValueObject::SetValueIsValid (bool b)
|
|
|
|
{
|
2010-09-02 10:59:18 +08:00
|
|
|
m_value_is_valid = b;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::GetValueDidChange ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
GetValueAsCString ();
|
2010-09-02 10:59:18 +08:00
|
|
|
return m_value_did_change;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ValueObject::SetValueDidChange (bool value_changed)
|
|
|
|
{
|
2010-09-02 10:59:18 +08:00
|
|
|
m_value_did_change = value_changed;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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(),
|
2010-09-29 09:12:09 +08:00
|
|
|
GetClangType(),
|
2010-12-14 10:59:59 +08:00
|
|
|
name.GetCString(),
|
2010-06-09 00:52:24 +08:00
|
|
|
omit_empty_base_classes);
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueObjectSP
|
|
|
|
ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create)
|
|
|
|
{
|
2011-01-09 04:28:42 +08:00
|
|
|
// when getting a child by name, it could be buried inside some base
|
2010-06-09 00:52:24 +08:00
|
|
|
// 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();
|
2010-09-29 09:12:09 +08:00
|
|
|
void *clang_type = GetClangType();
|
2010-06-09 00:52:24 +08:00
|
|
|
bool omit_empty_base_classes = true;
|
|
|
|
const size_t num_child_indexes = ClangASTContext::GetIndexOfChildMemberWithName (clang_ast,
|
|
|
|
clang_type,
|
2010-12-14 10:59:59 +08:00
|
|
|
name.GetCString(),
|
2010-06-09 00:52:24 +08:00
|
|
|
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 ()
|
|
|
|
{
|
2010-09-02 10:59:18 +08:00
|
|
|
if (!m_children_count_valid)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
SetNumChildren (CalculateNumChildren());
|
|
|
|
}
|
|
|
|
return m_children.size();
|
|
|
|
}
|
|
|
|
void
|
|
|
|
ValueObject::SetNumChildren (uint32_t num_children)
|
|
|
|
{
|
2010-09-02 10:59:18 +08:00
|
|
|
m_children_count_valid = true;
|
2010-06-09 00:52:24 +08:00
|
|
|
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;
|
2010-10-15 06:52:14 +08:00
|
|
|
bool child_is_base_class = false;
|
2011-01-21 09:59:00 +08:00
|
|
|
bool child_is_deref_of_parent = false;
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
const bool transparent_pointers = synthetic_array_member == false;
|
|
|
|
clang::ASTContext *clang_ast = GetClangAST();
|
2010-10-27 11:32:59 +08:00
|
|
|
clang_type_t clang_type = GetClangType();
|
|
|
|
clang_type_t child_clang_type;
|
2010-06-09 00:52:24 +08:00
|
|
|
child_clang_type = ClangASTContext::GetChildClangTypeAtIndex (clang_ast,
|
2010-12-14 10:59:59 +08:00
|
|
|
GetName().GetCString(),
|
2010-06-09 00:52:24 +08:00
|
|
|
clang_type,
|
|
|
|
idx,
|
|
|
|
transparent_pointers,
|
|
|
|
omit_empty_base_classes,
|
|
|
|
child_name_str,
|
|
|
|
child_byte_size,
|
|
|
|
child_byte_offset,
|
|
|
|
child_bitfield_bit_size,
|
2010-10-15 06:52:14 +08:00
|
|
|
child_bitfield_bit_offset,
|
2011-01-21 09:59:00 +08:00
|
|
|
child_is_base_class,
|
|
|
|
child_is_deref_of_parent);
|
2011-01-09 06:26:47 +08:00
|
|
|
if (child_clang_type && child_byte_size)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
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());
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
valobj_sp.reset (new ValueObjectChild (*this,
|
2010-06-09 00:52:24 +08:00
|
|
|
clang_ast,
|
|
|
|
child_clang_type,
|
|
|
|
child_name,
|
|
|
|
child_byte_size,
|
|
|
|
child_byte_offset,
|
|
|
|
child_bitfield_bit_size,
|
2010-10-15 06:52:14 +08:00
|
|
|
child_bitfield_bit_offset,
|
2011-01-21 09:59:00 +08:00
|
|
|
child_is_base_class,
|
|
|
|
child_is_deref_of_parent));
|
2010-12-14 10:59:59 +08:00
|
|
|
if (m_pointers_point_to_load_addrs)
|
|
|
|
valobj_sp->SetPointersPointToLoadAddrs (m_pointers_point_to_load_addrs);
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
return valobj_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::GetSummaryAsCString ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (UpdateValueIfNeeded ())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
if (m_summary_str.empty())
|
|
|
|
{
|
2010-10-27 11:32:59 +08:00
|
|
|
clang_type_t clang_type = GetClangType();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
// See if this is a pointer to a C string?
|
2010-09-13 11:32:57 +08:00
|
|
|
if (clang_type)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-09-13 11:32:57 +08:00
|
|
|
StreamString sstr;
|
2010-10-27 11:32:59 +08:00
|
|
|
clang_type_t elem_or_pointee_clang_type;
|
|
|
|
const Flags type_flags (ClangASTContext::GetTypeInfo (clang_type,
|
2010-12-14 10:59:59 +08:00
|
|
|
GetClangAST(),
|
|
|
|
&elem_or_pointee_clang_type));
|
2010-09-13 11:32:57 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
ExecutionContextScope *exe_scope = GetExecutionContextScope();
|
|
|
|
if (exe_scope)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (type_flags.AnySet (ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer) &&
|
|
|
|
ClangASTContext::IsCharType (elem_or_pointee_clang_type))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
Process *process = exe_scope->CalculateProcess();
|
|
|
|
if (process != NULL)
|
2010-10-27 11:32:59 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS;
|
|
|
|
AddressType cstr_address_type = eAddressTypeInvalid;
|
|
|
|
|
|
|
|
size_t cstr_len = 0;
|
|
|
|
if (type_flags.Test (ClangASTContext::eTypeIsArray))
|
2010-09-13 11:32:57 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
// We have an array
|
|
|
|
cstr_len = ClangASTContext::GetArraySize (clang_type);
|
|
|
|
cstr_address = GetAddressOf (cstr_address_type, true);
|
2010-09-13 11:32:57 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
// We have a pointer
|
|
|
|
cstr_address = GetPointerValue (cstr_address_type, true);
|
|
|
|
}
|
|
|
|
if (cstr_address != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
DataExtractor data;
|
|
|
|
size_t bytes_read = 0;
|
|
|
|
std::vector<char> data_buffer;
|
|
|
|
Error error;
|
|
|
|
if (cstr_len > 0)
|
|
|
|
{
|
|
|
|
data_buffer.resize(cstr_len);
|
|
|
|
data.SetData (&data_buffer.front(), data_buffer.size(), lldb::endian::InlHostByteOrder());
|
|
|
|
bytes_read = process->ReadMemory (cstr_address, &data_buffer.front(), cstr_len, error);
|
|
|
|
if (bytes_read > 0)
|
|
|
|
{
|
|
|
|
sstr << '"';
|
|
|
|
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';
|
2011-01-17 13:51:02 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
sstr << '"';
|
2010-09-13 11:32:57 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
data.SetData (&data_buffer.front(), data_buffer.size(), endian::InlHostByteOrder());
|
|
|
|
while ((bytes_read = process->ReadMemory (cstr_address, &data_buffer.front(), k_max_buf_size, error)) > 0)
|
|
|
|
{
|
|
|
|
size_t len = strlen(&data_buffer.front());
|
|
|
|
if (len == 0)
|
|
|
|
break;
|
|
|
|
if (len > bytes_read)
|
|
|
|
len = bytes_read;
|
|
|
|
|
|
|
|
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 += k_max_buf_size;
|
|
|
|
}
|
|
|
|
sstr << '"';
|
2010-09-13 11:32:57 +08:00
|
|
|
}
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2011-03-31 08:19:25 +08:00
|
|
|
|
|
|
|
if (sstr.GetSize() > 0)
|
|
|
|
m_summary_str.assign (sstr.GetData(), sstr.GetSize());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
2011-03-31 08:19:25 +08:00
|
|
|
else if (ClangASTContext::IsFunctionPointerType (clang_type))
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
AddressType func_ptr_address_type = eAddressTypeInvalid;
|
|
|
|
lldb::addr_t func_ptr_address = GetPointerValue (func_ptr_address_type, true);
|
2010-09-13 11:32:57 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
if (func_ptr_address != 0 && func_ptr_address != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
switch (func_ptr_address_type)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
case eAddressTypeInvalid:
|
|
|
|
case eAddressTypeFile:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case eAddressTypeLoad:
|
2010-09-13 11:32:57 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
Address so_addr;
|
|
|
|
Target *target = exe_scope->CalculateTarget();
|
|
|
|
if (target && target->GetSectionLoadList().IsEmpty() == false)
|
2010-09-13 11:32:57 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (target->GetSectionLoadList().ResolveLoadAddress(func_ptr_address, so_addr))
|
|
|
|
{
|
|
|
|
so_addr.Dump (&sstr,
|
|
|
|
exe_scope,
|
|
|
|
Address::DumpStyleResolvedDescription,
|
|
|
|
Address::DumpStyleSectionNameOffset);
|
|
|
|
}
|
2010-09-13 11:32:57 +08:00
|
|
|
}
|
|
|
|
}
|
2011-03-31 08:19:25 +08:00
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
case eAddressTypeHost:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sstr.GetSize() > 0)
|
|
|
|
{
|
|
|
|
m_summary_str.assign (1, '(');
|
|
|
|
m_summary_str.append (sstr.GetData(), sstr.GetSize());
|
|
|
|
m_summary_str.append (1, ')');
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_summary_str.empty())
|
|
|
|
return NULL;
|
|
|
|
return m_summary_str.c_str();
|
|
|
|
}
|
|
|
|
|
2010-09-11 07:12:17 +08:00
|
|
|
const char *
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::GetObjectDescription ()
|
2010-09-11 07:12:17 +08:00
|
|
|
{
|
|
|
|
if (!m_object_desc_str.empty())
|
|
|
|
return m_object_desc_str.c_str();
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
if (!UpdateValueIfNeeded ())
|
2010-09-11 07:12:17 +08:00
|
|
|
return NULL;
|
2011-03-31 08:19:25 +08:00
|
|
|
|
|
|
|
ExecutionContextScope *exe_scope = GetExecutionContextScope();
|
|
|
|
if (exe_scope == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2010-09-11 07:12:17 +08:00
|
|
|
Process *process = exe_scope->CalculateProcess();
|
2010-09-28 09:25:32 +08:00
|
|
|
if (process == NULL)
|
2010-09-11 07:12:17 +08:00
|
|
|
return NULL;
|
2010-09-28 09:25:32 +08:00
|
|
|
|
|
|
|
StreamString s;
|
2010-09-11 07:12:17 +08:00
|
|
|
|
2010-09-28 09:25:32 +08:00
|
|
|
lldb::LanguageType language = GetObjectRuntimeLanguage();
|
|
|
|
LanguageRuntime *runtime = process->GetLanguageRuntime(language);
|
2010-09-11 07:12:17 +08:00
|
|
|
|
2010-12-23 10:29:54 +08:00
|
|
|
if (runtime == NULL)
|
|
|
|
{
|
2011-03-18 08:05:18 +08:00
|
|
|
// Aw, hell, if the things a pointer, or even just an integer, let's try ObjC anyway...
|
2010-12-23 10:29:54 +08:00
|
|
|
clang_type_t opaque_qual_type = GetClangType();
|
|
|
|
if (opaque_qual_type != NULL)
|
|
|
|
{
|
2011-03-18 08:05:18 +08:00
|
|
|
bool is_signed;
|
|
|
|
if (ClangASTContext::IsIntegerType (opaque_qual_type, is_signed)
|
|
|
|
|| ClangASTContext::IsPointerType (opaque_qual_type))
|
|
|
|
{
|
2010-12-23 10:29:54 +08:00
|
|
|
runtime = process->GetLanguageRuntime(lldb::eLanguageTypeObjC);
|
2011-03-18 08:05:18 +08:00
|
|
|
}
|
2010-12-23 10:29:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-04-01 07:01:21 +08:00
|
|
|
if (runtime && runtime->GetObjectDescription(s, *this))
|
2010-09-11 07:12:17 +08:00
|
|
|
{
|
|
|
|
m_object_desc_str.append (s.GetData());
|
|
|
|
}
|
2010-10-23 08:18:49 +08:00
|
|
|
|
|
|
|
if (m_object_desc_str.empty())
|
|
|
|
return NULL;
|
|
|
|
else
|
|
|
|
return m_object_desc_str.c_str();
|
2010-09-11 07:12:17 +08:00
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
const char *
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::GetValueAsCString ()
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
// If our byte size is zero this is an aggregate type that has children
|
2010-09-29 09:12:09 +08:00
|
|
|
if (ClangASTContext::IsAggregateType (GetClangType()) == false)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (UpdateValueIfNeeded())
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
if (m_value_str.empty())
|
|
|
|
{
|
|
|
|
const Value::ContextType context_type = m_value.GetContextType();
|
|
|
|
|
|
|
|
switch (context_type)
|
|
|
|
{
|
2010-11-13 11:52:47 +08:00
|
|
|
case Value::eContextTypeClangType:
|
|
|
|
case Value::eContextTypeLLDBType:
|
|
|
|
case Value::eContextTypeVariable:
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
2010-10-27 11:32:59 +08:00
|
|
|
clang_type_t clang_type = GetClangType ();
|
2010-06-09 00:52:24 +08:00
|
|
|
if (clang_type)
|
|
|
|
{
|
|
|
|
StreamString sstr;
|
2010-10-06 11:09:11 +08:00
|
|
|
if (m_format == eFormatDefault)
|
|
|
|
m_format = ClangASTType::GetFormat(clang_type);
|
|
|
|
|
|
|
|
if (ClangASTType::DumpTypeValue (GetClangAST(), // The clang AST
|
|
|
|
clang_type, // The clang type to display
|
|
|
|
&sstr,
|
|
|
|
m_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
|
2010-06-09 00:52:24 +08:00
|
|
|
m_value_str.swap(sstr.GetString());
|
|
|
|
else
|
|
|
|
m_value_str.clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-11-13 11:52:47 +08:00
|
|
|
case Value::eContextTypeRegisterInfo:
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
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;
|
2010-07-10 04:39:50 +08:00
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
2010-09-02 10:59:18 +08:00
|
|
|
|
|
|
|
if (!m_value_did_change && m_old_value_valid)
|
|
|
|
{
|
|
|
|
// The value was gotten successfully, so we consider the
|
|
|
|
// value as changed if the value string differs
|
|
|
|
SetValueDidChange (m_old_value_str != m_value_str);
|
|
|
|
}
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (m_value_str.empty())
|
|
|
|
return NULL;
|
|
|
|
return m_value_str.c_str();
|
|
|
|
}
|
|
|
|
|
2010-10-27 11:32:59 +08:00
|
|
|
addr_t
|
2011-03-25 05:19:54 +08:00
|
|
|
ValueObject::GetAddressOf (AddressType &address_type, bool scalar_is_load_address)
|
2010-10-27 11:32:59 +08:00
|
|
|
{
|
|
|
|
switch (m_value.GetValueType())
|
|
|
|
{
|
|
|
|
case Value::eValueTypeScalar:
|
|
|
|
if (scalar_is_load_address)
|
|
|
|
{
|
|
|
|
address_type = eAddressTypeLoad;
|
|
|
|
return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Value::eValueTypeLoadAddress:
|
|
|
|
case Value::eValueTypeFileAddress:
|
|
|
|
case Value::eValueTypeHostAddress:
|
|
|
|
{
|
|
|
|
address_type = m_value.GetValueAddressType ();
|
|
|
|
return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
address_type = eAddressTypeInvalid;
|
|
|
|
return LLDB_INVALID_ADDRESS;
|
|
|
|
}
|
|
|
|
|
2010-09-13 11:32:57 +08:00
|
|
|
addr_t
|
2011-03-25 05:19:54 +08:00
|
|
|
ValueObject::GetPointerValue (AddressType &address_type, bool scalar_is_load_address)
|
2010-09-13 11:32:57 +08:00
|
|
|
{
|
|
|
|
lldb::addr_t address = LLDB_INVALID_ADDRESS;
|
|
|
|
address_type = eAddressTypeInvalid;
|
2010-10-27 11:32:59 +08:00
|
|
|
switch (m_value.GetValueType())
|
2010-09-13 11:32:57 +08:00
|
|
|
{
|
|
|
|
case Value::eValueTypeScalar:
|
|
|
|
if (scalar_is_load_address)
|
|
|
|
{
|
|
|
|
address = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS);
|
|
|
|
address_type = eAddressTypeLoad;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Value::eValueTypeLoadAddress:
|
|
|
|
case Value::eValueTypeFileAddress:
|
|
|
|
case Value::eValueTypeHostAddress:
|
|
|
|
{
|
|
|
|
uint32_t data_offset = 0;
|
|
|
|
address = m_data.GetPointer(&data_offset);
|
|
|
|
address_type = m_value.GetValueAddressType();
|
|
|
|
if (address_type == eAddressTypeInvalid)
|
|
|
|
address_type = eAddressTypeLoad;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2010-12-14 10:59:59 +08:00
|
|
|
|
|
|
|
if (m_pointers_point_to_load_addrs)
|
|
|
|
address_type = eAddressTypeLoad;
|
|
|
|
|
2010-09-13 11:32:57 +08:00
|
|
|
return address;
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
bool
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::SetValueFromCString (const char *value_str)
|
2010-06-09 00:52:24 +08:00
|
|
|
{
|
|
|
|
// Make sure our value is up to date first so that our location and location
|
|
|
|
// type is valid.
|
2011-03-31 08:19:25 +08:00
|
|
|
if (!UpdateValueIfNeeded())
|
2010-06-09 00:52:24 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
uint32_t count = 0;
|
2010-09-29 09:12:09 +08:00
|
|
|
lldb::Encoding encoding = ClangASTType::GetEncoding (GetClangType(), count);
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
char *end = NULL;
|
2010-07-14 08:18:15 +08:00
|
|
|
const size_t byte_size = GetByteSize();
|
2010-06-09 00:52:24 +08:00
|
|
|
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 off_t byte_offset = GetByteOffset();
|
2010-07-10 04:39:50 +08:00
|
|
|
uint8_t *dst = const_cast<uint8_t *>(m_data.PeekData(byte_offset, byte_size));
|
2010-06-09 00:52:24 +08:00
|
|
|
if (dst != NULL)
|
|
|
|
{
|
|
|
|
// We are decoding a float into host byte order below, so make
|
|
|
|
// sure m_data knows what it contains.
|
2011-02-01 09:31:41 +08:00
|
|
|
m_data.SetByteOrder(lldb::endian::InlHostByteOrder());
|
2010-06-09 00:52:24 +08:00
|
|
|
const size_t converted_byte_size = ClangASTContext::ConvertStringToFloatValue (
|
|
|
|
GetClangAST(),
|
2010-09-29 09:12:09 +08:00
|
|
|
GetClangType(),
|
2010-06-09 00:52:24 +08:00
|
|
|
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.
|
2011-03-31 08:19:25 +08:00
|
|
|
m_update_point.SetNeedsUpdate();
|
2010-06-09 00:52:24 +08:00
|
|
|
|
|
|
|
// TODO: when Value has a method to write a value back, call it from here.
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-09-28 09:25:32 +08:00
|
|
|
lldb::LanguageType
|
|
|
|
ValueObject::GetObjectRuntimeLanguage ()
|
|
|
|
{
|
2010-10-27 11:32:59 +08:00
|
|
|
clang_type_t opaque_qual_type = GetClangType();
|
2010-09-28 09:25:32 +08:00
|
|
|
if (opaque_qual_type == NULL)
|
|
|
|
return lldb::eLanguageTypeC;
|
|
|
|
|
|
|
|
// If the type is a reference, then resolve it to what it refers to first:
|
|
|
|
clang::QualType qual_type (clang::QualType::getFromOpaquePtr(opaque_qual_type).getNonReferenceType());
|
|
|
|
if (qual_type->isAnyPointerType())
|
|
|
|
{
|
|
|
|
if (qual_type->isObjCObjectPointerType())
|
|
|
|
return lldb::eLanguageTypeObjC;
|
|
|
|
|
|
|
|
clang::QualType pointee_type (qual_type->getPointeeType());
|
|
|
|
if (pointee_type->getCXXRecordDeclForPointerType() != NULL)
|
|
|
|
return lldb::eLanguageTypeC_plus_plus;
|
|
|
|
if (pointee_type->isObjCObjectOrInterfaceType())
|
|
|
|
return lldb::eLanguageTypeObjC;
|
|
|
|
if (pointee_type->isObjCClassType())
|
|
|
|
return lldb::eLanguageTypeObjC;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ClangASTContext::IsObjCClassType (opaque_qual_type))
|
|
|
|
return lldb::eLanguageTypeObjC;
|
2010-09-29 00:10:54 +08:00
|
|
|
if (ClangASTContext::IsCXXClassType (opaque_qual_type))
|
2010-09-28 09:25:32 +08:00
|
|
|
return lldb::eLanguageTypeC_plus_plus;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lldb::eLanguageTypeC;
|
|
|
|
}
|
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
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 ()
|
|
|
|
{
|
2010-09-29 09:12:09 +08:00
|
|
|
return ClangASTContext::IsPointerType (GetClangType());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
2011-03-18 08:05:18 +08:00
|
|
|
bool
|
|
|
|
ValueObject::IsIntegerType (bool &is_signed)
|
|
|
|
{
|
|
|
|
return ClangASTContext::IsIntegerType (GetClangType(), is_signed);
|
|
|
|
}
|
2010-10-27 11:32:59 +08:00
|
|
|
|
2010-06-09 00:52:24 +08:00
|
|
|
bool
|
|
|
|
ValueObject::IsPointerOrReferenceType ()
|
|
|
|
{
|
2010-09-29 09:12:09 +08:00
|
|
|
return ClangASTContext::IsPointerOrReferenceType(GetClangType());
|
2010-06-09 00:52:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2010-09-23 10:01:19 +08:00
|
|
|
|
|
|
|
bool
|
|
|
|
ValueObject::SetDynamicValue ()
|
|
|
|
{
|
|
|
|
if (!IsPointerOrReferenceType())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Check that the runtime class is correct for determining the most specific class.
|
|
|
|
// If it is a C++ class, see if it is dynamic:
|
2010-09-28 09:25:32 +08:00
|
|
|
|
2010-09-23 10:01:19 +08:00
|
|
|
return true;
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2011-01-21 09:59:00 +08:00
|
|
|
bool
|
|
|
|
ValueObject::GetBaseClassPath (Stream &s)
|
|
|
|
{
|
|
|
|
if (IsBaseClass())
|
|
|
|
{
|
|
|
|
bool parent_had_base_class = m_parent && m_parent->GetBaseClassPath (s);
|
|
|
|
clang_type_t clang_type = GetClangType();
|
|
|
|
std::string cxx_class_name;
|
|
|
|
bool this_had_base_class = ClangASTContext::GetCXXClassName (clang_type, cxx_class_name);
|
|
|
|
if (this_had_base_class)
|
|
|
|
{
|
|
|
|
if (parent_had_base_class)
|
|
|
|
s.PutCString("::");
|
|
|
|
s.PutCString(cxx_class_name.c_str());
|
|
|
|
}
|
|
|
|
return parent_had_base_class || this_had_base_class;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ValueObject *
|
|
|
|
ValueObject::GetNonBaseClassParent()
|
|
|
|
{
|
|
|
|
if (m_parent)
|
|
|
|
{
|
|
|
|
if (m_parent->IsBaseClass())
|
|
|
|
return m_parent->GetNonBaseClassParent();
|
|
|
|
else
|
|
|
|
return m_parent;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
void
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes)
|
2010-10-15 06:52:14 +08:00
|
|
|
{
|
2011-01-21 09:59:00 +08:00
|
|
|
const bool is_deref_of_parent = IsDereferenceOfParent ();
|
|
|
|
|
|
|
|
if (is_deref_of_parent)
|
|
|
|
s.PutCString("*(");
|
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
if (m_parent)
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
m_parent->GetExpressionPath (s, qualify_cxx_base_classes);
|
2011-01-21 09:59:00 +08:00
|
|
|
|
|
|
|
if (!IsBaseClass())
|
|
|
|
{
|
|
|
|
if (!is_deref_of_parent)
|
2010-10-15 06:52:14 +08:00
|
|
|
{
|
2011-01-21 09:59:00 +08:00
|
|
|
ValueObject *non_base_class_parent = GetNonBaseClassParent();
|
|
|
|
if (non_base_class_parent)
|
2010-10-15 06:52:14 +08:00
|
|
|
{
|
2011-01-21 09:59:00 +08:00
|
|
|
clang_type_t non_base_class_parent_clang_type = non_base_class_parent->GetClangType();
|
|
|
|
if (non_base_class_parent_clang_type)
|
|
|
|
{
|
|
|
|
const uint32_t non_base_class_parent_type_info = ClangASTContext::GetTypeInfo (non_base_class_parent_clang_type, NULL, NULL);
|
|
|
|
|
|
|
|
if (non_base_class_parent_type_info & ClangASTContext::eTypeIsPointer)
|
|
|
|
{
|
|
|
|
s.PutCString("->");
|
|
|
|
}
|
|
|
|
else if ((non_base_class_parent_type_info & ClangASTContext::eTypeHasChildren) &&
|
|
|
|
!(non_base_class_parent_type_info & ClangASTContext::eTypeIsArray))
|
|
|
|
{
|
|
|
|
s.PutChar('.');
|
|
|
|
}
|
|
|
|
}
|
2010-10-15 06:52:14 +08:00
|
|
|
}
|
2011-01-21 09:59:00 +08:00
|
|
|
|
|
|
|
const char *name = GetName().GetCString();
|
|
|
|
if (name)
|
2010-10-15 06:52:14 +08:00
|
|
|
{
|
2011-01-21 09:59:00 +08:00
|
|
|
if (qualify_cxx_base_classes)
|
|
|
|
{
|
|
|
|
if (GetBaseClassPath (s))
|
|
|
|
s.PutCString("::");
|
|
|
|
}
|
|
|
|
s.PutCString(name);
|
2010-10-15 06:52:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-21 09:59:00 +08:00
|
|
|
if (is_deref_of_parent)
|
|
|
|
s.PutChar(')');
|
2010-10-15 06:52:14 +08:00
|
|
|
}
|
|
|
|
|
2010-10-05 08:00:42 +08:00
|
|
|
void
|
|
|
|
ValueObject::DumpValueObject
|
|
|
|
(
|
|
|
|
Stream &s,
|
|
|
|
ValueObject *valobj,
|
|
|
|
const char *root_valobj_name,
|
|
|
|
uint32_t ptr_depth,
|
|
|
|
uint32_t curr_depth,
|
|
|
|
uint32_t max_depth,
|
|
|
|
bool show_types,
|
|
|
|
bool show_location,
|
|
|
|
bool use_objc,
|
2010-10-15 06:52:14 +08:00
|
|
|
bool scope_already_checked,
|
|
|
|
bool flat_output
|
2010-10-05 08:00:42 +08:00
|
|
|
)
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
if (valobj && valobj->UpdateValueIfNeeded ())
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
clang_type_t clang_type = valobj->GetClangType();
|
|
|
|
|
2010-10-27 11:32:59 +08:00
|
|
|
const Flags type_flags (ClangASTContext::GetTypeInfo (clang_type, NULL, NULL));
|
2010-10-15 06:52:14 +08:00
|
|
|
const char *err_cstr = NULL;
|
2010-10-27 11:32:59 +08:00
|
|
|
const bool has_children = type_flags.Test (ClangASTContext::eTypeHasChildren);
|
|
|
|
const bool has_value = type_flags.Test (ClangASTContext::eTypeHasValue);
|
2010-10-15 06:52:14 +08:00
|
|
|
|
|
|
|
const bool print_valobj = flat_output == false || has_value;
|
|
|
|
|
|
|
|
if (print_valobj)
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
if (show_location)
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
s.Printf("%s: ", valobj->GetLocationAsCString());
|
2010-10-15 06:52:14 +08:00
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
s.Indent();
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-11-02 09:50:16 +08:00
|
|
|
// Always show the type for the top level items.
|
2011-01-21 09:59:00 +08:00
|
|
|
if (show_types || (curr_depth == 0 && !flat_output))
|
2010-12-14 10:59:59 +08:00
|
|
|
s.Printf("(%s) ", valobj->GetTypeName().AsCString("<invalid type>"));
|
2010-10-05 08:00:42 +08:00
|
|
|
|
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
if (flat_output)
|
|
|
|
{
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
// If we are showing types, also qualify the C++ base classes
|
|
|
|
const bool qualify_cxx_base_classes = show_types;
|
|
|
|
valobj->GetExpressionPath(s, qualify_cxx_base_classes);
|
2010-10-15 06:52:14 +08:00
|
|
|
s.PutCString(" =");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString("");
|
|
|
|
s.Printf ("%s =", name_cstr);
|
|
|
|
}
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
if (!scope_already_checked && !valobj->IsInScope())
|
2010-10-15 06:52:14 +08:00
|
|
|
{
|
|
|
|
err_cstr = "error: out of scope";
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
const char *val_cstr = NULL;
|
|
|
|
|
|
|
|
if (err_cstr == NULL)
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
val_cstr = valobj->GetValueAsCString();
|
2010-10-15 06:52:14 +08:00
|
|
|
err_cstr = valobj->GetError().AsCString();
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
|
|
|
|
if (err_cstr)
|
|
|
|
{
|
2010-11-02 09:50:16 +08:00
|
|
|
s.Printf (" error: %s\n", err_cstr);
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-10-27 11:32:59 +08:00
|
|
|
const bool is_ref = type_flags.Test (ClangASTContext::eTypeIsReference);
|
2010-10-15 06:52:14 +08:00
|
|
|
if (print_valobj)
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
const char *sum_cstr = valobj->GetSummaryAsCString();
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
if (val_cstr)
|
|
|
|
s.Printf(" %s", val_cstr);
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-10-15 06:52:14 +08:00
|
|
|
if (sum_cstr)
|
|
|
|
s.Printf(" %s", sum_cstr);
|
|
|
|
|
|
|
|
if (use_objc)
|
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
const char *object_desc = valobj->GetObjectDescription();
|
2010-10-15 06:52:14 +08:00
|
|
|
if (object_desc)
|
|
|
|
s.Printf(" %s\n", object_desc);
|
|
|
|
else
|
2010-10-23 08:18:49 +08:00
|
|
|
s.Printf (" [no Objective-C description available]\n");
|
2010-10-15 06:52:14 +08:00
|
|
|
return;
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (curr_depth < max_depth)
|
|
|
|
{
|
2010-10-27 11:32:59 +08:00
|
|
|
// We will show children for all concrete types. We won't show
|
|
|
|
// pointer contents unless a pointer depth has been specified.
|
|
|
|
// We won't reference contents unless the reference is the
|
|
|
|
// root object (depth of zero).
|
|
|
|
bool print_children = true;
|
|
|
|
|
|
|
|
// Use a new temporary pointer depth in case we override the
|
|
|
|
// current pointer depth below...
|
|
|
|
uint32_t curr_ptr_depth = ptr_depth;
|
|
|
|
|
|
|
|
const bool is_ptr = type_flags.Test (ClangASTContext::eTypeIsPointer);
|
|
|
|
if (is_ptr || is_ref)
|
|
|
|
{
|
|
|
|
// We have a pointer or reference whose value is an address.
|
|
|
|
// Make sure that address is not NULL
|
2011-03-25 05:19:54 +08:00
|
|
|
AddressType ptr_address_type;
|
2010-10-27 11:32:59 +08:00
|
|
|
if (valobj->GetPointerValue (ptr_address_type, true) == 0)
|
|
|
|
print_children = false;
|
|
|
|
|
|
|
|
else if (is_ref && curr_depth == 0)
|
|
|
|
{
|
|
|
|
// If this is the root object (depth is zero) that we are showing
|
|
|
|
// and it is a reference, and no pointer depth has been supplied
|
|
|
|
// print out what it references. Don't do this at deeper depths
|
|
|
|
// otherwise we can end up with infinite recursion...
|
|
|
|
curr_ptr_depth = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curr_ptr_depth == 0)
|
|
|
|
print_children = false;
|
|
|
|
}
|
2010-10-05 08:00:42 +08:00
|
|
|
|
2010-10-27 11:32:59 +08:00
|
|
|
if (print_children)
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
const uint32_t num_children = valobj->GetNumChildren();
|
|
|
|
if (num_children)
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
if (flat_output)
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
if (print_valobj)
|
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (print_valobj)
|
2010-10-29 12:59:35 +08:00
|
|
|
s.PutCString(is_ref ? ": {\n" : " {\n");
|
2010-10-15 06:52:14 +08:00
|
|
|
s.IndentMore();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t idx=0; idx<num_children; ++idx)
|
|
|
|
{
|
|
|
|
ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true));
|
|
|
|
if (child_sp.get())
|
|
|
|
{
|
|
|
|
DumpValueObject (s,
|
|
|
|
child_sp.get(),
|
|
|
|
NULL,
|
2010-10-27 11:32:59 +08:00
|
|
|
(is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth,
|
2010-10-15 06:52:14 +08:00
|
|
|
curr_depth + 1,
|
|
|
|
max_depth,
|
|
|
|
show_types,
|
|
|
|
show_location,
|
|
|
|
false,
|
|
|
|
true,
|
|
|
|
flat_output);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!flat_output)
|
|
|
|
{
|
|
|
|
s.IndentLess();
|
|
|
|
s.Indent("}\n");
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
|
|
|
}
|
2010-10-15 06:52:14 +08:00
|
|
|
else if (has_children)
|
|
|
|
{
|
|
|
|
// Aggregate, no children...
|
|
|
|
if (print_valobj)
|
2010-10-27 11:32:59 +08:00
|
|
|
s.PutCString(" {}\n");
|
2010-10-15 06:52:14 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (print_valobj)
|
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
2010-10-15 06:52:14 +08:00
|
|
|
else
|
|
|
|
{
|
2010-10-05 08:00:42 +08:00
|
|
|
s.EOL();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
if (has_children && print_valobj)
|
2010-10-05 08:00:42 +08:00
|
|
|
{
|
2010-10-15 06:52:14 +08:00
|
|
|
s.PutCString("{...}\n");
|
2010-10-05 08:00:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-14 10:59:59 +08:00
|
|
|
|
|
|
|
ValueObjectSP
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::CreateConstantValue (const ConstString &name)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
|
|
|
ValueObjectSP valobj_sp;
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
if (UpdateValueIfNeeded() && m_error.Success())
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
ExecutionContextScope *exe_scope = GetExecutionContextScope();
|
|
|
|
if (exe_scope)
|
|
|
|
{
|
|
|
|
ExecutionContext exe_ctx;
|
|
|
|
exe_scope->CalculateExecutionContext(exe_ctx);
|
2010-12-14 10:59:59 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
clang::ASTContext *ast = GetClangAST ();
|
2010-12-14 10:59:59 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
DataExtractor data;
|
|
|
|
data.SetByteOrder (m_data.GetByteOrder());
|
|
|
|
data.SetAddressByteSize(m_data.GetAddressByteSize());
|
2010-12-14 10:59:59 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
m_error = m_value.GetValueAsData (&exe_ctx, ast, data, 0);
|
2010-12-14 10:59:59 +08:00
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
valobj_sp.reset (new ValueObjectConstResult (exe_scope,
|
|
|
|
ast,
|
|
|
|
GetClangType(),
|
|
|
|
name,
|
|
|
|
data));
|
|
|
|
}
|
2010-12-14 10:59:59 +08:00
|
|
|
}
|
2011-03-31 08:19:25 +08:00
|
|
|
|
|
|
|
if (!valobj_sp)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
2011-03-31 08:19:25 +08:00
|
|
|
valobj_sp.reset (new ValueObjectConstResult (NULL, m_error));
|
2010-12-14 10:59:59 +08:00
|
|
|
}
|
|
|
|
return valobj_sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
lldb::ValueObjectSP
|
2010-12-21 04:49:23 +08:00
|
|
|
ValueObject::Dereference (Error &error)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
|
|
|
lldb::ValueObjectSP valobj_sp;
|
2010-12-15 13:08:08 +08:00
|
|
|
const bool is_pointer_type = IsPointerType();
|
|
|
|
if (is_pointer_type)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
bool child_is_base_class = false;
|
2011-01-21 09:59:00 +08:00
|
|
|
bool child_is_deref_of_parent = false;
|
2010-12-14 10:59:59 +08:00
|
|
|
const bool transparent_pointers = false;
|
|
|
|
clang::ASTContext *clang_ast = GetClangAST();
|
|
|
|
clang_type_t clang_type = GetClangType();
|
|
|
|
clang_type_t child_clang_type;
|
|
|
|
child_clang_type = ClangASTContext::GetChildClangTypeAtIndex (clang_ast,
|
|
|
|
GetName().GetCString(),
|
|
|
|
clang_type,
|
|
|
|
0,
|
|
|
|
transparent_pointers,
|
|
|
|
omit_empty_base_classes,
|
|
|
|
child_name_str,
|
|
|
|
child_byte_size,
|
|
|
|
child_byte_offset,
|
|
|
|
child_bitfield_bit_size,
|
|
|
|
child_bitfield_bit_offset,
|
2011-01-21 09:59:00 +08:00
|
|
|
child_is_base_class,
|
|
|
|
child_is_deref_of_parent);
|
2011-01-10 05:07:35 +08:00
|
|
|
if (child_clang_type && child_byte_size)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
|
|
|
ConstString child_name;
|
|
|
|
if (!child_name_str.empty())
|
|
|
|
child_name.SetCString (child_name_str.c_str());
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
valobj_sp.reset (new ValueObjectChild (*this,
|
2010-12-14 10:59:59 +08:00
|
|
|
clang_ast,
|
|
|
|
child_clang_type,
|
|
|
|
child_name,
|
|
|
|
child_byte_size,
|
|
|
|
child_byte_offset,
|
|
|
|
child_bitfield_bit_size,
|
|
|
|
child_bitfield_bit_offset,
|
2011-01-21 09:59:00 +08:00
|
|
|
child_is_base_class,
|
|
|
|
child_is_deref_of_parent));
|
2010-12-14 10:59:59 +08:00
|
|
|
}
|
|
|
|
}
|
2010-12-15 13:08:08 +08:00
|
|
|
|
|
|
|
if (valobj_sp)
|
|
|
|
{
|
|
|
|
error.Clear();
|
|
|
|
}
|
2010-12-14 10:59:59 +08:00
|
|
|
else
|
|
|
|
{
|
2010-12-15 13:08:08 +08:00
|
|
|
StreamString strm;
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
GetExpressionPath(strm, true);
|
2010-12-15 13:08:08 +08:00
|
|
|
|
|
|
|
if (is_pointer_type)
|
|
|
|
error.SetErrorStringWithFormat("dereference failed: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str());
|
|
|
|
else
|
|
|
|
error.SetErrorStringWithFormat("not a pointer type: (%s) %s", GetTypeName().AsCString("<invalid type>"), strm.GetString().c_str());
|
2010-12-14 10:59:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return valobj_sp;
|
|
|
|
}
|
|
|
|
|
2010-12-15 13:08:08 +08:00
|
|
|
lldb::ValueObjectSP
|
|
|
|
ValueObject::AddressOf (Error &error)
|
2010-12-14 10:59:59 +08:00
|
|
|
{
|
|
|
|
lldb::ValueObjectSP valobj_sp;
|
2011-03-25 05:19:54 +08:00
|
|
|
AddressType address_type = eAddressTypeInvalid;
|
2010-12-14 10:59:59 +08:00
|
|
|
const bool scalar_is_load_address = false;
|
|
|
|
lldb::addr_t addr = GetAddressOf (address_type, scalar_is_load_address);
|
2010-12-15 13:08:08 +08:00
|
|
|
error.Clear();
|
2010-12-14 10:59:59 +08:00
|
|
|
if (addr != LLDB_INVALID_ADDRESS)
|
|
|
|
{
|
|
|
|
switch (address_type)
|
|
|
|
{
|
2010-12-15 13:08:08 +08:00
|
|
|
default:
|
2010-12-14 10:59:59 +08:00
|
|
|
case eAddressTypeInvalid:
|
2010-12-15 13:08:08 +08:00
|
|
|
{
|
|
|
|
StreamString expr_path_strm;
|
A few of the issue I have been trying to track down and fix have been due to
the way LLDB lazily gets complete definitions for types within the debug info.
When we run across a class/struct/union definition in the DWARF, we will only
parse the full definition if we need to. This works fine for top level types
that are assigned directly to variables and arguments, but when we have a
variable with a class, lets say "A" for this example, that has a member:
"B *m_b". Initially we don't need to hunt down a definition for this class
unless we are ever asked to do something with it ("expr m_b->getDecl()" for
example). With my previous approach to lazy type completion, we would be able
to take a "A *a" and get a complete type for it, but we wouldn't be able to
then do an "a->m_b->getDecl()" unless we always expanded all types within a
class prior to handing out the type. Expanding everything is very costly and
it would be great if there were a better way.
A few months ago I worked with the llvm/clang folks to have the
ExternalASTSource class be able to complete classes if there weren't completed
yet:
class ExternalASTSource {
....
virtual void
CompleteType (clang::TagDecl *Tag);
virtual void
CompleteType (clang::ObjCInterfaceDecl *Class);
};
This was great, because we can now have the class that is producing the AST
(SymbolFileDWARF and SymbolFileDWARFDebugMap) sign up as external AST sources
and the object that creates the forward declaration types can now also
complete them anywhere within the clang type system.
This patch makes a few major changes:
- lldb_private::Module classes now own the AST context. Previously the TypeList
objects did.
- The DWARF parsers now sign up as an external AST sources so they can complete
types.
- All of the pure clang type system wrapper code we have in LLDB (ClangASTContext,
ClangASTType, and more) can now be iterating through children of any type,
and if a class/union/struct type (clang::RecordType or ObjC interface)
is found that is incomplete, we can ask the AST to get the definition.
- The SymbolFileDWARFDebugMap class now will create and use a single AST that
all child SymbolFileDWARF classes will share (much like what happens when
we have a complete linked DWARF for an executable).
We will need to modify some of the ClangUserExpression code to take more
advantage of this completion ability in the near future. Meanwhile we should
be better off now that we can be accessing any children of variables through
pointers and always be able to resolve the clang type if needed.
llvm-svn: 123613
2011-01-17 11:46:26 +08:00
|
|
|
GetExpressionPath(expr_path_strm, true);
|
2010-12-15 13:08:08 +08:00
|
|
|
error.SetErrorStringWithFormat("'%s' is not in memory", expr_path_strm.GetString().c_str());
|
|
|
|
}
|
2010-12-14 10:59:59 +08:00
|
|
|
break;
|
2010-12-15 13:08:08 +08:00
|
|
|
|
2010-12-14 10:59:59 +08:00
|
|
|
case eAddressTypeFile:
|
|
|
|
case eAddressTypeLoad:
|
|
|
|
case eAddressTypeHost:
|
|
|
|
{
|
|
|
|
clang::ASTContext *ast = GetClangAST();
|
|
|
|
clang_type_t clang_type = GetClangType();
|
|
|
|
if (ast && clang_type)
|
|
|
|
{
|
|
|
|
std::string name (1, '&');
|
|
|
|
name.append (m_name.AsCString(""));
|
2011-03-31 08:19:25 +08:00
|
|
|
valobj_sp.reset (new ValueObjectConstResult (GetExecutionContextScope(),
|
|
|
|
ast,
|
2010-12-14 10:59:59 +08:00
|
|
|
ClangASTContext::CreatePointerType (ast, clang_type),
|
|
|
|
ConstString (name.c_str()),
|
|
|
|
addr,
|
2011-01-13 16:53:35 +08:00
|
|
|
eAddressTypeInvalid,
|
2010-12-14 10:59:59 +08:00
|
|
|
m_data.GetAddressByteSize()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return valobj_sp;
|
|
|
|
}
|
|
|
|
|
2011-03-31 08:19:25 +08:00
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint () :
|
|
|
|
m_stop_id (0),
|
|
|
|
m_thread_id (LLDB_INVALID_UID)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint (ExecutionContextScope *exe_scope, bool use_selected):
|
|
|
|
m_stop_id (0),
|
|
|
|
m_thread_id (LLDB_INVALID_UID),
|
|
|
|
m_needs_update (true),
|
|
|
|
m_first_update (true)
|
|
|
|
{
|
|
|
|
ExecutionContext exe_ctx;
|
|
|
|
ExecutionContextScope *computed_exe_scope = exe_scope; // If use_selected is true, we may find a better scope,
|
|
|
|
// and if so we want to cache that not the original.
|
|
|
|
if (exe_scope)
|
|
|
|
exe_scope->CalculateExecutionContext(exe_ctx);
|
|
|
|
if (exe_ctx.target != NULL)
|
|
|
|
{
|
|
|
|
m_target_sp = exe_ctx.target->GetSP();
|
|
|
|
|
|
|
|
if (exe_ctx.process == NULL)
|
|
|
|
m_process_sp = exe_ctx.target->GetProcessSP();
|
|
|
|
else
|
|
|
|
m_process_sp = exe_ctx.process->GetSP();
|
|
|
|
|
|
|
|
if (m_process_sp != NULL)
|
|
|
|
{
|
|
|
|
m_stop_id = m_process_sp->GetStopID();
|
|
|
|
Thread *thread = NULL;
|
|
|
|
|
|
|
|
if (exe_ctx.thread == NULL)
|
|
|
|
{
|
|
|
|
if (use_selected)
|
|
|
|
{
|
|
|
|
thread = m_process_sp->GetThreadList().GetSelectedThread().get();
|
|
|
|
if (thread)
|
|
|
|
computed_exe_scope = thread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
thread = exe_ctx.thread;
|
|
|
|
|
|
|
|
if (thread != NULL)
|
|
|
|
{
|
|
|
|
m_thread_id = thread->GetIndexID();
|
|
|
|
if (exe_ctx.frame == NULL)
|
|
|
|
{
|
|
|
|
if (use_selected)
|
|
|
|
{
|
|
|
|
StackFrame *frame = exe_ctx.thread->GetSelectedFrame().get();
|
|
|
|
if (frame)
|
|
|
|
{
|
|
|
|
m_stack_id = frame->GetStackID();
|
|
|
|
computed_exe_scope = frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_stack_id = exe_ctx.frame->GetStackID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_exe_scope = computed_exe_scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueObject::EvaluationPoint::EvaluationPoint (const ValueObject::EvaluationPoint &rhs) :
|
|
|
|
m_exe_scope (rhs.m_exe_scope),
|
|
|
|
m_target_sp (rhs.m_target_sp),
|
|
|
|
m_process_sp (rhs.m_process_sp),
|
|
|
|
m_thread_id (rhs.m_thread_id),
|
|
|
|
m_stack_id (rhs.m_stack_id),
|
|
|
|
m_needs_update(true),
|
|
|
|
m_first_update(true),
|
|
|
|
m_stop_id (0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ValueObject::EvaluationPoint::~EvaluationPoint ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ExecutionContextScope *
|
|
|
|
ValueObject::EvaluationPoint::GetExecutionContextScope ()
|
|
|
|
{
|
|
|
|
// We have to update before giving out the scope, or we could be handing out stale pointers.
|
|
|
|
SyncWithProcessState();
|
|
|
|
|
|
|
|
return m_exe_scope;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function checks the EvaluationPoint against the current process state. If the current
|
|
|
|
// state matches the evaluation point, or the evaluation point is already invalid, then we return
|
|
|
|
// false, meaning "no change". If the current state is different, we update our state, and return
|
|
|
|
// true meaning "yes, change". If we did see a change, we also set m_needs_update to true, so
|
|
|
|
// future calls to NeedsUpdate will return true.
|
|
|
|
|
|
|
|
bool
|
|
|
|
ValueObject::EvaluationPoint::SyncWithProcessState()
|
|
|
|
{
|
|
|
|
// If we're already invalid, we don't need to do anything, and nothing has changed:
|
|
|
|
if (m_stop_id == LLDB_INVALID_UID)
|
|
|
|
{
|
|
|
|
// Can't update with an invalid state.
|
|
|
|
m_needs_update = false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have a process nothing can change.
|
|
|
|
if (!m_process_sp)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// If our stop id is the current stop ID, nothing has changed:
|
|
|
|
if (m_stop_id == m_process_sp->GetStopID())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
m_stop_id = m_process_sp->GetStopID();
|
|
|
|
m_needs_update = true;
|
|
|
|
m_exe_scope = m_process_sp.get();
|
|
|
|
|
|
|
|
// Something has changed, so we will return true. Now make sure the thread & frame still exist, and if either
|
|
|
|
// doesn't, mark ourselves as invalid.
|
|
|
|
|
|
|
|
if (m_thread_id != LLDB_INVALID_THREAD_ID)
|
|
|
|
{
|
|
|
|
Thread *our_thread = m_process_sp->GetThreadList().FindThreadByIndexID (m_thread_id).get();
|
|
|
|
if (our_thread == NULL)
|
|
|
|
SetInvalid();
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_exe_scope = our_thread;
|
|
|
|
|
|
|
|
if (m_stack_id.IsValid())
|
|
|
|
{
|
|
|
|
StackFrame *our_frame = our_thread->GetFrameWithStackID (m_stack_id).get();
|
|
|
|
if (our_frame == NULL)
|
|
|
|
SetInvalid();
|
|
|
|
else
|
|
|
|
m_exe_scope = our_frame;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
ValueObject::EvaluationPoint::SetContext (ExecutionContextScope *exe_scope)
|
|
|
|
{
|
|
|
|
if (!IsValid())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool needs_update = false;
|
|
|
|
m_exe_scope = NULL;
|
|
|
|
|
|
|
|
// The target has to be non-null, and the
|
|
|
|
Target *target = exe_scope->CalculateTarget();
|
|
|
|
if (target != NULL)
|
|
|
|
{
|
|
|
|
Target *old_target = m_target_sp.get();
|
|
|
|
assert (target == old_target);
|
|
|
|
Process *process = exe_scope->CalculateProcess();
|
|
|
|
if (process != NULL)
|
|
|
|
{
|
|
|
|
// FOR NOW - assume you can't update variable objects across process boundaries.
|
|
|
|
Process *old_process = m_process_sp.get();
|
|
|
|
assert (process == old_process);
|
|
|
|
|
|
|
|
lldb::user_id_t stop_id = process->GetStopID();
|
|
|
|
if (stop_id != m_stop_id)
|
|
|
|
{
|
|
|
|
needs_update = true;
|
|
|
|
m_stop_id = stop_id;
|
|
|
|
}
|
|
|
|
// See if we're switching the thread or stack context. If no thread is given, this is
|
|
|
|
// being evaluated in a global context.
|
|
|
|
Thread *thread = exe_scope->CalculateThread();
|
|
|
|
if (thread != NULL)
|
|
|
|
{
|
|
|
|
lldb::user_id_t new_thread_index = thread->GetIndexID();
|
|
|
|
if (new_thread_index != m_thread_id)
|
|
|
|
{
|
|
|
|
needs_update = true;
|
|
|
|
m_thread_id = new_thread_index;
|
|
|
|
m_stack_id.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
StackFrame *new_frame = exe_scope->CalculateStackFrame();
|
|
|
|
if (new_frame != NULL)
|
|
|
|
{
|
|
|
|
if (new_frame->GetStackID() != m_stack_id)
|
|
|
|
{
|
|
|
|
needs_update = true;
|
|
|
|
m_stack_id = new_frame->GetStackID();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_stack_id.Clear();
|
|
|
|
needs_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If this had been given a thread, and now there is none, we should update.
|
|
|
|
// Otherwise we don't have to do anything.
|
|
|
|
if (m_thread_id != LLDB_INVALID_UID)
|
|
|
|
{
|
|
|
|
m_thread_id = LLDB_INVALID_UID;
|
|
|
|
m_stack_id.Clear();
|
|
|
|
needs_update = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If there is no process, then we don't need to update anything.
|
|
|
|
// But if we're switching from having a process to not, we should try to update.
|
|
|
|
if (m_process_sp.get() != NULL)
|
|
|
|
{
|
|
|
|
needs_update = true;
|
|
|
|
m_process_sp.reset();
|
|
|
|
m_thread_id = LLDB_INVALID_UID;
|
|
|
|
m_stack_id.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// If there's no target, nothing can change so we don't need to update anything.
|
|
|
|
// But if we're switching from having a target to not, we should try to update.
|
|
|
|
if (m_target_sp.get() != NULL)
|
|
|
|
{
|
|
|
|
needs_update = true;
|
|
|
|
m_target_sp.reset();
|
|
|
|
m_process_sp.reset();
|
|
|
|
m_thread_id = LLDB_INVALID_UID;
|
|
|
|
m_stack_id.Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!m_needs_update)
|
|
|
|
m_needs_update = needs_update;
|
|
|
|
|
|
|
|
return needs_update;
|
|
|
|
}
|