forked from OSchip/llvm-project
Improve the x86_64 return value decoder to handle most structure returns.
Switch from GetReturnValue, which was hardly ever used, to GetReturnValueObject which is much more convenient. Return the "return value object" as a persistent variable if requested. llvm-svn: 147157
This commit is contained in:
parent
6901c0de67
commit
ef65160016
|
@ -45,14 +45,19 @@ public:
|
|||
GetArgumentValues (Thread &thread,
|
||||
ValueList &values) const = 0;
|
||||
|
||||
virtual bool
|
||||
GetReturnValue (Thread &thread,
|
||||
Value &value) const = 0;
|
||||
|
||||
virtual lldb::ValueObjectSP
|
||||
public:
|
||||
lldb::ValueObjectSP
|
||||
GetReturnValueObject (Thread &thread,
|
||||
ClangASTType &type) const;
|
||||
ClangASTType &type,
|
||||
bool persistent = true) const;
|
||||
|
||||
protected:
|
||||
// This is the method the ABI will call to actually calculate the return value.
|
||||
// Don't put it in a persistant value object, that will be done by the ABI::GetReturnValueObject.
|
||||
virtual lldb::ValueObjectSP
|
||||
GetReturnValueObjectImpl (Thread &thread,
|
||||
ClangASTType &type) const = 0;
|
||||
public:
|
||||
virtual bool
|
||||
CreateFunctionEntryUnwindPlan (UnwindPlan &unwind_plan) = 0;
|
||||
|
||||
|
|
|
@ -22,9 +22,13 @@ namespace lldb_private {
|
|||
|
||||
class ThreadPlanCallFunction : public ThreadPlan
|
||||
{
|
||||
// Create a thread plan to call a function at the address passed in the "function"
|
||||
// argument. If you plan to call GetReturnValueObject, then pass in the
|
||||
// return type, otherwise just pass in an invalid ClangASTType.
|
||||
public:
|
||||
ThreadPlanCallFunction (Thread &thread,
|
||||
Address &function,
|
||||
const ClangASTType &return_type,
|
||||
lldb::addr_t arg,
|
||||
bool stop_other_threads,
|
||||
bool discard_on_error = true,
|
||||
|
@ -33,6 +37,7 @@ public:
|
|||
|
||||
ThreadPlanCallFunction (Thread &thread,
|
||||
Address &function,
|
||||
const ClangASTType &return_type,
|
||||
bool stop_other_threads,
|
||||
bool discard_on_error,
|
||||
lldb::addr_t *arg1_ptr = NULL,
|
||||
|
@ -90,16 +95,10 @@ public:
|
|||
// plan is complete, you can call "GetReturnValue()" to retrieve the value
|
||||
// that was extracted.
|
||||
|
||||
const lldb::ValueSP &
|
||||
GetReturnValue ()
|
||||
virtual lldb::ValueObjectSP
|
||||
GetReturnValueObject ()
|
||||
{
|
||||
return m_return_value_sp;
|
||||
}
|
||||
|
||||
void
|
||||
RequestReturnValue (lldb::ValueSP &return_value_sp)
|
||||
{
|
||||
m_return_value_sp = return_value_sp;
|
||||
return m_return_valobj_sp;
|
||||
}
|
||||
|
||||
// Return the stack pointer that the function received
|
||||
|
@ -165,7 +164,8 @@ private:
|
|||
// thread plans, but for reporting purposes,
|
||||
// it's nice to know the real stop reason.
|
||||
// This gets set in DoTakedown.
|
||||
lldb::ValueSP m_return_value_sp; // If this contains a valid pointer, use the ABI to extract values when complete
|
||||
ClangASTType m_return_type;
|
||||
lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete
|
||||
bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that.
|
||||
lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown;
|
||||
|
||||
|
|
|
@ -1709,10 +1709,8 @@ Debugger::FormatPrompt
|
|||
ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject (stop_info_sp);
|
||||
if (return_valobj_sp)
|
||||
{
|
||||
cstr = return_valobj_sp->GetValueAsCString ();
|
||||
if (cstr && cstr[0])
|
||||
{
|
||||
s.PutCString(cstr);
|
||||
ValueObject::DumpValueObjectOptions dump_options;
|
||||
ValueObject::DumpValueObject (s, return_valobj_sp.get(), dump_options);
|
||||
var_success = true;
|
||||
}
|
||||
}
|
||||
|
@ -1720,7 +1718,6 @@ Debugger::FormatPrompt
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (::strncmp (var_name_begin, "target.", strlen("target.")) == 0)
|
||||
{
|
||||
Target *target = Target::GetTargetFromContexts (exe_ctx, sc);
|
||||
|
@ -2579,7 +2576,7 @@ Debugger::SettingsController::global_settings_table[] =
|
|||
MODULE_WITH_FUNC\
|
||||
FILE_AND_LINE\
|
||||
"{, stop reason = ${thread.stop-reason}}"\
|
||||
"{, return value = ${thread.return-value}}"\
|
||||
"{\\nReturn value: ${thread.return-value}}"\
|
||||
"\\n"
|
||||
|
||||
//#define DEFAULT_THREAD_FORMAT "thread #${thread.index}: tid = ${thread.id}"\
|
||||
|
|
|
@ -405,6 +405,7 @@ ClangFunction::GetThreadPlanToCallFunction (ExecutionContext &exe_ctx,
|
|||
Address wrapper_address (NULL, func_addr);
|
||||
ThreadPlan *new_plan = new ThreadPlanCallFunction (*thread,
|
||||
wrapper_address,
|
||||
ClangASTType(),
|
||||
args_addr,
|
||||
stop_others,
|
||||
discard_on_error,
|
||||
|
@ -418,7 +419,8 @@ ClangFunction::FetchFunctionResults (ExecutionContext &exe_ctx, lldb::addr_t arg
|
|||
{
|
||||
// Read the return value - it is the last field in the struct:
|
||||
// FIXME: How does clang tell us there's no return value? We need to handle that case.
|
||||
|
||||
// FIXME: Create our ThreadPlanCallFunction with the return ClangASTType, and then use GetReturnValueObject
|
||||
// to fetch the value. That way we can fetch any values we need.
|
||||
Process *process = exe_ctx.GetProcessPtr();
|
||||
|
||||
if (process == NULL)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "lldb/Core/RegisterValue.h"
|
||||
#include "lldb/Core/Scalar.h"
|
||||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Core/ValueObjectConstResult.h"
|
||||
#include "lldb/Symbol/ClangASTContext.h"
|
||||
#include "lldb/Symbol/UnwindPlan.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
@ -417,29 +418,32 @@ ABIMacOSX_arm::GetArgumentValues (Thread &thread,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ABIMacOSX_arm::GetReturnValue (Thread &thread,
|
||||
Value &value) const
|
||||
ValueObjectSP
|
||||
ABIMacOSX_arm::GetReturnValueObjectImpl (Thread &thread,
|
||||
lldb_private::ClangASTType &ast_type) const
|
||||
{
|
||||
switch (value.GetContextType())
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case Value::eContextTypeClangType:
|
||||
{
|
||||
// Extract the Clang AST context from the PC so that we can figure out type
|
||||
// sizes
|
||||
Value value;
|
||||
ValueObjectSP return_valobj_sp;
|
||||
|
||||
clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext();
|
||||
void *value_type = ast_type.GetOpaqueQualType();
|
||||
if (!value_type)
|
||||
return return_valobj_sp;
|
||||
|
||||
clang::ASTContext *ast_context = ast_type.GetASTContext();
|
||||
if (!ast_context)
|
||||
return return_valobj_sp;
|
||||
|
||||
value.SetContext (Value::eContextTypeClangType, value_type);
|
||||
|
||||
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
||||
if (!reg_ctx)
|
||||
return return_valobj_sp;
|
||||
|
||||
bool is_signed;
|
||||
|
||||
// Get the pointer to the first stack argument so we have a place to start
|
||||
// when reading data
|
||||
|
||||
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
||||
|
||||
void *value_type = value.GetClangType();
|
||||
bool is_signed;
|
||||
|
||||
const RegisterInfo *r0_reg_info = reg_ctx->GetRegisterInfoByName("r0", 0);
|
||||
if (ClangASTContext::IsIntegerType (value_type, is_signed))
|
||||
{
|
||||
|
@ -448,7 +452,7 @@ ABIMacOSX_arm::GetReturnValue (Thread &thread,
|
|||
switch (bit_width)
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
return return_valobj_sp;
|
||||
case 64:
|
||||
{
|
||||
const RegisterInfo *r1_reg_info = reg_ctx->GetRegisterInfoByName("r1", 0);
|
||||
|
@ -489,13 +493,17 @@ ABIMacOSX_arm::GetReturnValue (Thread &thread,
|
|||
else
|
||||
{
|
||||
// not handled yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
return true;
|
||||
// If we get here, we have a valid Value, so make our ValueObject out of it:
|
||||
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
thread.GetStackFrameAtIndex(0).get(),
|
||||
ast_type.GetASTContext(),
|
||||
value,
|
||||
ConstString(""));
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -41,10 +41,12 @@ public:
|
|||
GetArgumentValues (lldb_private::Thread &thread,
|
||||
lldb_private::ValueList &values) const;
|
||||
|
||||
virtual bool
|
||||
GetReturnValue (lldb_private::Thread &thread,
|
||||
lldb_private::Value &value) const;
|
||||
protected:
|
||||
virtual lldb::ValueObjectSP
|
||||
GetReturnValueObjectImpl (lldb_private::Thread &thread,
|
||||
lldb_private::ClangASTType &ast_type) const;
|
||||
|
||||
public:
|
||||
virtual bool
|
||||
CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan);
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Core/RegisterValue.h"
|
||||
#include "lldb/Core/Scalar.h"
|
||||
#include "lldb/Core/ValueObjectConstResult.h"
|
||||
#include "lldb/Symbol/ClangASTContext.h"
|
||||
#include "lldb/Symbol/UnwindPlan.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
@ -681,27 +682,27 @@ ABIMacOSX_i386::GetArgumentValues (Thread &thread,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ABIMacOSX_i386::GetReturnValue (Thread &thread,
|
||||
Value &value) const
|
||||
ValueObjectSP
|
||||
ABIMacOSX_i386::GetReturnValueObjectImpl (Thread &thread,
|
||||
ClangASTType &ast_type) const
|
||||
{
|
||||
switch (value.GetContextType())
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case Value::eContextTypeClangType:
|
||||
{
|
||||
// Extract the Clang AST context from the PC so that we can figure out type
|
||||
// sizes
|
||||
Value value;
|
||||
ValueObjectSP return_valobj_sp;
|
||||
|
||||
clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext();
|
||||
void *value_type = ast_type.GetOpaqueQualType();
|
||||
if (!value_type)
|
||||
return return_valobj_sp;
|
||||
|
||||
// Get the pointer to the first stack argument so we have a place to start
|
||||
// when reading data
|
||||
clang::ASTContext *ast_context = ast_type.GetASTContext();
|
||||
if (!ast_context)
|
||||
return return_valobj_sp;
|
||||
|
||||
value.SetContext (Value::eContextTypeClangType, value_type);
|
||||
|
||||
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
||||
if (!reg_ctx)
|
||||
return return_valobj_sp;
|
||||
|
||||
void *value_type = value.GetClangType();
|
||||
bool is_signed;
|
||||
|
||||
if (ClangASTContext::IsIntegerType (value_type, is_signed))
|
||||
|
@ -716,7 +717,7 @@ ABIMacOSX_i386::GetReturnValue (Thread &thread,
|
|||
default:
|
||||
case 128:
|
||||
// Scalar can't hold 128-bit literals, so we don't handle this
|
||||
return false;
|
||||
return return_valobj_sp;
|
||||
case 64:
|
||||
uint64_t raw_value;
|
||||
raw_value = thread.GetRegisterContext()->ReadRegisterAsUnsigned(eax_id, 0) & 0xffffffff;
|
||||
|
@ -755,13 +756,17 @@ ABIMacOSX_i386::GetReturnValue (Thread &thread,
|
|||
else
|
||||
{
|
||||
// not handled yet
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
return true;
|
||||
// If we get here, we have a valid Value, so make our ValueObject out of it:
|
||||
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
thread.GetStackFrameAtIndex(0).get(),
|
||||
ast_type.GetASTContext(),
|
||||
value,
|
||||
ConstString(""));
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -51,9 +51,12 @@ public:
|
|||
GetArgumentValues (lldb_private::Thread &thread,
|
||||
lldb_private::ValueList &values) const;
|
||||
|
||||
virtual bool
|
||||
GetReturnValue (lldb_private::Thread &thread,
|
||||
lldb_private::Value &value) const;
|
||||
protected:
|
||||
virtual lldb::ValueObjectSP
|
||||
GetReturnValueObjectImpl (lldb_private::Thread &thread,
|
||||
lldb_private::ClangASTType &ast_type) const;
|
||||
|
||||
public:
|
||||
|
||||
virtual bool
|
||||
CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan);
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#include "lldb/Core/PluginManager.h"
|
||||
#include "lldb/Core/RegisterValue.h"
|
||||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Core/ValueObjectConstResult.h"
|
||||
#include "lldb/Core/ValueObjectRegister.h"
|
||||
#include "lldb/Core/ValueObjectMemory.h"
|
||||
#include "lldb/Symbol/ClangASTContext.h"
|
||||
#include "lldb/Symbol/UnwindPlan.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
|
@ -547,30 +550,36 @@ ABISysV_x86_64::GetArgumentValues (Thread &thread,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ABISysV_x86_64::GetReturnValue (Thread &thread,
|
||||
Value &value) const
|
||||
ValueObjectSP
|
||||
ABISysV_x86_64::GetReturnValueObjectSimple (Thread &thread,
|
||||
ClangASTType &ast_type) const
|
||||
{
|
||||
switch (value.GetContextType())
|
||||
{
|
||||
default:
|
||||
return false;
|
||||
case Value::eContextTypeClangType:
|
||||
{
|
||||
void *value_type = value.GetClangType();
|
||||
bool is_signed;
|
||||
ValueObjectSP return_valobj_sp;
|
||||
Value value;
|
||||
|
||||
clang_type_t value_type = ast_type.GetOpaqueQualType();
|
||||
if (!value_type)
|
||||
return return_valobj_sp;
|
||||
|
||||
clang::ASTContext *ast_context = ast_type.GetASTContext();
|
||||
if (!ast_context)
|
||||
return return_valobj_sp;
|
||||
|
||||
value.SetContext (Value::eContextTypeClangType, value_type);
|
||||
|
||||
RegisterContext *reg_ctx = thread.GetRegisterContext().get();
|
||||
|
||||
if (!reg_ctx)
|
||||
return false;
|
||||
return return_valobj_sp;
|
||||
|
||||
bool is_signed;
|
||||
bool is_complex;
|
||||
uint32_t count;
|
||||
|
||||
if (ClangASTContext::IsIntegerType (value_type, is_signed))
|
||||
{
|
||||
// For now, assume that the types in the AST values come from the Target's
|
||||
// scratch AST.
|
||||
|
||||
clang::ASTContext *ast_context = thread.CalculateTarget()->GetScratchClangASTContext()->getASTContext();
|
||||
|
||||
// Extract the register context so we can read arguments from registers
|
||||
|
||||
|
@ -582,7 +591,7 @@ ABISysV_x86_64::GetReturnValue (Thread &thread,
|
|||
default:
|
||||
case 128:
|
||||
// Scalar can't hold 128-bit literals, so we don't handle this
|
||||
return false;
|
||||
return return_valobj_sp;
|
||||
case 64:
|
||||
if (is_signed)
|
||||
value.GetScalar() = (int64_t)(thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0));
|
||||
|
@ -609,6 +618,57 @@ ABISysV_x86_64::GetReturnValue (Thread &thread,
|
|||
break;
|
||||
}
|
||||
}
|
||||
else if (ClangASTContext::IsFloatingPointType(value_type, count, is_complex))
|
||||
{
|
||||
// Don't handle complex yet.
|
||||
if (is_complex)
|
||||
return return_valobj_sp;
|
||||
|
||||
size_t bit_width = ClangASTType::GetClangTypeBitWidth(ast_context, value_type);
|
||||
if (bit_width <= 64)
|
||||
{
|
||||
const RegisterInfo *xmm0_info = reg_ctx->GetRegisterInfoByName("xmm0", 0);
|
||||
RegisterValue xmm0_value;
|
||||
if (reg_ctx->ReadRegister (xmm0_info, xmm0_value))
|
||||
{
|
||||
DataExtractor data;
|
||||
if (xmm0_value.GetData(data))
|
||||
{
|
||||
uint32_t offset = 0;
|
||||
switch (bit_width)
|
||||
{
|
||||
default:
|
||||
return return_valobj_sp;
|
||||
case 32:
|
||||
value.GetScalar() = (float) data.GetFloat(&offset);
|
||||
break;
|
||||
case 64:
|
||||
value.GetScalar() = (double) data.GetDouble(&offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bit_width == 128)
|
||||
{
|
||||
// FIXME: x86_64 returns long doubles in stmm0, which is in some 80 bit long double
|
||||
// format, and so we'll have to write some code to convert that into 128 bit long doubles.
|
||||
// const RegisterInfo *st0_info = reg_ctx->GetRegisterInfoByName("stmm0", 0);
|
||||
// RegisterValue st0_value;
|
||||
// if (reg_ctx->ReadRegister (st0_info, st0_value))
|
||||
// {
|
||||
// DataExtractor data;
|
||||
// if (st0_value.GetData(data))
|
||||
// {
|
||||
// uint32_t offset = 0;
|
||||
// value.GetScalar() = (long double) data.GetLongDouble (&offset);
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
|
||||
return return_valobj_sp;
|
||||
}
|
||||
}
|
||||
else if (ClangASTContext::IsPointerType (value_type))
|
||||
{
|
||||
unsigned rax_id = reg_ctx->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
|
||||
|
@ -616,14 +676,278 @@ ABISysV_x86_64::GetReturnValue (Thread &thread,
|
|||
}
|
||||
else
|
||||
{
|
||||
// not handled yet
|
||||
return false;
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
// If we get here, we have a valid Value, so make our ValueObject out of it:
|
||||
|
||||
return_valobj_sp = ValueObjectConstResult::Create(
|
||||
thread.GetStackFrameAtIndex(0).get(),
|
||||
ast_type.GetASTContext(),
|
||||
value,
|
||||
ConstString(""));
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
ValueObjectSP
|
||||
ABISysV_x86_64::GetReturnValueObjectImpl (Thread &thread,
|
||||
ClangASTType &ast_type) const
|
||||
{
|
||||
|
||||
ValueObjectSP return_valobj_sp;
|
||||
|
||||
return_valobj_sp = GetReturnValueObjectSimple(thread, ast_type);
|
||||
if (return_valobj_sp)
|
||||
return return_valobj_sp;
|
||||
|
||||
clang_type_t ret_value_type = ast_type.GetOpaqueQualType();
|
||||
if (!ret_value_type)
|
||||
return return_valobj_sp;
|
||||
|
||||
clang::ASTContext *ast_context = ast_type.GetASTContext();
|
||||
if (!ast_context)
|
||||
return return_valobj_sp;
|
||||
|
||||
RegisterContextSP reg_ctx_sp = thread.GetRegisterContext();
|
||||
if (!reg_ctx_sp)
|
||||
return return_valobj_sp;
|
||||
|
||||
size_t bit_width = ClangASTType::GetClangTypeBitWidth(ast_context, ret_value_type);
|
||||
if (ClangASTContext::IsAggregateType(ret_value_type))
|
||||
{
|
||||
Target &target = thread.GetProcess().GetTarget();
|
||||
bool is_memory = true;
|
||||
if (bit_width <= 128)
|
||||
{
|
||||
ByteOrder target_byte_order = target.GetArchitecture().GetByteOrder();
|
||||
DataBufferSP data_sp (new DataBufferHeap(16, 0));
|
||||
DataExtractor return_ext (data_sp,
|
||||
target_byte_order,
|
||||
target.GetArchitecture().GetAddressByteSize());
|
||||
|
||||
const RegisterInfo *rax_info = reg_ctx_sp->GetRegisterInfoByName("rax", 0);
|
||||
const RegisterInfo *rdx_info = reg_ctx_sp->GetRegisterInfoByName("rdx", 0);
|
||||
const RegisterInfo *xmm0_info = reg_ctx_sp->GetRegisterInfoByName("xmm0", 0);
|
||||
const RegisterInfo *xmm1_info = reg_ctx_sp->GetRegisterInfoByName("xmm1", 0);
|
||||
|
||||
RegisterValue rax_value, rdx_value, xmm0_value, xmm1_value;
|
||||
reg_ctx_sp->ReadRegister (rax_info, rax_value);
|
||||
reg_ctx_sp->ReadRegister (rdx_info, rdx_value);
|
||||
reg_ctx_sp->ReadRegister (xmm0_info, xmm0_value);
|
||||
reg_ctx_sp->ReadRegister (xmm1_info, xmm1_value);
|
||||
|
||||
DataExtractor rax_data, rdx_data, xmm0_data, xmm1_data;
|
||||
|
||||
rax_value.GetData(rax_data);
|
||||
rdx_value.GetData(rdx_data);
|
||||
xmm0_value.GetData(xmm0_data);
|
||||
xmm1_value.GetData(xmm1_data);
|
||||
|
||||
uint32_t fp_bytes = 0; // Tracks how much of the xmm registers we've consumed so far
|
||||
uint32_t integer_bytes = 0; // Tracks how much of the rax/rds registers we've consumed so far
|
||||
|
||||
uint32_t num_children = ClangASTContext::GetNumFields (ast_context, ret_value_type);
|
||||
|
||||
// Since we are in the small struct regime, assume we are not in memory.
|
||||
is_memory = false;
|
||||
|
||||
for (uint32_t idx = 0; idx < num_children; idx++)
|
||||
{
|
||||
std::string name;
|
||||
uint32_t field_bit_offset;
|
||||
bool is_signed;
|
||||
bool is_complex;
|
||||
uint32_t count;
|
||||
|
||||
clang_type_t field_clang_type = ClangASTContext::GetFieldAtIndex (ast_context, ret_value_type, idx, name, &field_bit_offset);
|
||||
size_t field_bit_width = ClangASTType::GetClangTypeBitWidth(ast_context, field_clang_type);
|
||||
|
||||
// If there are any unaligned fields, this is stored in memory.
|
||||
if (field_bit_offset % field_bit_width != 0)
|
||||
{
|
||||
is_memory = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
uint32_t field_byte_width = field_bit_width/8;
|
||||
uint32_t field_byte_offset = field_bit_offset/8;
|
||||
|
||||
|
||||
DataExtractor *copy_from_extractor = NULL;
|
||||
uint32_t copy_from_offset = 0;
|
||||
|
||||
if (ClangASTContext::IsIntegerType (field_clang_type, is_signed) || ClangASTContext::IsPointerType (field_clang_type))
|
||||
{
|
||||
if (integer_bytes < 8)
|
||||
{
|
||||
if (integer_bytes + field_byte_width <= 8)
|
||||
{
|
||||
// This is in RAX, copy from register to our result structure:
|
||||
copy_from_extractor = &rax_data;
|
||||
copy_from_offset = integer_bytes;
|
||||
integer_bytes += field_byte_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The next field wouldn't fit in the remaining space, so we pushed it to rdx.
|
||||
copy_from_extractor = &rdx_data;
|
||||
copy_from_offset = 0;
|
||||
integer_bytes = 8 + field_byte_width;
|
||||
|
||||
}
|
||||
}
|
||||
else if (integer_bytes + field_byte_width <= 16)
|
||||
{
|
||||
copy_from_extractor = &rdx_data;
|
||||
copy_from_offset = integer_bytes - 8;
|
||||
integer_bytes += field_byte_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The last field didn't fit. I can't see how that would happen w/o the overall size being
|
||||
// greater than 16 bytes. For now, return a NULL return value object.
|
||||
return return_valobj_sp;
|
||||
}
|
||||
}
|
||||
else if (ClangASTContext::IsFloatingPointType (field_clang_type, count, is_complex))
|
||||
{
|
||||
// Structs with long doubles are always passed in memory.
|
||||
if (field_bit_width == 128)
|
||||
{
|
||||
is_memory = true;
|
||||
break;
|
||||
}
|
||||
else if (field_bit_width == 64)
|
||||
{
|
||||
// These have to be in a single xmm register.
|
||||
if (fp_bytes == 0)
|
||||
copy_from_extractor = &xmm0_data;
|
||||
else
|
||||
copy_from_extractor = &xmm1_data;
|
||||
|
||||
copy_from_offset = 0;
|
||||
fp_bytes += field_byte_width;
|
||||
}
|
||||
else if (field_bit_width == 32)
|
||||
{
|
||||
// This one is kind of complicated. If we are in an "eightbyte" with another float, we'll
|
||||
// be stuffed into an xmm register with it. If we are in an "eightbyte" with one or more ints,
|
||||
// then we will be stuffed into the appropriate GPR with them.
|
||||
bool in_gpr;
|
||||
if (field_byte_offset % 8 == 0)
|
||||
{
|
||||
// We are at the beginning of one of the eightbytes, so check the next element (if any)
|
||||
if (idx == num_children - 1)
|
||||
in_gpr = true;
|
||||
else
|
||||
{
|
||||
uint32_t next_field_bit_offset;
|
||||
clang_type_t next_field_clang_type = ClangASTContext::GetFieldAtIndex (ast_context,
|
||||
ret_value_type,
|
||||
idx + 1,
|
||||
name,
|
||||
&next_field_bit_offset);
|
||||
if (ClangASTContext::IsIntegerType (next_field_clang_type, is_signed))
|
||||
in_gpr = true;
|
||||
else
|
||||
{
|
||||
copy_from_offset = 0;
|
||||
in_gpr = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (field_byte_offset % 4 == 0)
|
||||
{
|
||||
// We are inside of an eightbyte, so see if the field before us is floating point:
|
||||
// This could happen if somebody put padding in the structure.
|
||||
if (idx == 0)
|
||||
in_gpr = false;
|
||||
else
|
||||
{
|
||||
uint32_t prev_field_bit_offset;
|
||||
clang_type_t prev_field_clang_type = ClangASTContext::GetFieldAtIndex (ast_context,
|
||||
ret_value_type,
|
||||
idx - 1,
|
||||
name,
|
||||
&prev_field_bit_offset);
|
||||
if (ClangASTContext::IsIntegerType (prev_field_clang_type, is_signed))
|
||||
in_gpr = true;
|
||||
else
|
||||
{
|
||||
copy_from_offset = 4;
|
||||
in_gpr = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
is_memory = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Okay, we've figured out whether we are in GPR or XMM, now figure out which one.
|
||||
if (in_gpr)
|
||||
{
|
||||
if (integer_bytes < 8)
|
||||
{
|
||||
// This is in RAX, copy from register to our result structure:
|
||||
copy_from_extractor = &rax_data;
|
||||
copy_from_offset = integer_bytes;
|
||||
integer_bytes += field_byte_width;
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_from_extractor = &rdx_data;
|
||||
copy_from_offset = integer_bytes - 8;
|
||||
integer_bytes += field_byte_width;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fp_bytes < 8)
|
||||
copy_from_extractor = &xmm0_data;
|
||||
else
|
||||
copy_from_extractor = &xmm1_data;
|
||||
|
||||
fp_bytes += field_byte_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!copy_from_extractor)
|
||||
return return_valobj_sp;
|
||||
|
||||
copy_from_extractor->CopyByteOrderedData (copy_from_offset,
|
||||
field_byte_width,
|
||||
data_sp->GetBytes() + field_byte_offset,
|
||||
field_byte_width,
|
||||
target_byte_order);
|
||||
}
|
||||
|
||||
if (!is_memory)
|
||||
{
|
||||
// The result is in our data buffer. Let's make a variable object out of it:
|
||||
return_valobj_sp = ValueObjectConstResult::Create (&thread,
|
||||
ast_context,
|
||||
ret_value_type,
|
||||
ConstString(""),
|
||||
return_ext);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_memory)
|
||||
{
|
||||
unsigned rax_id = reg_ctx_sp->GetRegisterInfoByName("rax", 0)->kinds[eRegisterKindLLDB];
|
||||
lldb::addr_t storage_addr = (uint64_t)thread.GetRegisterContext()->ReadRegisterAsUnsigned(rax_id, 0);
|
||||
return_valobj_sp = ValueObjectMemory::Create (&thread,
|
||||
"",
|
||||
Address (storage_addr, NULL),
|
||||
ast_type);
|
||||
}
|
||||
}
|
||||
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -45,9 +45,14 @@ public:
|
|||
GetArgumentValues (lldb_private::Thread &thread,
|
||||
lldb_private::ValueList &values) const;
|
||||
|
||||
virtual bool
|
||||
GetReturnValue (lldb_private::Thread &thread,
|
||||
lldb_private::Value &value) const;
|
||||
protected:
|
||||
lldb::ValueObjectSP
|
||||
GetReturnValueObjectSimple (lldb_private::Thread &thread,
|
||||
lldb_private::ClangASTType &ast_type) const;
|
||||
public:
|
||||
virtual lldb::ValueObjectSP
|
||||
GetReturnValueObjectImpl (lldb_private::Thread &thread,
|
||||
lldb_private::ClangASTType &type) const;
|
||||
|
||||
virtual bool
|
||||
CreateFunctionEntryUnwindPlan (lldb_private::UnwindPlan &unwind_plan);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
#include "InferiorCallPOSIX.h"
|
||||
#include "lldb/Core/StreamFile.h"
|
||||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Core/ValueObject.h"
|
||||
#include "lldb/Symbol/SymbolContext.h"
|
||||
#include "lldb/Target/ExecutionContext.h"
|
||||
#include "lldb/Target/Process.h"
|
||||
|
@ -70,9 +70,12 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
|
|||
AddressRange mmap_range;
|
||||
if (sc.GetAddressRange(range_scope, 0, use_inline_block_range, mmap_range))
|
||||
{
|
||||
ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
|
||||
lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
|
||||
ThreadPlanCallFunction *call_function_thread_plan
|
||||
= new ThreadPlanCallFunction (*thread,
|
||||
mmap_range.GetBaseAddress(),
|
||||
ClangASTType (clang_ast_context->getASTContext(), clang_void_ptr_type),
|
||||
stop_other_threads,
|
||||
discard_on_error,
|
||||
&addr,
|
||||
|
@ -84,13 +87,6 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
|
|||
lldb::ThreadPlanSP call_plan_sp (call_function_thread_plan);
|
||||
if (call_plan_sp)
|
||||
{
|
||||
ValueSP return_value_sp (new Value);
|
||||
ClangASTContext *clang_ast_context = process->GetTarget().GetScratchClangASTContext();
|
||||
lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
|
||||
return_value_sp->SetValueType (Value::eValueTypeScalar);
|
||||
return_value_sp->SetContext (Value::eContextTypeClangType, clang_void_ptr_type);
|
||||
call_function_thread_plan->RequestReturnValue (return_value_sp);
|
||||
|
||||
StreamFile error_strm;
|
||||
StackFrame *frame = thread->GetStackFrameAtIndex (0).get();
|
||||
if (frame)
|
||||
|
@ -106,7 +102,8 @@ bool lldb_private::InferiorCallMmap(Process *process, addr_t &allocated_addr,
|
|||
error_strm);
|
||||
if (result == eExecutionCompleted)
|
||||
{
|
||||
allocated_addr = return_value_sp->GetScalar().ULongLong();
|
||||
|
||||
allocated_addr = call_plan_sp->GetReturnValueObject()->GetValueAsUnsigned(LLDB_INVALID_ADDRESS);
|
||||
if (process->GetAddressByteSize() == 4)
|
||||
{
|
||||
if (allocated_addr == UINT32_MAX)
|
||||
|
@ -155,6 +152,7 @@ bool lldb_private::InferiorCallMunmap(Process *process, addr_t addr,
|
|||
{
|
||||
lldb::ThreadPlanSP call_plan_sp (new ThreadPlanCallFunction (*thread,
|
||||
munmap_range.GetBaseAddress(),
|
||||
ClangASTType(),
|
||||
stop_other_threads,
|
||||
discard_on_error,
|
||||
&addr,
|
||||
|
|
|
@ -5411,10 +5411,11 @@ ClangASTContext::IsIntegerType (clang_type_t clang_type, bool &is_signed)
|
|||
if (builtin_type)
|
||||
{
|
||||
if (builtin_type->isInteger())
|
||||
{
|
||||
is_signed = builtin_type->isSignedInteger();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "lldb/Core/Value.h"
|
||||
#include "lldb/Core/ValueObjectConstResult.h"
|
||||
#include "lldb/Symbol/ClangASTType.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
#include "lldb/Target/Thread.h"
|
||||
|
||||
using namespace lldb;
|
||||
|
@ -104,25 +105,69 @@ ABI::GetRegisterInfoByKind (RegisterKind reg_kind, uint32_t reg_num, RegisterInf
|
|||
|
||||
ValueObjectSP
|
||||
ABI::GetReturnValueObject (Thread &thread,
|
||||
ClangASTType &ast_type) const
|
||||
ClangASTType &ast_type,
|
||||
bool persistent) const
|
||||
{
|
||||
if (!ast_type.IsValid())
|
||||
return ValueObjectSP();
|
||||
|
||||
Value ret_value;
|
||||
ret_value.SetContext(Value::eContextTypeClangType,
|
||||
ast_type.GetOpaqueQualType());
|
||||
if (GetReturnValue (thread, ret_value))
|
||||
{
|
||||
return ValueObjectConstResult::Create(
|
||||
thread.GetStackFrameAtIndex(0).get(),
|
||||
ast_type.GetASTContext(),
|
||||
ret_value,
|
||||
ConstString("FunctionReturn"));
|
||||
ValueObjectSP return_valobj_sp;
|
||||
|
||||
return_valobj_sp = GetReturnValueObjectImpl(thread, ast_type);
|
||||
if (!return_valobj_sp)
|
||||
return return_valobj_sp;
|
||||
|
||||
// Now turn this into a persistent variable.
|
||||
// FIXME: This code is duplicated from Target::EvaluateExpression, and it is used in similar form in a couple
|
||||
// of other places. Figure out the correct Create function to do all this work.
|
||||
|
||||
if (persistent)
|
||||
{
|
||||
ClangPersistentVariables& persistent_variables = thread.GetProcess().GetTarget().GetPersistentVariables();
|
||||
ConstString persistent_variable_name (persistent_variables.GetNextPersistentVariableName());
|
||||
|
||||
lldb::ValueObjectSP const_valobj_sp;
|
||||
|
||||
// Check in case our value is already a constant value
|
||||
if (return_valobj_sp->GetIsConstant())
|
||||
{
|
||||
const_valobj_sp = return_valobj_sp;
|
||||
const_valobj_sp->SetName (persistent_variable_name);
|
||||
}
|
||||
else
|
||||
return ValueObjectSP();
|
||||
const_valobj_sp = return_valobj_sp->CreateConstantValue (persistent_variable_name);
|
||||
|
||||
lldb::ValueObjectSP live_valobj_sp = return_valobj_sp;
|
||||
|
||||
return_valobj_sp = const_valobj_sp;
|
||||
|
||||
ClangExpressionVariableSP clang_expr_variable_sp(persistent_variables.CreatePersistentVariable(return_valobj_sp));
|
||||
|
||||
assert (clang_expr_variable_sp.get());
|
||||
|
||||
// Set flags and live data as appropriate
|
||||
|
||||
const Value &result_value = live_valobj_sp->GetValue();
|
||||
|
||||
switch (result_value.GetValueType())
|
||||
{
|
||||
case Value::eValueTypeHostAddress:
|
||||
case Value::eValueTypeFileAddress:
|
||||
// we don't do anything with these for now
|
||||
break;
|
||||
case Value::eValueTypeScalar:
|
||||
clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsLLDBAllocated;
|
||||
clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVNeedsAllocation;
|
||||
break;
|
||||
case Value::eValueTypeLoadAddress:
|
||||
clang_expr_variable_sp->m_live_sp = live_valobj_sp;
|
||||
clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference;
|
||||
break;
|
||||
}
|
||||
return_valobj_sp = clang_expr_variable_sp->GetValueObject();
|
||||
|
||||
}
|
||||
return return_valobj_sp;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -861,7 +861,7 @@ Thread::QueueThreadPlanForCallFunction (bool abort_other_plans,
|
|||
bool stop_other_threads,
|
||||
bool discard_on_error)
|
||||
{
|
||||
ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, arg, stop_other_threads, discard_on_error));
|
||||
ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this, function, ClangASTType(), arg, stop_other_threads, discard_on_error));
|
||||
QueueThreadPlan (thread_plan_sp, abort_other_plans);
|
||||
return thread_plan_sp.get();
|
||||
}
|
||||
|
|
|
@ -37,6 +37,7 @@ using namespace lldb_private;
|
|||
|
||||
ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
|
||||
Address &function,
|
||||
const ClangASTType &return_type,
|
||||
addr_t arg,
|
||||
bool stop_other_threads,
|
||||
bool discard_on_error,
|
||||
|
@ -47,6 +48,7 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
|
|||
m_stop_other_threads (stop_other_threads),
|
||||
m_function_addr (function),
|
||||
m_function_sp (NULL),
|
||||
m_return_type (return_type),
|
||||
m_takedown_done (false),
|
||||
m_stop_address (LLDB_INVALID_ADDRESS)
|
||||
{
|
||||
|
@ -149,6 +151,7 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
|
|||
|
||||
ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
|
||||
Address &function,
|
||||
const ClangASTType &return_type,
|
||||
bool stop_other_threads,
|
||||
bool discard_on_error,
|
||||
addr_t *arg1_ptr,
|
||||
|
@ -162,6 +165,7 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
|
|||
m_stop_other_threads (stop_other_threads),
|
||||
m_function_addr (function),
|
||||
m_function_sp(NULL),
|
||||
m_return_type (return_type),
|
||||
m_takedown_done (false)
|
||||
{
|
||||
SetOkayToDiscard (discard_on_error);
|
||||
|
@ -280,14 +284,13 @@ ThreadPlanCallFunction::DoTakedown ()
|
|||
{
|
||||
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
|
||||
if (!m_takedown_done)
|
||||
{
|
||||
// TODO: how do we tell if all went well?
|
||||
if (m_return_value_sp)
|
||||
{
|
||||
const ABI *abi = m_thread.GetProcess().GetABI().get();
|
||||
if (abi)
|
||||
abi->GetReturnValue(m_thread, *m_return_value_sp);
|
||||
if (abi && m_return_type.IsValid())
|
||||
{
|
||||
m_return_valobj_sp = abi->GetReturnValueObject (m_thread, m_return_type);
|
||||
}
|
||||
|
||||
if (log)
|
||||
log->Printf ("DoTakedown called for thread 0x%4.4llx, m_valid: %d complete: %d.\n", m_thread.GetID(), m_valid, IsPlanComplete());
|
||||
m_takedown_done = true;
|
||||
|
|
|
@ -44,7 +44,7 @@ ThreadPlanCallUserExpression::ThreadPlanCallUserExpression (Thread &thread,
|
|||
lldb::addr_t *this_arg,
|
||||
lldb::addr_t *cmd_arg,
|
||||
ClangUserExpression::ClangUserExpressionSP &user_expression_sp) :
|
||||
ThreadPlanCallFunction (thread, function, arg, stop_other_threads, discard_on_error, this_arg, cmd_arg),
|
||||
ThreadPlanCallFunction (thread, function, ClangASTType(), arg, stop_other_threads, discard_on_error, this_arg, cmd_arg),
|
||||
m_user_expression_sp (user_expression_sp)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../make
|
||||
|
||||
C_SOURCES := call-func.c
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,127 @@
|
|||
"""
|
||||
Test getting return-values correctly when stepping out
|
||||
"""
|
||||
|
||||
import os, time
|
||||
import re
|
||||
import unittest2
|
||||
import lldb, lldbutil
|
||||
from lldbtest import *
|
||||
|
||||
class ReturnValueTestCase(TestBase):
|
||||
|
||||
mydir = os.path.join("functionalities", "return-value")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
@python_api_test
|
||||
def test_with_dsym_python(self):
|
||||
"""Test getting return values from stepping out with dsyms."""
|
||||
self.buildDsym()
|
||||
self.do_return_value()
|
||||
|
||||
@python_api_test
|
||||
def test_with_dwarf_python(self):
|
||||
"""Test getting return values from stepping out."""
|
||||
self.buildDwarf()
|
||||
self.do_return_value()
|
||||
|
||||
def do_return_value(self):
|
||||
"""Test getting return values from stepping out."""
|
||||
exe = os.path.join(os.getcwd(), "a.out")
|
||||
error = lldb.SBError()
|
||||
|
||||
target = self.dbg.CreateTarget(exe)
|
||||
self.assertTrue(target, VALID_TARGET)
|
||||
|
||||
inner_sint_bkpt = target.BreakpointCreateByName("inner_sint", exe)
|
||||
self.assertTrue(inner_sint_bkpt, VALID_BREAKPOINT)
|
||||
|
||||
# Now launch the process, and do not stop at entry point.
|
||||
process = target.LaunchSimple(None, None, os.getcwd())
|
||||
|
||||
self.assertTrue(process, PROCESS_IS_VALID)
|
||||
|
||||
# The stop reason of the thread should be breakpoint.
|
||||
self.assertTrue(process.GetState() == lldb.eStateStopped,
|
||||
STOPPED_DUE_TO_BREAKPOINT)
|
||||
|
||||
# Now finish, and make sure the return value is correct.
|
||||
thread = lldbutil.get_stopped_thread (process, lldb.eStopReasonBreakpoint)
|
||||
|
||||
thread.StepOut();
|
||||
|
||||
self.assertTrue (process.GetState() == lldb.eStateStopped)
|
||||
self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete)
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue (fun_name == "outer_sint")
|
||||
|
||||
return_value = thread.GetStopReturnValue()
|
||||
self.assertTrue (return_value.IsValid())
|
||||
|
||||
in_int = frame.FindVariable ("value").GetValueAsSigned(error)
|
||||
self.assertTrue (error.Success())
|
||||
ret_int = return_value.GetValueAsSigned(error)
|
||||
self.assertTrue (error.Success())
|
||||
self.assertTrue (in_int == ret_int)
|
||||
|
||||
# Run again and we will stop in inner_sint the second time outer_sint is called.
|
||||
#Then test stepping out two frames at once:
|
||||
|
||||
process.Continue()
|
||||
thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, inner_sint_bkpt)
|
||||
self.assertTrue(len(thread_list) == 1)
|
||||
thread = thread_list[0]
|
||||
|
||||
frame = thread.GetFrameAtIndex(1)
|
||||
fun_name = frame.GetFunctionName ()
|
||||
self.assertTrue (fun_name == "outer_sint")
|
||||
in_int = frame.FindVariable ("value").GetValueAsSigned(error)
|
||||
self.assertTrue (error.Success())
|
||||
|
||||
thread.StepOutOfFrame (frame)
|
||||
|
||||
self.assertTrue (process.GetState() == lldb.eStateStopped)
|
||||
self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete)
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue (fun_name == "main")
|
||||
|
||||
ret_value = thread.GetStopReturnValue()
|
||||
self.assertTrue (return_value.IsValid())
|
||||
ret_int = ret_value.GetValueAsSigned (error)
|
||||
self.assertTrue (error.Success())
|
||||
self.assertTrue (in_int == ret_int)
|
||||
|
||||
# Now try some simple returns that have different types:
|
||||
inner_float_bkpt = target.BreakpointCreateByName("inner_float", exe)
|
||||
self.assertTrue(inner_float_bkpt, VALID_BREAKPOINT)
|
||||
process.Continue()
|
||||
thread_list = lldbutil.get_threads_stopped_at_breakpoint (process, inner_float_bkpt)
|
||||
self.assertTrue (len(thread_list) == 1)
|
||||
thread = thread_list[0]
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
in_value = frame.FindVariable ("value")
|
||||
in_float = float (in_value.GetValue())
|
||||
thread.StepOut()
|
||||
|
||||
self.assertTrue (process.GetState() == lldb.eStateStopped)
|
||||
self.assertTrue (thread.GetStopReason() == lldb.eStopReasonPlanComplete)
|
||||
|
||||
frame = thread.GetFrameAtIndex(0)
|
||||
fun_name = frame.GetFunctionName()
|
||||
self.assertTrue (fun_name == "outer_float")
|
||||
|
||||
return_value = thread.GetStopReturnValue()
|
||||
self.assertTrue (return_value.IsValid())
|
||||
return_float = float (return_value.GetValue())
|
||||
|
||||
self.assertTrue(in_float == return_float)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,90 @@
|
|||
int
|
||||
inner_sint (int value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
int
|
||||
outer_sint (int value)
|
||||
{
|
||||
return inner_sint (value);
|
||||
}
|
||||
|
||||
float
|
||||
inner_float (float value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
float
|
||||
outer_float (float value)
|
||||
{
|
||||
return inner_float(value);
|
||||
}
|
||||
|
||||
double
|
||||
inner_double (double value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
double
|
||||
outer_double (double value)
|
||||
{
|
||||
return inner_double(value);
|
||||
}
|
||||
|
||||
long double
|
||||
inner_long_double (long double value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
long double
|
||||
outer_long_double (long double value)
|
||||
{
|
||||
return inner_long_double(value);
|
||||
}
|
||||
|
||||
struct
|
||||
large_return_struct
|
||||
{
|
||||
long long first_long;
|
||||
long long second_long;
|
||||
long long third_long;
|
||||
long long fourth_long;
|
||||
|
||||
};
|
||||
|
||||
struct large_return_struct
|
||||
return_large_struct (long long first, long long second, long long third, long long fourth)
|
||||
{
|
||||
return (struct large_return_struct) {first, second, third, fourth};
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
int first_int = 123456;
|
||||
int second_int = 234567;
|
||||
|
||||
outer_sint (first_int);
|
||||
outer_sint (second_int);
|
||||
|
||||
float float_value = 12.34;
|
||||
|
||||
outer_float (float_value);
|
||||
|
||||
double double_value = -23.45;
|
||||
|
||||
outer_double (double_value);
|
||||
|
||||
long double long_double_value = -3456789.987654321;
|
||||
|
||||
outer_long_double (long_double_value);
|
||||
|
||||
return_large_struct (10, 20, 30, 40);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
Loading…
Reference in New Issue