aboutsummaryrefslogtreecommitdiff
path: root/source/Target
diff options
context:
space:
mode:
Diffstat (limited to 'source/Target')
-rw-r--r--source/Target/ABI.cpp175
-rw-r--r--source/Target/CPPLanguageRuntime.cpp386
-rw-r--r--source/Target/ExecutionContext.cpp824
-rw-r--r--source/Target/LanguageRuntime.cpp343
-rw-r--r--source/Target/Memory.cpp461
-rw-r--r--source/Target/ObjCLanguageRuntime.cpp605
-rw-r--r--source/Target/OperatingSystem.cpp67
-rw-r--r--source/Target/PathMappingList.cpp348
-rw-r--r--source/Target/Platform.cpp779
-rw-r--r--source/Target/Process.cpp5595
-rw-r--r--source/Target/RegisterContext.cpp600
-rw-r--r--source/Target/SectionLoadList.cpp276
-rw-r--r--source/Target/StackFrame.cpp1449
-rw-r--r--source/Target/StackFrameList.cpp899
-rw-r--r--source/Target/StackID.cpp110
-rw-r--r--source/Target/StopInfo.cpp1143
-rw-r--r--source/Target/Target.cpp2866
-rw-r--r--source/Target/TargetList.cpp576
-rw-r--r--source/Target/Thread.cpp1988
-rw-r--r--source/Target/ThreadList.cpp785
-rw-r--r--source/Target/ThreadPlan.cpp353
-rw-r--r--source/Target/ThreadPlanBase.cpp237
-rw-r--r--source/Target/ThreadPlanCallFunction.cpp614
-rw-r--r--source/Target/ThreadPlanCallUserExpression.cpp82
-rw-r--r--source/Target/ThreadPlanRunToAddress.cpp268
-rw-r--r--source/Target/ThreadPlanShouldStopHere.cpp74
-rw-r--r--source/Target/ThreadPlanStepInRange.cpp485
-rw-r--r--source/Target/ThreadPlanStepInstruction.cpp227
-rw-r--r--source/Target/ThreadPlanStepOut.cpp489
-rw-r--r--source/Target/ThreadPlanStepOverBreakpoint.cpp165
-rw-r--r--source/Target/ThreadPlanStepOverRange.cpp388
-rw-r--r--source/Target/ThreadPlanStepRange.cpp522
-rw-r--r--source/Target/ThreadPlanStepThrough.cpp290
-rw-r--r--source/Target/ThreadPlanStepUntil.cpp413
-rw-r--r--source/Target/ThreadPlanTracer.cpp286
-rw-r--r--source/Target/ThreadSpec.cpp158
-rw-r--r--source/Target/UnixSignals.cpp293
-rw-r--r--source/Target/UnwindAssembly.cpp41
38 files changed, 25660 insertions, 0 deletions
diff --git a/source/Target/ABI.cpp b/source/Target/ABI.cpp
new file mode 100644
index 000000000000..06215221d961
--- /dev/null
+++ b/source/Target/ABI.cpp
@@ -0,0 +1,175 @@
+//===-- ABI.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/Target/ABI.h"
+#include "lldb/Core/PluginManager.h"
+#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;
+using namespace lldb_private;
+
+ABISP
+ABI::FindPlugin (const ArchSpec &arch)
+{
+ ABISP abi_sp;
+ ABICreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetABICreateCallbackAtIndex(idx)) != NULL;
+ ++idx)
+ {
+ abi_sp = create_callback(arch);
+
+ if (abi_sp)
+ return abi_sp;
+ }
+ abi_sp.reset();
+ return abi_sp;
+}
+
+//----------------------------------------------------------------------
+// Constructor
+//----------------------------------------------------------------------
+ABI::ABI()
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ABI::~ABI()
+{
+}
+
+
+bool
+ABI::GetRegisterInfoByName (const ConstString &name, RegisterInfo &info)
+{
+ uint32_t count = 0;
+ const RegisterInfo *register_info_array = GetRegisterInfoArray (count);
+ if (register_info_array)
+ {
+ const char *unique_name_cstr = name.GetCString();
+ uint32_t i;
+ for (i=0; i<count; ++i)
+ {
+ if (register_info_array[i].name == unique_name_cstr)
+ {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ for (i=0; i<count; ++i)
+ {
+ if (register_info_array[i].alt_name == unique_name_cstr)
+ {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool
+ABI::GetRegisterInfoByKind (RegisterKind reg_kind, uint32_t reg_num, RegisterInfo &info)
+{
+ if (reg_kind < eRegisterKindGCC || reg_kind >= kNumRegisterKinds)
+ return false;
+
+ uint32_t count = 0;
+ const RegisterInfo *register_info_array = GetRegisterInfoArray (count);
+ if (register_info_array)
+ {
+ for (uint32_t i=0; i<count; ++i)
+ {
+ if (register_info_array[i].kinds[reg_kind] == reg_num)
+ {
+ info = register_info_array[i];
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+ValueObjectSP
+ABI::GetReturnValueObject (Thread &thread,
+ ClangASTType &ast_type,
+ bool persistent) const
+{
+ if (!ast_type.IsValid())
+ return ValueObjectSP();
+
+ 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.CalculateTarget()->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
+ 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:
+ case Value::eValueTypeVector:
+ clang_expr_variable_sp->m_flags |= ClangExpressionVariable::EVIsFreezeDried;
+ 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;
+}
+
+
diff --git a/source/Target/CPPLanguageRuntime.cpp b/source/Target/CPPLanguageRuntime.cpp
new file mode 100644
index 000000000000..f5b7f7fc41a6
--- /dev/null
+++ b/source/Target/CPPLanguageRuntime.cpp
@@ -0,0 +1,386 @@
+//===-- CPPLanguageRuntime.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/Target/CPPLanguageRuntime.h"
+
+#include <string.h>
+
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/UniqueCStringMap.h"
+#include "lldb/Target/ExecutionContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+class CPPRuntimeEquivalents
+{
+public:
+ CPPRuntimeEquivalents ()
+ {
+
+ m_impl.Append(ConstString("std::basic_string<char, std::char_traits<char>, std::allocator<char> >").AsCString(), ConstString("basic_string<char>"));
+
+ // these two (with a prefixed std::) occur when c++stdlib string class occurs as a template argument in some STL container
+ m_impl.Append(ConstString("std::basic_string<char, std::char_traits<char>, std::allocator<char> >").AsCString(), ConstString("std::basic_string<char>"));
+
+ m_impl.Sort();
+ }
+
+ void
+ Add (ConstString& type_name,
+ ConstString& type_equivalent)
+ {
+ m_impl.Insert(type_name.AsCString(), type_equivalent);
+ }
+
+ uint32_t
+ FindExactMatches (ConstString& type_name,
+ std::vector<ConstString>& equivalents)
+ {
+
+ uint32_t count = 0;
+
+ for (ImplData match = m_impl.FindFirstValueForName(type_name.AsCString());
+ match != NULL;
+ match = m_impl.FindNextValueForName(match))
+ {
+ equivalents.push_back(match->value);
+ count++;
+ }
+
+ return count;
+ }
+
+ // partial matches can occur when a name with equivalents is a template argument.
+ // e.g. we may have "class Foo" be a match for "struct Bar". if we have a typename
+ // such as "class Templatized<class Foo, Anything>" we want this to be replaced with
+ // "class Templatized<struct Bar, Anything>". Since partial matching is time consuming
+ // once we get a partial match, we add it to the exact matches list for faster retrieval
+ uint32_t
+ FindPartialMatches (ConstString& type_name,
+ std::vector<ConstString>& equivalents)
+ {
+
+ uint32_t count = 0;
+
+ const char* type_name_cstr = type_name.AsCString();
+
+ size_t items_count = m_impl.GetSize();
+
+ for (size_t item = 0; item < items_count; item++)
+ {
+ const char* key_cstr = m_impl.GetCStringAtIndex(item);
+ if ( strstr(type_name_cstr,key_cstr) )
+ {
+ count += AppendReplacements(type_name_cstr,
+ key_cstr,
+ equivalents);
+ }
+ }
+
+ return count;
+
+ }
+
+private:
+
+ std::string& replace (std::string& target,
+ std::string& pattern,
+ std::string& with)
+ {
+ size_t pos;
+ size_t pattern_len = pattern.size();
+
+ while ( (pos = target.find(pattern)) != std::string::npos )
+ target.replace(pos, pattern_len, with);
+
+ return target;
+ }
+
+ uint32_t
+ AppendReplacements (const char* original,
+ const char *matching_key,
+ std::vector<ConstString>& equivalents)
+ {
+
+ std::string matching_key_str(matching_key);
+ ConstString original_const(original);
+
+ uint32_t count = 0;
+
+ for (ImplData match = m_impl.FindFirstValueForName(matching_key);
+ match != NULL;
+ match = m_impl.FindNextValueForName(match))
+ {
+ std::string target(original);
+ std::string equiv_class(match->value.AsCString());
+
+ replace (target, matching_key_str, equiv_class);
+
+ ConstString target_const(target.c_str());
+
+// you will most probably want to leave this off since it might make this map grow indefinitely
+#ifdef ENABLE_CPP_EQUIVALENTS_MAP_TO_GROW
+ Add(original_const, target_const);
+#endif
+ equivalents.push_back(target_const);
+
+ count++;
+ }
+
+ return count;
+ }
+
+ typedef UniqueCStringMap<ConstString> Impl;
+ typedef const Impl::Entry* ImplData;
+ Impl m_impl;
+};
+
+static CPPRuntimeEquivalents&
+GetEquivalentsMap ()
+{
+ static CPPRuntimeEquivalents g_equivalents_map;
+ return g_equivalents_map;
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+CPPLanguageRuntime::~CPPLanguageRuntime()
+{
+}
+
+CPPLanguageRuntime::CPPLanguageRuntime (Process *process) :
+ LanguageRuntime (process)
+{
+
+}
+
+bool
+CPPLanguageRuntime::GetObjectDescription (Stream &str, ValueObject &object)
+{
+ // C++ has no generic way to do this.
+ return false;
+}
+
+bool
+CPPLanguageRuntime::GetObjectDescription (Stream &str, Value &value, ExecutionContextScope *exe_scope)
+{
+ // C++ has no generic way to do this.
+ return false;
+}
+
+bool
+CPPLanguageRuntime::IsCPPMangledName (const char *name)
+{
+ // FIXME, we should really run through all the known C++ Language plugins and ask each one if
+ // this is a C++ mangled name, but we can put that off till there is actually more than one
+ // we care about.
+
+ if (name && name[0] == '_' && name[1] == 'Z')
+ return true;
+ else
+ return false;
+}
+
+bool
+CPPLanguageRuntime::StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end)
+{
+ if (base_name_end == NULL)
+ base_name_end = name + strlen (name);
+
+ const char *last_colon = strrchr (name, ':');
+
+ if (last_colon == NULL)
+ {
+ base_name_start = name;
+ return true;
+ }
+
+ // Can't have a C++ name that begins with a single ':', nor contains an internal single ':'
+ if (last_colon == name)
+ return false;
+ else if (last_colon[-1] != ':')
+ return false;
+ else
+ {
+ // FIXME: should check if there is
+ base_name_start = last_colon + 1;
+ return true;
+ }
+}
+
+uint32_t
+CPPLanguageRuntime::FindEquivalentNames(ConstString type_name, std::vector<ConstString>& equivalents)
+{
+ uint32_t count = GetEquivalentsMap().FindExactMatches(type_name, equivalents);
+
+ bool might_have_partials=
+ ( count == 0 ) // if we have a full name match just use it
+ && (strchr(type_name.AsCString(), '<') != NULL // we should only have partial matches when templates are involved, check that we have
+ && strchr(type_name.AsCString(), '>') != NULL); // angle brackets in the type_name before trying to scan for partial matches
+
+ if ( might_have_partials )
+ count = GetEquivalentsMap().FindPartialMatches(type_name, equivalents);
+
+ return count;
+}
+
+void
+CPPLanguageRuntime::MethodName::Clear()
+{
+ m_full.Clear();
+ m_basename = llvm::StringRef();
+ m_context = llvm::StringRef();
+ m_arguments = llvm::StringRef();
+ m_qualifiers = llvm::StringRef();
+ m_type = eTypeInvalid;
+ m_parsed = false;
+ m_parse_error = false;
+}
+
+bool
+ReverseFindMatchingChars (const llvm::StringRef &s,
+ const llvm::StringRef &left_right_chars,
+ size_t &left_pos,
+ size_t &right_pos,
+ size_t pos = llvm::StringRef::npos)
+{
+ assert (left_right_chars.size() == 2);
+ left_pos = llvm::StringRef::npos;
+ const char left_char = left_right_chars[0];
+ const char right_char = left_right_chars[1];
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos || s[pos] == left_char)
+ return false;
+ right_pos = pos;
+ uint32_t depth = 1;
+ while (pos > 0 && depth > 0)
+ {
+ pos = s.find_last_of(left_right_chars, pos);
+ if (pos == llvm::StringRef::npos)
+ return false;
+ if (s[pos] == left_char)
+ {
+ if (--depth == 0)
+ {
+ left_pos = pos;
+ return left_pos < right_pos;
+ }
+ }
+ else if (s[pos] == right_char)
+ {
+ ++depth;
+ }
+ }
+ return false;
+}
+
+void
+CPPLanguageRuntime::MethodName::Parse()
+{
+ if (!m_parsed && m_full)
+ {
+// ConstString mangled;
+// m_full.GetMangledCounterpart(mangled);
+// printf ("\n parsing = '%s'\n", m_full.GetCString());
+// if (mangled)
+// printf (" mangled = '%s'\n", mangled.GetCString());
+ m_parse_error = false;
+ m_parsed = true;
+ llvm::StringRef full (m_full.GetCString());
+
+ size_t arg_start, arg_end;
+ llvm::StringRef parens("()", 2);
+ if (ReverseFindMatchingChars (full, parens, arg_start, arg_end))
+ {
+ m_arguments = full.substr(arg_start, arg_end - arg_start + 1);
+ if (arg_end + 1 < full.size())
+ m_qualifiers = full.substr(arg_end + 1);
+ if (arg_start > 0)
+ {
+ size_t basename_end = arg_start;
+ size_t context_end = llvm::StringRef::npos;
+ if (basename_end > 0 && full[basename_end-1] == '>')
+ {
+ // TODO: handle template junk...
+ // Templated function
+ size_t template_start, template_end;
+ llvm::StringRef lt_gt("<>", 2);
+ if (ReverseFindMatchingChars (full, lt_gt, template_start, template_end, basename_end))
+ context_end = full.rfind(':', template_start);
+ }
+ if (context_end == llvm::StringRef::npos)
+ context_end = full.rfind(':', basename_end);
+
+ if (context_end == llvm::StringRef::npos)
+ m_basename = full.substr(0, basename_end);
+ else
+ {
+ m_context = full.substr(0, context_end - 1);
+ const size_t basename_begin = context_end + 1;
+ m_basename = full.substr(basename_begin, basename_end - basename_begin);
+ }
+ m_type = eTypeUnknownMethod;
+ }
+ else
+ {
+ m_parse_error = true;
+ return;
+ }
+
+// if (!m_context.empty())
+// printf (" context = '%s'\n", m_context.str().c_str());
+// if (m_basename)
+// printf (" basename = '%s'\n", m_basename.GetCString());
+// if (!m_arguments.empty())
+// printf (" arguments = '%s'\n", m_arguments.str().c_str());
+// if (!m_qualifiers.empty())
+// printf ("qualifiers = '%s'\n", m_qualifiers.str().c_str());
+ }
+ else
+ {
+ m_parse_error = true;
+// printf ("error: didn't find matching parens for arguments\n");
+ }
+ }
+}
+
+llvm::StringRef
+CPPLanguageRuntime::MethodName::GetBasename ()
+{
+ if (!m_parsed)
+ Parse();
+ return m_basename;
+}
+
+llvm::StringRef
+CPPLanguageRuntime::MethodName::GetContext ()
+{
+ if (!m_parsed)
+ Parse();
+ return m_context;
+}
+
+llvm::StringRef
+CPPLanguageRuntime::MethodName::GetArguments ()
+{
+ if (!m_parsed)
+ Parse();
+ return m_arguments;
+}
+
+llvm::StringRef
+CPPLanguageRuntime::MethodName::GetQualifiers ()
+{
+ if (!m_parsed)
+ Parse();
+ return m_qualifiers;
+}
+
diff --git a/source/Target/ExecutionContext.cpp b/source/Target/ExecutionContext.cpp
new file mode 100644
index 000000000000..8b5731e80280
--- /dev/null
+++ b/source/Target/ExecutionContext.cpp
@@ -0,0 +1,824 @@
+//===-- ExecutionContext.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/Target/ExecutionContext.h"
+
+#include "lldb/Core/State.h"
+#include "lldb/Target/ExecutionContextScope.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb_private;
+
+ExecutionContext::ExecutionContext() :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+}
+
+ExecutionContext::ExecutionContext (const ExecutionContext &rhs) :
+ m_target_sp(rhs.m_target_sp),
+ m_process_sp(rhs.m_process_sp),
+ m_thread_sp(rhs.m_thread_sp),
+ m_frame_sp(rhs.m_frame_sp)
+{
+}
+
+ExecutionContext::ExecutionContext (const lldb::TargetSP &target_sp, bool get_process) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (target_sp)
+ SetContext (target_sp, get_process);
+}
+
+ExecutionContext::ExecutionContext (const lldb::ProcessSP &process_sp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (process_sp)
+ SetContext (process_sp);
+}
+
+ExecutionContext::ExecutionContext (const lldb::ThreadSP &thread_sp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (thread_sp)
+ SetContext (thread_sp);
+}
+
+ExecutionContext::ExecutionContext (const lldb::StackFrameSP &frame_sp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (frame_sp)
+ SetContext (frame_sp);
+}
+
+ExecutionContext::ExecutionContext (const lldb::TargetWP &target_wp, bool get_process) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ lldb::TargetSP target_sp(target_wp.lock());
+ if (target_sp)
+ SetContext (target_sp, get_process);
+}
+
+ExecutionContext::ExecutionContext (const lldb::ProcessWP &process_wp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ lldb::ProcessSP process_sp(process_wp.lock());
+ if (process_sp)
+ SetContext (process_sp);
+}
+
+ExecutionContext::ExecutionContext (const lldb::ThreadWP &thread_wp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ lldb::ThreadSP thread_sp(thread_wp.lock());
+ if (thread_sp)
+ SetContext (thread_sp);
+}
+
+ExecutionContext::ExecutionContext (const lldb::StackFrameWP &frame_wp) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ lldb::StackFrameSP frame_sp(frame_wp.lock());
+ if (frame_sp)
+ SetContext (frame_sp);
+}
+
+ExecutionContext::ExecutionContext (Target* t, bool fill_current_process_thread_frame) :
+ m_target_sp (t->shared_from_this()),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (t && fill_current_process_thread_frame)
+ {
+ m_process_sp = t->GetProcessSP();
+ if (m_process_sp)
+ {
+ m_thread_sp = m_process_sp->GetThreadList().GetSelectedThread();
+ if (m_thread_sp)
+ m_frame_sp = m_thread_sp->GetSelectedFrame();
+ }
+ }
+}
+
+ExecutionContext::ExecutionContext(Process* process, Thread *thread, StackFrame *frame) :
+ m_target_sp (),
+ m_process_sp (process->shared_from_this()),
+ m_thread_sp (thread->shared_from_this()),
+ m_frame_sp (frame->shared_from_this())
+{
+ if (process)
+ m_target_sp = process->GetTarget().shared_from_this();
+}
+
+ExecutionContext::ExecutionContext (const ExecutionContextRef &exe_ctx_ref) :
+ m_target_sp (exe_ctx_ref.GetTargetSP()),
+ m_process_sp (exe_ctx_ref.GetProcessSP()),
+ m_thread_sp (exe_ctx_ref.GetThreadSP()),
+ m_frame_sp (exe_ctx_ref.GetFrameSP())
+{
+}
+
+ExecutionContext::ExecutionContext (const ExecutionContextRef *exe_ctx_ref_ptr) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (exe_ctx_ref_ptr)
+ {
+ m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
+ m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
+ m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
+ m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
+ }
+}
+
+ExecutionContext::ExecutionContext (const ExecutionContextRef *exe_ctx_ref_ptr, Mutex::Locker &locker) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (exe_ctx_ref_ptr)
+ {
+ m_target_sp = exe_ctx_ref_ptr->GetTargetSP();
+ if (m_target_sp)
+ {
+ locker.Lock(m_target_sp->GetAPIMutex());
+ m_process_sp = exe_ctx_ref_ptr->GetProcessSP();
+ m_thread_sp = exe_ctx_ref_ptr->GetThreadSP();
+ m_frame_sp = exe_ctx_ref_ptr->GetFrameSP();
+ }
+ }
+}
+
+ExecutionContext::ExecutionContext (const ExecutionContextRef &exe_ctx_ref, Mutex::Locker &locker) :
+ m_target_sp (exe_ctx_ref.GetTargetSP()),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (m_target_sp)
+ {
+ locker.Lock(m_target_sp->GetAPIMutex());
+ m_process_sp = exe_ctx_ref.GetProcessSP();
+ m_thread_sp = exe_ctx_ref.GetThreadSP();
+ m_frame_sp = exe_ctx_ref.GetFrameSP();
+ }
+}
+
+ExecutionContext::ExecutionContext (ExecutionContextScope *exe_scope_ptr) :
+ m_target_sp (),
+ m_process_sp (),
+ m_thread_sp (),
+ m_frame_sp ()
+{
+ if (exe_scope_ptr)
+ exe_scope_ptr->CalculateExecutionContext (*this);
+}
+
+ExecutionContext::ExecutionContext (ExecutionContextScope &exe_scope_ref)
+{
+ exe_scope_ref.CalculateExecutionContext (*this);
+}
+
+void
+ExecutionContext::Clear()
+{
+ m_target_sp.reset();
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+ExecutionContext::~ExecutionContext()
+{
+}
+
+uint32_t
+ExecutionContext::GetAddressByteSize() const
+{
+ if (m_target_sp && m_target_sp->GetArchitecture().IsValid())
+ m_target_sp->GetArchitecture().GetAddressByteSize();
+ if (m_process_sp)
+ m_process_sp->GetAddressByteSize();
+ return sizeof(void *);
+}
+
+
+
+RegisterContext *
+ExecutionContext::GetRegisterContext () const
+{
+ if (m_frame_sp)
+ return m_frame_sp->GetRegisterContext().get();
+ else if (m_thread_sp)
+ return m_thread_sp->GetRegisterContext().get();
+ return NULL;
+}
+
+Target *
+ExecutionContext::GetTargetPtr () const
+{
+ if (m_target_sp)
+ return m_target_sp.get();
+ if (m_process_sp)
+ return &m_process_sp->GetTarget();
+ return NULL;
+}
+
+Process *
+ExecutionContext::GetProcessPtr () const
+{
+ if (m_process_sp)
+ return m_process_sp.get();
+ if (m_target_sp)
+ return m_target_sp->GetProcessSP().get();
+ return NULL;
+}
+
+ExecutionContextScope *
+ExecutionContext::GetBestExecutionContextScope () const
+{
+ if (m_frame_sp)
+ return m_frame_sp.get();
+ if (m_thread_sp)
+ return m_thread_sp.get();
+ if (m_process_sp)
+ return m_process_sp.get();
+ return m_target_sp.get();
+}
+
+Target &
+ExecutionContext::GetTargetRef () const
+{
+#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
+ assert (m_target_sp.get());
+#endif
+ return *m_target_sp;
+}
+
+Process &
+ExecutionContext::GetProcessRef () const
+{
+#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
+ assert (m_process_sp.get());
+#endif
+ return *m_process_sp;
+}
+
+Thread &
+ExecutionContext::GetThreadRef () const
+{
+#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
+ assert (m_thread_sp.get());
+#endif
+ return *m_thread_sp;
+}
+
+StackFrame &
+ExecutionContext::GetFrameRef () const
+{
+#if defined (LLDB_CONFIGURATION_DEBUG) || defined (LLDB_CONFIGURATION_RELEASE)
+ assert (m_frame_sp.get());
+#endif
+ return *m_frame_sp;
+}
+
+void
+ExecutionContext::SetTargetSP (const lldb::TargetSP &target_sp)
+{
+ m_target_sp = target_sp;
+}
+
+void
+ExecutionContext::SetProcessSP (const lldb::ProcessSP &process_sp)
+{
+ m_process_sp = process_sp;
+}
+
+void
+ExecutionContext::SetThreadSP (const lldb::ThreadSP &thread_sp)
+{
+ m_thread_sp = thread_sp;
+}
+
+void
+ExecutionContext::SetFrameSP (const lldb::StackFrameSP &frame_sp)
+{
+ m_frame_sp = frame_sp;
+}
+
+void
+ExecutionContext::SetTargetPtr (Target* target)
+{
+ if (target)
+ m_target_sp = target->shared_from_this();
+ else
+ m_target_sp.reset();
+}
+
+void
+ExecutionContext::SetProcessPtr (Process *process)
+{
+ if (process)
+ m_process_sp = process->shared_from_this();
+ else
+ m_process_sp.reset();
+}
+
+void
+ExecutionContext::SetThreadPtr (Thread *thread)
+{
+ if (thread)
+ m_thread_sp = thread->shared_from_this();
+ else
+ m_thread_sp.reset();
+}
+
+void
+ExecutionContext::SetFramePtr (StackFrame *frame)
+{
+ if (frame)
+ m_frame_sp = frame->shared_from_this();
+ else
+ m_frame_sp.reset();
+}
+
+void
+ExecutionContext::SetContext (const lldb::TargetSP &target_sp, bool get_process)
+{
+ m_target_sp = target_sp;
+ if (get_process && target_sp)
+ m_process_sp = target_sp->GetProcessSP();
+ else
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+void
+ExecutionContext::SetContext (const lldb::ProcessSP &process_sp)
+{
+ m_process_sp = process_sp;
+ if (process_sp)
+ m_target_sp = process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ m_thread_sp.reset();
+ m_frame_sp.reset();
+}
+
+void
+ExecutionContext::SetContext (const lldb::ThreadSP &thread_sp)
+{
+ m_frame_sp.reset();
+ m_thread_sp = thread_sp;
+ if (thread_sp)
+ {
+ m_process_sp = thread_sp->GetProcess();
+ if (m_process_sp)
+ m_target_sp = m_process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ }
+ else
+ {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ }
+}
+
+void
+ExecutionContext::SetContext (const lldb::StackFrameSP &frame_sp)
+{
+ m_frame_sp = frame_sp;
+ if (frame_sp)
+ {
+ m_thread_sp = frame_sp->CalculateThread();
+ if (m_thread_sp)
+ {
+ m_process_sp = m_thread_sp->GetProcess();
+ if (m_process_sp)
+ m_target_sp = m_process_sp->GetTarget().shared_from_this();
+ else
+ m_target_sp.reset();
+ }
+ else
+ {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ }
+ }
+ else
+ {
+ m_target_sp.reset();
+ m_process_sp.reset();
+ m_thread_sp.reset();
+ }
+}
+
+ExecutionContext &
+ExecutionContext::operator =(const ExecutionContext &rhs)
+{
+ if (this != &rhs)
+ {
+ m_target_sp = rhs.m_target_sp;
+ m_process_sp = rhs.m_process_sp;
+ m_thread_sp = rhs.m_thread_sp;
+ m_frame_sp = rhs.m_frame_sp;
+ }
+ return *this;
+}
+
+bool
+ExecutionContext::operator ==(const ExecutionContext &rhs) const
+{
+ // Check that the frame shared pointers match, or both are valid and their stack
+ // IDs match since sometimes we get new objects that represent the same
+ // frame within a thread.
+ if ((m_frame_sp == rhs.m_frame_sp) || (m_frame_sp && rhs.m_frame_sp && m_frame_sp->GetStackID() == rhs.m_frame_sp->GetStackID()))
+ {
+ // Check that the thread shared pointers match, or both are valid and
+ // their thread IDs match since sometimes we get new objects that
+ // represent the same thread within a process.
+ if ((m_thread_sp == rhs.m_thread_sp) || (m_thread_sp && rhs.m_thread_sp && m_thread_sp->GetID() == rhs.m_thread_sp->GetID()))
+ {
+ // Processes and targets don't change much
+ return m_process_sp == rhs.m_process_sp && m_target_sp == rhs.m_target_sp;
+ }
+ }
+ return false;
+}
+
+bool
+ExecutionContext::operator !=(const ExecutionContext &rhs) const
+{
+ return !(*this == rhs);
+}
+
+bool
+ExecutionContext::HasTargetScope () const
+{
+ return ((bool) m_target_sp
+ && m_target_sp->IsValid());
+}
+
+bool
+ExecutionContext::HasProcessScope () const
+{
+ return (HasTargetScope()
+ && ((bool) m_process_sp && m_process_sp->IsValid()));
+}
+
+bool
+ExecutionContext::HasThreadScope () const
+{
+ return (HasProcessScope()
+ && ((bool) m_thread_sp && m_thread_sp->IsValid()));
+}
+
+bool
+ExecutionContext::HasFrameScope () const
+{
+ return HasThreadScope() && m_frame_sp;
+}
+
+ExecutionContextRef::ExecutionContextRef() :
+ m_target_wp (),
+ m_process_wp (),
+ m_thread_wp (),
+ m_tid(LLDB_INVALID_THREAD_ID),
+ m_stack_id ()
+{
+}
+
+ExecutionContextRef::ExecutionContextRef (const ExecutionContext *exe_ctx) :
+ m_target_wp (),
+ m_process_wp (),
+ m_thread_wp (),
+ m_tid(LLDB_INVALID_THREAD_ID),
+ m_stack_id ()
+{
+ if (exe_ctx)
+ *this = *exe_ctx;
+}
+
+ExecutionContextRef::ExecutionContextRef (const ExecutionContext &exe_ctx) :
+ m_target_wp (),
+ m_process_wp (),
+ m_thread_wp (),
+ m_tid(LLDB_INVALID_THREAD_ID),
+ m_stack_id ()
+{
+ *this = exe_ctx;
+}
+
+
+ExecutionContextRef::ExecutionContextRef (Target *target, bool adopt_selected) :
+ m_target_wp(),
+ m_process_wp(),
+ m_thread_wp(),
+ m_tid(LLDB_INVALID_THREAD_ID),
+ m_stack_id ()
+{
+ SetTargetPtr (target, adopt_selected);
+}
+
+
+
+
+ExecutionContextRef::ExecutionContextRef (const ExecutionContextRef &rhs) :
+ m_target_wp (rhs.m_target_wp),
+ m_process_wp(rhs.m_process_wp),
+ m_thread_wp (rhs.m_thread_wp),
+ m_tid (rhs.m_tid),
+ m_stack_id (rhs.m_stack_id)
+{
+}
+
+ExecutionContextRef &
+ExecutionContextRef::operator =(const ExecutionContextRef &rhs)
+{
+ if (this != &rhs)
+ {
+ m_target_wp = rhs.m_target_wp;
+ m_process_wp = rhs.m_process_wp;
+ m_thread_wp = rhs.m_thread_wp;
+ m_tid = rhs.m_tid;
+ m_stack_id = rhs.m_stack_id;
+ }
+ return *this;
+}
+
+ExecutionContextRef &
+ExecutionContextRef::operator =(const ExecutionContext &exe_ctx)
+{
+ m_target_wp = exe_ctx.GetTargetSP();
+ m_process_wp = exe_ctx.GetProcessSP();
+ lldb::ThreadSP thread_sp (exe_ctx.GetThreadSP());
+ m_thread_wp = thread_sp;
+ if (thread_sp)
+ m_tid = thread_sp->GetID();
+ else
+ m_tid = LLDB_INVALID_THREAD_ID;
+ lldb::StackFrameSP frame_sp (exe_ctx.GetFrameSP());
+ if (frame_sp)
+ m_stack_id = frame_sp->GetStackID();
+ else
+ m_stack_id.Clear();
+ return *this;
+}
+
+void
+ExecutionContextRef::Clear()
+{
+ m_target_wp.reset();
+ m_process_wp.reset();
+ ClearThread();
+ ClearFrame();
+}
+
+ExecutionContextRef::~ExecutionContextRef()
+{
+}
+
+void
+ExecutionContextRef::SetTargetSP (const lldb::TargetSP &target_sp)
+{
+ m_target_wp = target_sp;
+}
+
+void
+ExecutionContextRef::SetProcessSP (const lldb::ProcessSP &process_sp)
+{
+ if (process_sp)
+ {
+ m_process_wp = process_sp;
+ SetTargetSP (process_sp->GetTarget().shared_from_this());
+ }
+ else
+ {
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void
+ExecutionContextRef::SetThreadSP (const lldb::ThreadSP &thread_sp)
+{
+ if (thread_sp)
+ {
+ m_thread_wp = thread_sp;
+ m_tid = thread_sp->GetID();
+ SetProcessSP (thread_sp->GetProcess());
+ }
+ else
+ {
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void
+ExecutionContextRef::SetFrameSP (const lldb::StackFrameSP &frame_sp)
+{
+ if (frame_sp)
+ {
+ m_stack_id = frame_sp->GetStackID();
+ SetThreadSP (frame_sp->GetThread());
+ }
+ else
+ {
+ ClearFrame();
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+
+}
+
+void
+ExecutionContextRef::SetTargetPtr (Target* target, bool adopt_selected)
+{
+ Clear();
+ if (target)
+ {
+ lldb::TargetSP target_sp (target->shared_from_this());
+ if (target_sp)
+ {
+ m_target_wp = target_sp;
+ if (adopt_selected)
+ {
+ lldb::ProcessSP process_sp (target_sp->GetProcessSP());
+ if (process_sp)
+ {
+ m_process_wp = process_sp;
+ if (process_sp)
+ {
+ // Only fill in the thread and frame if our process is stopped
+ if (StateIsStoppedState (process_sp->GetState(), true))
+ {
+ lldb::ThreadSP thread_sp (process_sp->GetThreadList().GetSelectedThread());
+ if (!thread_sp)
+ thread_sp = process_sp->GetThreadList().GetThreadAtIndex(0);
+
+ if (thread_sp)
+ {
+ SetThreadSP (thread_sp);
+ lldb::StackFrameSP frame_sp (thread_sp->GetSelectedFrame());
+ if (!frame_sp)
+ frame_sp = thread_sp->GetStackFrameAtIndex(0);
+ if (frame_sp)
+ SetFrameSP (frame_sp);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+ExecutionContextRef::SetProcessPtr (Process *process)
+{
+ if (process)
+ {
+ SetProcessSP(process->shared_from_this());
+ }
+ else
+ {
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void
+ExecutionContextRef::SetThreadPtr (Thread *thread)
+{
+ if (thread)
+ {
+ SetThreadSP (thread->shared_from_this());
+ }
+ else
+ {
+ ClearThread();
+ m_process_wp.reset();
+ m_target_wp.reset();
+ }
+}
+
+void
+ExecutionContextRef::SetFramePtr (StackFrame *frame)
+{
+ if (frame)
+ SetFrameSP (frame->shared_from_this());
+ else
+ Clear();
+}
+
+lldb::TargetSP
+ExecutionContextRef::GetTargetSP () const
+{
+ lldb::TargetSP target_sp(m_target_wp.lock());
+ if (target_sp && !target_sp->IsValid())
+ target_sp.reset();
+ return target_sp;
+}
+
+lldb::ProcessSP
+ExecutionContextRef::GetProcessSP () const
+{
+ lldb::ProcessSP process_sp(m_process_wp.lock());
+ if (process_sp && !process_sp->IsValid())
+ process_sp.reset();
+ return process_sp;
+}
+
+lldb::ThreadSP
+ExecutionContextRef::GetThreadSP () const
+{
+ lldb::ThreadSP thread_sp (m_thread_wp.lock());
+
+ if (m_tid != LLDB_INVALID_THREAD_ID)
+ {
+ // We check if the thread has been destroyed in cases where clients
+ // might still have shared pointer to a thread, but the thread is
+ // not valid anymore (not part of the process)
+ if (!thread_sp || !thread_sp->IsValid())
+ {
+ lldb::ProcessSP process_sp(GetProcessSP());
+ if (process_sp && process_sp->IsValid())
+ {
+ thread_sp = process_sp->GetThreadList().FindThreadByID(m_tid);
+ m_thread_wp = thread_sp;
+ }
+ }
+ }
+
+ // Check that we aren't about to return an invalid thread sp. We might return a NULL thread_sp,
+ // but don't return an invalid one.
+
+ if (thread_sp && !thread_sp->IsValid())
+ thread_sp.reset();
+
+ return thread_sp;
+}
+
+lldb::StackFrameSP
+ExecutionContextRef::GetFrameSP () const
+{
+ if (m_stack_id.IsValid())
+ {
+ lldb::ThreadSP thread_sp (GetThreadSP());
+ if (thread_sp)
+ return thread_sp->GetFrameWithStackID (m_stack_id);
+ }
+ return lldb::StackFrameSP();
+}
+
+ExecutionContext
+ExecutionContextRef::Lock () const
+{
+ return ExecutionContext(this);
+}
+
+
diff --git a/source/Target/LanguageRuntime.cpp b/source/Target/LanguageRuntime.cpp
new file mode 100644
index 000000000000..d2f586ac82fb
--- /dev/null
+++ b/source/Target/LanguageRuntime.cpp
@@ -0,0 +1,343 @@
+//===-- LanguageRuntime.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/Target/LanguageRuntime.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Core/PluginManager.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+class ExceptionSearchFilter : public SearchFilter
+{
+public:
+ ExceptionSearchFilter (const lldb::TargetSP &target_sp,
+ lldb::LanguageType language) :
+ SearchFilter (target_sp),
+ m_language (language),
+ m_language_runtime (NULL),
+ m_filter_sp ()
+ {
+ UpdateModuleListIfNeeded ();
+ }
+
+ virtual bool
+ ModulePasses (const lldb::ModuleSP &module_sp)
+ {
+ UpdateModuleListIfNeeded ();
+ if (m_filter_sp)
+ return m_filter_sp->ModulePasses (module_sp);
+ return false;
+ }
+
+ virtual bool
+ ModulePasses (const FileSpec &spec)
+ {
+ UpdateModuleListIfNeeded ();
+ if (m_filter_sp)
+ return m_filter_sp->ModulePasses (spec);
+ return false;
+
+ }
+
+ virtual void
+ Search (Searcher &searcher)
+ {
+ UpdateModuleListIfNeeded ();
+ if (m_filter_sp)
+ m_filter_sp->Search (searcher);
+ }
+
+ virtual void
+ GetDescription (Stream *s)
+ {
+ UpdateModuleListIfNeeded ();
+ if (m_filter_sp)
+ m_filter_sp->GetDescription (s);
+ }
+
+protected:
+ LanguageType m_language;
+ LanguageRuntime *m_language_runtime;
+ SearchFilterSP m_filter_sp;
+
+ void
+ UpdateModuleListIfNeeded ()
+ {
+ ProcessSP process_sp (m_target_sp->GetProcessSP());
+ if (process_sp)
+ {
+ bool refreash_filter = !m_filter_sp;
+ if (m_language_runtime == NULL)
+ {
+ m_language_runtime = process_sp->GetLanguageRuntime(m_language);
+ refreash_filter = true;
+ }
+ else
+ {
+ LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(m_language);
+ if (m_language_runtime != language_runtime)
+ {
+ m_language_runtime = language_runtime;
+ refreash_filter = true;
+ }
+ }
+
+ if (refreash_filter && m_language_runtime)
+ {
+ m_filter_sp = m_language_runtime->CreateExceptionSearchFilter ();
+ }
+ }
+ else
+ {
+ m_filter_sp.reset();
+ m_language_runtime = NULL;
+ }
+ }
+};
+
+// The Target is the one that knows how to create breakpoints, so this function
+// is meant to be used either by the target or internally in Set/ClearExceptionBreakpoints.
+class ExceptionBreakpointResolver : public BreakpointResolver
+{
+public:
+ ExceptionBreakpointResolver (lldb::LanguageType language,
+ bool catch_bp,
+ bool throw_bp) :
+ BreakpointResolver (NULL, BreakpointResolver::ExceptionResolver),
+ m_language (language),
+ m_language_runtime (NULL),
+ m_catch_bp (catch_bp),
+ m_throw_bp (throw_bp)
+ {
+ }
+
+ virtual
+ ~ExceptionBreakpointResolver()
+ {
+ }
+
+ virtual Searcher::CallbackReturn
+ SearchCallback (SearchFilter &filter,
+ SymbolContext &context,
+ Address *addr,
+ bool containing)
+ {
+
+ if (SetActualResolver())
+ return m_actual_resolver_sp->SearchCallback (filter, context, addr, containing);
+ else
+ return eCallbackReturnStop;
+ }
+
+ virtual Searcher::Depth
+ GetDepth ()
+ {
+ if (SetActualResolver())
+ return m_actual_resolver_sp->GetDepth();
+ else
+ return eDepthTarget;
+ }
+
+ virtual void
+ GetDescription (Stream *s)
+ {
+ s->Printf ("Exception breakpoint (catch: %s throw: %s)",
+ m_catch_bp ? "on" : "off",
+ m_throw_bp ? "on" : "off");
+
+ SetActualResolver();
+ if (m_actual_resolver_sp)
+ {
+ s->Printf (" using: ");
+ m_actual_resolver_sp->GetDescription (s);
+ }
+ else
+ s->Printf (" the correct runtime exception handler will be determined when you run");
+ }
+
+ virtual void
+ Dump (Stream *s) const
+ {
+ }
+
+ /// Methods for support type inquiry through isa, cast, and dyn_cast:
+ static inline bool classof(const BreakpointResolverName *) { return true; }
+ static inline bool classof(const BreakpointResolver *V) {
+ return V->getResolverID() == BreakpointResolver::ExceptionResolver;
+ }
+protected:
+ bool
+ SetActualResolver()
+ {
+ ProcessSP process_sp;
+ if (m_breakpoint)
+ {
+ process_sp = m_breakpoint->GetTarget().GetProcessSP();
+ if (process_sp)
+ {
+ bool refreash_resolver = !m_actual_resolver_sp;
+ if (m_language_runtime == NULL)
+ {
+ m_language_runtime = process_sp->GetLanguageRuntime(m_language);
+ refreash_resolver = true;
+ }
+ else
+ {
+ LanguageRuntime *language_runtime = process_sp->GetLanguageRuntime(m_language);
+ if (m_language_runtime != language_runtime)
+ {
+ m_language_runtime = language_runtime;
+ refreash_resolver = true;
+ }
+ }
+
+ if (refreash_resolver && m_language_runtime)
+ {
+ m_actual_resolver_sp = m_language_runtime->CreateExceptionResolver (m_breakpoint, m_catch_bp, m_throw_bp);
+ }
+ }
+ else
+ {
+ m_actual_resolver_sp.reset();
+ m_language_runtime = NULL;
+ }
+ }
+ else
+ {
+ m_actual_resolver_sp.reset();
+ m_language_runtime = NULL;
+ }
+ return (bool)m_actual_resolver_sp;
+ }
+ lldb::BreakpointResolverSP m_actual_resolver_sp;
+ lldb::LanguageType m_language;
+ LanguageRuntime *m_language_runtime;
+ bool m_catch_bp;
+ bool m_throw_bp;
+};
+
+
+LanguageRuntime*
+LanguageRuntime::FindPlugin (Process *process, lldb::LanguageType language)
+{
+ std::unique_ptr<LanguageRuntime> language_runtime_ap;
+ LanguageRuntimeCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetLanguageRuntimeCreateCallbackAtIndex(idx)) != NULL;
+ ++idx)
+ {
+ language_runtime_ap.reset (create_callback(process, language));
+
+ if (language_runtime_ap.get())
+ return language_runtime_ap.release();
+ }
+
+ return NULL;
+}
+
+//----------------------------------------------------------------------
+// Constructor
+//----------------------------------------------------------------------
+LanguageRuntime::LanguageRuntime(Process *process) :
+ m_process (process)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+LanguageRuntime::~LanguageRuntime()
+{
+}
+
+BreakpointSP
+LanguageRuntime::CreateExceptionBreakpoint (Target &target,
+ lldb::LanguageType language,
+ bool catch_bp,
+ bool throw_bp,
+ bool is_internal)
+{
+ BreakpointResolverSP resolver_sp(new ExceptionBreakpointResolver(language, catch_bp, throw_bp));
+ SearchFilterSP filter_sp(new ExceptionSearchFilter(target.shared_from_this(), language));
+
+ BreakpointSP exc_breakpt_sp (target.CreateBreakpoint (filter_sp, resolver_sp, is_internal));
+ if (is_internal)
+ exc_breakpt_sp->SetBreakpointKind("exception");
+
+ return exc_breakpt_sp;
+}
+
+struct language_name_pair {
+ const char *name;
+ LanguageType type;
+};
+
+struct language_name_pair language_names[] =
+{
+ // To allow GetNameForLanguageType to be a simple array lookup, the first
+ // part of this array must follow enum LanguageType exactly.
+ { "unknown", eLanguageTypeUnknown },
+ { "c89", eLanguageTypeC89 },
+ { "c", eLanguageTypeC },
+ { "ada83", eLanguageTypeAda83 },
+ { "c++", eLanguageTypeC_plus_plus },
+ { "cobol74", eLanguageTypeCobol74 },
+ { "cobol85", eLanguageTypeCobol85 },
+ { "fortran77", eLanguageTypeFortran77 },
+ { "fortran90", eLanguageTypeFortran90 },
+ { "pascal83", eLanguageTypePascal83 },
+ { "modula2", eLanguageTypeModula2 },
+ { "java", eLanguageTypeJava },
+ { "c99", eLanguageTypeC99 },
+ { "ada95", eLanguageTypeAda95 },
+ { "fortran95", eLanguageTypeFortran95 },
+ { "pli", eLanguageTypePLI },
+ { "objective-c", eLanguageTypeObjC },
+ { "objective-c++", eLanguageTypeObjC_plus_plus },
+ { "upc", eLanguageTypeUPC },
+ { "d", eLanguageTypeD },
+ { "python", eLanguageTypePython },
+ // Now synonyms, in arbitrary order
+ { "objc", eLanguageTypeObjC },
+ { "objc++", eLanguageTypeObjC_plus_plus }
+};
+
+static uint32_t num_languages = sizeof(language_names) / sizeof (struct language_name_pair);
+
+LanguageType
+LanguageRuntime::GetLanguageTypeFromString (const char *string)
+{
+ for (uint32_t i = 0; i < num_languages; i++)
+ {
+ if (strcasecmp (language_names[i].name, string) == 0)
+ return (LanguageType) language_names[i].type;
+ }
+ return eLanguageTypeUnknown;
+}
+
+const char *
+LanguageRuntime::GetNameForLanguageType (LanguageType language)
+{
+ if (language < num_languages)
+ return language_names[language].name;
+ else
+ return language_names[eLanguageTypeUnknown].name;
+}
+
+lldb::SearchFilterSP
+LanguageRuntime::CreateExceptionSearchFilter ()
+{
+ return m_process->GetTarget().GetSearchFilterForModule(NULL);
+}
+
+
+
diff --git a/source/Target/Memory.cpp b/source/Target/Memory.cpp
new file mode 100644
index 000000000000..3c8d483f3003
--- /dev/null
+++ b/source/Target/Memory.cpp
@@ -0,0 +1,461 @@
+//===-- Memory.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/Target/Memory.h"
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// MemoryCache constructor
+//----------------------------------------------------------------------
+MemoryCache::MemoryCache(Process &process) :
+ m_process (process),
+ m_cache_line_byte_size (512),
+ m_mutex (Mutex::eMutexTypeRecursive),
+ m_cache (),
+ m_invalid_ranges ()
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+MemoryCache::~MemoryCache()
+{
+}
+
+void
+MemoryCache::Clear(bool clear_invalid_ranges)
+{
+ Mutex::Locker locker (m_mutex);
+ m_cache.clear();
+ if (clear_invalid_ranges)
+ m_invalid_ranges.Clear();
+}
+
+void
+MemoryCache::Flush (addr_t addr, size_t size)
+{
+ if (size == 0)
+ return;
+
+ Mutex::Locker locker (m_mutex);
+ if (m_cache.empty())
+ return;
+
+ const uint32_t cache_line_byte_size = m_cache_line_byte_size;
+ const addr_t end_addr = (addr + size - 1);
+ const addr_t first_cache_line_addr = addr - (addr % cache_line_byte_size);
+ const addr_t last_cache_line_addr = end_addr - (end_addr % cache_line_byte_size);
+ // Watch for overflow where size will cause us to go off the end of the
+ // 64 bit address space
+ uint32_t num_cache_lines;
+ if (last_cache_line_addr >= first_cache_line_addr)
+ num_cache_lines = ((last_cache_line_addr - first_cache_line_addr)/cache_line_byte_size) + 1;
+ else
+ num_cache_lines = (UINT64_MAX - first_cache_line_addr + 1)/cache_line_byte_size;
+
+ uint32_t cache_idx = 0;
+ for (addr_t curr_addr = first_cache_line_addr;
+ cache_idx < num_cache_lines;
+ curr_addr += cache_line_byte_size, ++cache_idx)
+ {
+ BlockMap::iterator pos = m_cache.find (curr_addr);
+ if (pos != m_cache.end())
+ m_cache.erase(pos);
+ }
+}
+
+void
+MemoryCache::AddInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size)
+{
+ if (byte_size > 0)
+ {
+ Mutex::Locker locker (m_mutex);
+ InvalidRanges::Entry range (base_addr, byte_size);
+ m_invalid_ranges.Append(range);
+ m_invalid_ranges.Sort();
+ }
+}
+
+bool
+MemoryCache::RemoveInvalidRange (lldb::addr_t base_addr, lldb::addr_t byte_size)
+{
+ if (byte_size > 0)
+ {
+ Mutex::Locker locker (m_mutex);
+ const uint32_t idx = m_invalid_ranges.FindEntryIndexThatContains(base_addr);
+ if (idx != UINT32_MAX)
+ {
+ const InvalidRanges::Entry *entry = m_invalid_ranges.GetEntryAtIndex (idx);
+ if (entry->GetRangeBase() == base_addr && entry->GetByteSize() == byte_size)
+ return m_invalid_ranges.RemoveEntrtAtIndex (idx);
+ }
+ }
+ return false;
+}
+
+
+
+size_t
+MemoryCache::Read (addr_t addr,
+ void *dst,
+ size_t dst_len,
+ Error &error)
+{
+ size_t bytes_left = dst_len;
+ if (dst && bytes_left > 0)
+ {
+ const uint32_t cache_line_byte_size = m_cache_line_byte_size;
+ uint8_t *dst_buf = (uint8_t *)dst;
+ addr_t curr_addr = addr - (addr % cache_line_byte_size);
+ addr_t cache_offset = addr - curr_addr;
+ Mutex::Locker locker (m_mutex);
+
+ while (bytes_left > 0)
+ {
+ if (m_invalid_ranges.FindEntryThatContains(curr_addr))
+ {
+ error.SetErrorStringWithFormat("memory read failed for 0x%" PRIx64, curr_addr);
+ return dst_len - bytes_left;
+ }
+
+ BlockMap::const_iterator pos = m_cache.find (curr_addr);
+ BlockMap::const_iterator end = m_cache.end ();
+
+ if (pos != end)
+ {
+ size_t curr_read_size = cache_line_byte_size - cache_offset;
+ if (curr_read_size > bytes_left)
+ curr_read_size = bytes_left;
+
+ memcpy (dst_buf + dst_len - bytes_left, pos->second->GetBytes() + cache_offset, curr_read_size);
+
+ bytes_left -= curr_read_size;
+ curr_addr += curr_read_size + cache_offset;
+ cache_offset = 0;
+
+ if (bytes_left > 0)
+ {
+ // Get sequential cache page hits
+ for (++pos; (pos != end) && (bytes_left > 0); ++pos)
+ {
+ assert ((curr_addr % cache_line_byte_size) == 0);
+
+ if (pos->first != curr_addr)
+ break;
+
+ curr_read_size = pos->second->GetByteSize();
+ if (curr_read_size > bytes_left)
+ curr_read_size = bytes_left;
+
+ memcpy (dst_buf + dst_len - bytes_left, pos->second->GetBytes(), curr_read_size);
+
+ bytes_left -= curr_read_size;
+ curr_addr += curr_read_size;
+
+ // We have a cache page that succeeded to read some bytes
+ // but not an entire page. If this happens, we must cap
+ // off how much data we are able to read...
+ if (pos->second->GetByteSize() != cache_line_byte_size)
+ return dst_len - bytes_left;
+ }
+ }
+ }
+
+ // We need to read from the process
+
+ if (bytes_left > 0)
+ {
+ assert ((curr_addr % cache_line_byte_size) == 0);
+ std::unique_ptr<DataBufferHeap> data_buffer_heap_ap(new DataBufferHeap (cache_line_byte_size, 0));
+ size_t process_bytes_read = m_process.ReadMemoryFromInferior (curr_addr,
+ data_buffer_heap_ap->GetBytes(),
+ data_buffer_heap_ap->GetByteSize(),
+ error);
+ if (process_bytes_read == 0)
+ return dst_len - bytes_left;
+
+ if (process_bytes_read != cache_line_byte_size)
+ data_buffer_heap_ap->SetByteSize (process_bytes_read);
+ m_cache[curr_addr] = DataBufferSP (data_buffer_heap_ap.release());
+ // We have read data and put it into the cache, continue through the
+ // loop again to get the data out of the cache...
+ }
+ }
+ }
+
+ return dst_len - bytes_left;
+}
+
+
+
+AllocatedBlock::AllocatedBlock (lldb::addr_t addr,
+ uint32_t byte_size,
+ uint32_t permissions,
+ uint32_t chunk_size) :
+ m_addr (addr),
+ m_byte_size (byte_size),
+ m_permissions (permissions),
+ m_chunk_size (chunk_size),
+ m_offset_to_chunk_size ()
+// m_allocated (byte_size / chunk_size)
+{
+ assert (byte_size > chunk_size);
+}
+
+AllocatedBlock::~AllocatedBlock ()
+{
+}
+
+lldb::addr_t
+AllocatedBlock::ReserveBlock (uint32_t size)
+{
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ if (size <= m_byte_size)
+ {
+ const uint32_t needed_chunks = CalculateChunksNeededForSize (size);
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+
+ if (m_offset_to_chunk_size.empty())
+ {
+ m_offset_to_chunk_size[0] = needed_chunks;
+ if (log)
+ log->Printf ("[1] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, 0, needed_chunks, m_chunk_size);
+ addr = m_addr;
+ }
+ else
+ {
+ uint32_t last_offset = 0;
+ OffsetToChunkSize::const_iterator pos = m_offset_to_chunk_size.begin();
+ OffsetToChunkSize::const_iterator end = m_offset_to_chunk_size.end();
+ while (pos != end)
+ {
+ if (pos->first > last_offset)
+ {
+ const uint32_t bytes_available = pos->first - last_offset;
+ const uint32_t num_chunks = CalculateChunksNeededForSize (bytes_available);
+ if (num_chunks >= needed_chunks)
+ {
+ m_offset_to_chunk_size[last_offset] = needed_chunks;
+ if (log)
+ log->Printf ("[2] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, last_offset, needed_chunks, m_chunk_size);
+ addr = m_addr + last_offset;
+ break;
+ }
+ }
+
+ last_offset = pos->first + pos->second * m_chunk_size;
+
+ if (++pos == end)
+ {
+ // Last entry...
+ const uint32_t chunks_left = CalculateChunksNeededForSize (m_byte_size - last_offset);
+ if (chunks_left >= needed_chunks)
+ {
+ m_offset_to_chunk_size[last_offset] = needed_chunks;
+ if (log)
+ log->Printf ("[3] AllocatedBlock::ReserveBlock (size = %u (0x%x)) => offset = 0x%x, %u %u bit chunks", size, size, last_offset, needed_chunks, m_chunk_size);
+ addr = m_addr + last_offset;
+ break;
+ }
+ }
+ }
+ }
+// const uint32_t total_chunks = m_allocated.size ();
+// uint32_t unallocated_idx = 0;
+// uint32_t allocated_idx = m_allocated.find_first();
+// uint32_t first_chunk_idx = UINT32_MAX;
+// uint32_t num_chunks;
+// while (1)
+// {
+// if (allocated_idx == UINT32_MAX)
+// {
+// // No more bits are set starting from unallocated_idx, so we
+// // either have enough chunks for the request, or we don't.
+// // Eiter way we break out of the while loop...
+// num_chunks = total_chunks - unallocated_idx;
+// if (needed_chunks <= num_chunks)
+// first_chunk_idx = unallocated_idx;
+// break;
+// }
+// else if (allocated_idx > unallocated_idx)
+// {
+// // We have some allocated chunks, check if there are enough
+// // free chunks to satisfy the request?
+// num_chunks = allocated_idx - unallocated_idx;
+// if (needed_chunks <= num_chunks)
+// {
+// // Yep, we have enough!
+// first_chunk_idx = unallocated_idx;
+// break;
+// }
+// }
+//
+// while (unallocated_idx < total_chunks)
+// {
+// if (m_allocated[unallocated_idx])
+// ++unallocated_idx;
+// else
+// break;
+// }
+//
+// if (unallocated_idx >= total_chunks)
+// break;
+//
+// allocated_idx = m_allocated.find_next(unallocated_idx);
+// }
+//
+// if (first_chunk_idx != UINT32_MAX)
+// {
+// const uint32_t end_bit_idx = unallocated_idx + needed_chunks;
+// for (uint32_t idx = first_chunk_idx; idx < end_bit_idx; ++idx)
+// m_allocated.set(idx);
+// return m_addr + m_chunk_size * first_chunk_idx;
+// }
+ }
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ log->Printf ("AllocatedBlock::ReserveBlock (size = %u (0x%x)) => 0x%16.16" PRIx64, size, size, (uint64_t)addr);
+ return addr;
+}
+
+bool
+AllocatedBlock::FreeBlock (addr_t addr)
+{
+ uint32_t offset = addr - m_addr;
+ OffsetToChunkSize::iterator pos = m_offset_to_chunk_size.find (offset);
+ bool success = false;
+ if (pos != m_offset_to_chunk_size.end())
+ {
+ m_offset_to_chunk_size.erase (pos);
+ success = true;
+ }
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ log->Printf ("AllocatedBlock::FreeBlock (addr = 0x%16.16" PRIx64 ") => %i", (uint64_t)addr, success);
+ return success;
+}
+
+
+AllocatedMemoryCache::AllocatedMemoryCache (Process &process) :
+ m_process (process),
+ m_mutex (Mutex::eMutexTypeRecursive),
+ m_memory_map()
+{
+}
+
+AllocatedMemoryCache::~AllocatedMemoryCache ()
+{
+}
+
+
+void
+AllocatedMemoryCache::Clear()
+{
+ Mutex::Locker locker (m_mutex);
+ if (m_process.IsAlive())
+ {
+ PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
+ for (pos = m_memory_map.begin(); pos != end; ++pos)
+ m_process.DoDeallocateMemory(pos->second->GetBaseAddress());
+ }
+ m_memory_map.clear();
+}
+
+
+AllocatedMemoryCache::AllocatedBlockSP
+AllocatedMemoryCache::AllocatePage (uint32_t byte_size,
+ uint32_t permissions,
+ uint32_t chunk_size,
+ Error &error)
+{
+ AllocatedBlockSP block_sp;
+ const size_t page_size = 4096;
+ const size_t num_pages = (byte_size + page_size - 1) / page_size;
+ const size_t page_byte_size = num_pages * page_size;
+
+ addr_t addr = m_process.DoAllocateMemory(page_byte_size, permissions, error);
+
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ {
+ log->Printf ("Process::DoAllocateMemory (byte_size = 0x%8.8zx, permissions = %s) => 0x%16.16" PRIx64,
+ page_byte_size,
+ GetPermissionsAsCString(permissions),
+ (uint64_t)addr);
+ }
+
+ if (addr != LLDB_INVALID_ADDRESS)
+ {
+ block_sp.reset (new AllocatedBlock (addr, page_byte_size, permissions, chunk_size));
+ m_memory_map.insert (std::make_pair (permissions, block_sp));
+ }
+ return block_sp;
+}
+
+lldb::addr_t
+AllocatedMemoryCache::AllocateMemory (size_t byte_size,
+ uint32_t permissions,
+ Error &error)
+{
+ Mutex::Locker locker (m_mutex);
+
+ addr_t addr = LLDB_INVALID_ADDRESS;
+ std::pair<PermissionsToBlockMap::iterator, PermissionsToBlockMap::iterator> range = m_memory_map.equal_range (permissions);
+
+ for (PermissionsToBlockMap::iterator pos = range.first; pos != range.second; ++pos)
+ {
+ addr = (*pos).second->ReserveBlock (byte_size);
+ }
+
+ if (addr == LLDB_INVALID_ADDRESS)
+ {
+ AllocatedBlockSP block_sp (AllocatePage (byte_size, permissions, 16, error));
+
+ if (block_sp)
+ addr = block_sp->ReserveBlock (byte_size);
+ }
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("AllocatedMemoryCache::AllocateMemory (byte_size = 0x%8.8zx, permissions = %s) => 0x%16.16" PRIx64, byte_size, GetPermissionsAsCString(permissions), (uint64_t)addr);
+ return addr;
+}
+
+bool
+AllocatedMemoryCache::DeallocateMemory (lldb::addr_t addr)
+{
+ Mutex::Locker locker (m_mutex);
+
+ PermissionsToBlockMap::iterator pos, end = m_memory_map.end();
+ bool success = false;
+ for (pos = m_memory_map.begin(); pos != end; ++pos)
+ {
+ if (pos->second->Contains (addr))
+ {
+ success = pos->second->FreeBlock (addr);
+ break;
+ }
+ }
+ Log *log (GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("AllocatedMemoryCache::DeallocateMemory (addr = 0x%16.16" PRIx64 ") => %i", (uint64_t)addr, success);
+ return success;
+}
+
+
diff --git a/source/Target/ObjCLanguageRuntime.cpp b/source/Target/ObjCLanguageRuntime.cpp
new file mode 100644
index 000000000000..64ddfcc6c796
--- /dev/null
+++ b/source/Target/ObjCLanguageRuntime.cpp
@@ -0,0 +1,605 @@
+//===-- ObjCLanguageRuntime.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/AST/Type.h"
+
+#include "lldb/Core/Log.h"
+#include "lldb/Core/MappedHash.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Symbol/ClangASTContext.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
+#include "lldb/Target/Target.h"
+
+#include "llvm/ADT/StringRef.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ObjCLanguageRuntime::~ObjCLanguageRuntime()
+{
+}
+
+ObjCLanguageRuntime::ObjCLanguageRuntime (Process *process) :
+ LanguageRuntime (process),
+ m_has_new_literals_and_indexing (eLazyBoolCalculate),
+ m_isa_to_descriptor(),
+ m_isa_to_descriptor_stop_id (UINT32_MAX)
+{
+
+}
+
+bool
+ObjCLanguageRuntime::AddClass (ObjCISA isa, const ClassDescriptorSP &descriptor_sp, const char *class_name)
+{
+ if (isa != 0)
+ {
+ m_isa_to_descriptor[isa] = descriptor_sp;
+ // class_name is assumed to be valid
+ m_hash_to_isa_map.insert(std::make_pair(MappedHash::HashStringUsingDJB(class_name), isa));
+ return true;
+ }
+ return false;
+}
+
+void
+ObjCLanguageRuntime::AddToMethodCache (lldb::addr_t class_addr, lldb::addr_t selector, lldb::addr_t impl_addr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ log->Printf ("Caching: class 0x%" PRIx64 " selector 0x%" PRIx64 " implementation 0x%" PRIx64 ".", class_addr, selector, impl_addr);
+ }
+ m_impl_cache.insert (std::pair<ClassAndSel,lldb::addr_t> (ClassAndSel(class_addr, selector), impl_addr));
+}
+
+lldb::addr_t
+ObjCLanguageRuntime::LookupInMethodCache (lldb::addr_t class_addr, lldb::addr_t selector)
+{
+ MsgImplMap::iterator pos, end = m_impl_cache.end();
+ pos = m_impl_cache.find (ClassAndSel(class_addr, selector));
+ if (pos != end)
+ return (*pos).second;
+ return LLDB_INVALID_ADDRESS;
+}
+
+
+lldb::TypeSP
+ObjCLanguageRuntime::LookupInCompleteClassCache (ConstString &name)
+{
+ CompleteClassMap::iterator complete_class_iter = m_complete_class_cache.find(name);
+
+ if (complete_class_iter != m_complete_class_cache.end())
+ {
+ // Check the weak pointer to make sure the type hasn't been unloaded
+ TypeSP complete_type_sp (complete_class_iter->second.lock());
+
+ if (complete_type_sp)
+ return complete_type_sp;
+ else
+ m_complete_class_cache.erase(name);
+ }
+
+ if (m_negative_complete_class_cache.count(name) > 0)
+ return TypeSP();
+
+ const ModuleList &modules = m_process->GetTarget().GetImages();
+
+ SymbolContextList sc_list;
+ const size_t matching_symbols = modules.FindSymbolsWithNameAndType (name,
+ eSymbolTypeObjCClass,
+ sc_list);
+
+ if (matching_symbols)
+ {
+ SymbolContext sc;
+
+ sc_list.GetContextAtIndex(0, sc);
+
+ ModuleSP module_sp(sc.module_sp);
+
+ if (!module_sp)
+ return TypeSP();
+
+ const SymbolContext null_sc;
+ const bool exact_match = true;
+ const uint32_t max_matches = UINT32_MAX;
+ TypeList types;
+
+ const uint32_t num_types = module_sp->FindTypes (null_sc,
+ name,
+ exact_match,
+ max_matches,
+ types);
+
+ if (num_types)
+ {
+ uint32_t i;
+ for (i = 0; i < num_types; ++i)
+ {
+ TypeSP type_sp (types.GetTypeAtIndex(i));
+
+ if (type_sp->GetClangForwardType().IsObjCObjectOrInterfaceType())
+ {
+ if (type_sp->IsCompleteObjCClass())
+ {
+ m_complete_class_cache[name] = type_sp;
+ return type_sp;
+ }
+ }
+ }
+ }
+ }
+ m_negative_complete_class_cache.insert(name);
+ return TypeSP();
+}
+
+size_t
+ObjCLanguageRuntime::GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name)
+{
+ return LLDB_INVALID_IVAR_OFFSET;
+}
+
+void
+ObjCLanguageRuntime::MethodName::Clear()
+{
+ m_full.Clear();
+ m_class.Clear();
+ m_category.Clear();
+ m_selector.Clear();
+ m_type = eTypeUnspecified;
+ m_category_is_valid = false;
+}
+
+//bool
+//ObjCLanguageRuntime::MethodName::SetName (const char *name, bool strict)
+//{
+// Clear();
+// if (name && name[0])
+// {
+// // If "strict" is true. then the method must be specified with a
+// // '+' or '-' at the beginning. If "strict" is false, then the '+'
+// // or '-' can be omitted
+// bool valid_prefix = false;
+//
+// if (name[0] == '+' || name[0] == '-')
+// {
+// valid_prefix = name[1] == '[';
+// }
+// else if (!strict)
+// {
+// // "strict" is false, the name just needs to start with '['
+// valid_prefix = name[0] == '[';
+// }
+//
+// if (valid_prefix)
+// {
+// static RegularExpression g_regex("^([-+]?)\\[([A-Za-z_][A-Za-z_0-9]*)(\\([A-Za-z_][A-Za-z_0-9]*\\))? ([A-Za-z_][A-Za-z_0-9:]*)\\]$");
+// llvm::StringRef matches[4];
+// // Since we are using a global regular expression, we must use the threadsafe version of execute
+// if (g_regex.ExecuteThreadSafe(name, matches, 4))
+// {
+// m_full.SetCString(name);
+// if (matches[0].empty())
+// m_type = eTypeUnspecified;
+// else if (matches[0][0] == '+')
+// m_type = eTypeClassMethod;
+// else
+// m_type = eTypeInstanceMethod;
+// m_class.SetString(matches[1]);
+// m_selector.SetString(matches[3]);
+// if (!matches[2].empty())
+// m_category.SetString(matches[2]);
+// }
+// }
+// }
+// return IsValid(strict);
+//}
+
+bool
+ObjCLanguageRuntime::MethodName::SetName (const char *name, bool strict)
+{
+ Clear();
+ if (name && name[0])
+ {
+ // If "strict" is true. then the method must be specified with a
+ // '+' or '-' at the beginning. If "strict" is false, then the '+'
+ // or '-' can be omitted
+ bool valid_prefix = false;
+
+ if (name[0] == '+' || name[0] == '-')
+ {
+ valid_prefix = name[1] == '[';
+ if (name[0] == '+')
+ m_type = eTypeClassMethod;
+ else
+ m_type = eTypeInstanceMethod;
+ }
+ else if (!strict)
+ {
+ // "strict" is false, the name just needs to start with '['
+ valid_prefix = name[0] == '[';
+ }
+
+ if (valid_prefix)
+ {
+ int name_len = strlen (name);
+ // Objective C methods must have at least:
+ // "-[" or "+[" prefix
+ // One character for a class name
+ // One character for the space between the class name
+ // One character for the method name
+ // "]" suffix
+ if (name_len >= (5 + (strict ? 1 : 0)) && name[name_len - 1] == ']')
+ {
+ m_full.SetCStringWithLength(name, name_len);
+ }
+ }
+ }
+ return IsValid(strict);
+}
+
+const ConstString &
+ObjCLanguageRuntime::MethodName::GetClassName ()
+{
+ if (!m_class)
+ {
+ if (IsValid(false))
+ {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *paren_pos = strchr (class_start, '(');
+ if (paren_pos)
+ {
+ m_class.SetCStringWithLength (class_start, paren_pos - class_start);
+ }
+ else
+ {
+ // No '(' was found in the full name, we can definitively say
+ // that our category was valid (and empty).
+ m_category_is_valid = true;
+ const char *space_pos = strchr (full, ' ');
+ if (space_pos)
+ {
+ m_class.SetCStringWithLength (class_start, space_pos - class_start);
+ if (!m_class_category)
+ {
+ // No category in name, so we can also fill in the m_class_category
+ m_class_category = m_class;
+ }
+ }
+ }
+ }
+ }
+ return m_class;
+}
+
+const ConstString &
+ObjCLanguageRuntime::MethodName::GetClassNameWithCategory ()
+{
+ if (!m_class_category)
+ {
+ if (IsValid(false))
+ {
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *space_pos = strchr (full, ' ');
+ if (space_pos)
+ {
+ m_class_category.SetCStringWithLength (class_start, space_pos - class_start);
+ // If m_class hasn't been filled in and the class with category doesn't
+ // contain a '(', then we can also fill in the m_class
+ if (!m_class && strchr (m_class_category.GetCString(), '(') == NULL)
+ {
+ m_class = m_class_category;
+ // No '(' was found in the full name, we can definitively say
+ // that our category was valid (and empty).
+ m_category_is_valid = true;
+
+ }
+ }
+ }
+ }
+ return m_class_category;
+}
+
+const ConstString &
+ObjCLanguageRuntime::MethodName::GetSelector ()
+{
+ if (!m_selector)
+ {
+ if (IsValid(false))
+ {
+ const char *full = m_full.GetCString();
+ const char *space_pos = strchr (full, ' ');
+ if (space_pos)
+ {
+ ++space_pos; // skip the space
+ m_selector.SetCStringWithLength (space_pos, m_full.GetLength() - (space_pos - full) - 1);
+ }
+ }
+ }
+ return m_selector;
+}
+
+const ConstString &
+ObjCLanguageRuntime::MethodName::GetCategory ()
+{
+ if (!m_category_is_valid && !m_category)
+ {
+ if (IsValid(false))
+ {
+ m_category_is_valid = true;
+ const char *full = m_full.GetCString();
+ const char *class_start = (full[0] == '[' ? full + 1 : full + 2);
+ const char *open_paren_pos = strchr (class_start, '(');
+ if (open_paren_pos)
+ {
+ ++open_paren_pos; // Skip the open paren
+ const char *close_paren_pos = strchr (open_paren_pos, ')');
+ if (close_paren_pos)
+ m_category.SetCStringWithLength (open_paren_pos, close_paren_pos - open_paren_pos);
+ }
+ }
+ }
+ return m_category;
+}
+
+ConstString
+ObjCLanguageRuntime::MethodName::GetFullNameWithoutCategory (bool empty_if_no_category)
+{
+ if (IsValid(false))
+ {
+ if (HasCategory())
+ {
+ StreamString strm;
+ if (m_type == eTypeClassMethod)
+ strm.PutChar('+');
+ else if (m_type == eTypeInstanceMethod)
+ strm.PutChar('-');
+ strm.Printf("[%s %s]", GetClassName().GetCString(), GetSelector().GetCString());
+ return ConstString(strm.GetString().c_str());
+ }
+
+ if (!empty_if_no_category)
+ {
+ // Just return the full name since it doesn't have a category
+ return GetFullName();
+ }
+ }
+ return ConstString();
+}
+
+size_t
+ObjCLanguageRuntime::MethodName::GetFullNames (std::vector<ConstString> &names, bool append)
+{
+ if (!append)
+ names.clear();
+ if (IsValid(false))
+ {
+ StreamString strm;
+ const bool is_class_method = m_type == eTypeClassMethod;
+ const bool is_instance_method = m_type == eTypeInstanceMethod;
+ const ConstString &category = GetCategory();
+ if (is_class_method || is_instance_method)
+ {
+ names.push_back (m_full);
+ if (category)
+ {
+ strm.Printf("%c[%s %s]",
+ is_class_method ? '+' : '-',
+ GetClassName().GetCString(),
+ GetSelector().GetCString());
+ names.push_back(ConstString(strm.GetString().c_str()));
+ }
+ }
+ else
+ {
+ const ConstString &class_name = GetClassName();
+ const ConstString &selector = GetSelector();
+ strm.Printf("+[%s %s]", class_name.GetCString(), selector.GetCString());
+ names.push_back(ConstString(strm.GetString().c_str()));
+ strm.Clear();
+ strm.Printf("-[%s %s]", class_name.GetCString(), selector.GetCString());
+ names.push_back(ConstString(strm.GetString().c_str()));
+ strm.Clear();
+ if (category)
+ {
+ strm.Printf("+[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString());
+ names.push_back(ConstString(strm.GetString().c_str()));
+ strm.Clear();
+ strm.Printf("-[%s(%s) %s]", class_name.GetCString(), category.GetCString(), selector.GetCString());
+ names.push_back(ConstString(strm.GetString().c_str()));
+ }
+ }
+ }
+ return names.size();
+}
+
+
+bool
+ObjCLanguageRuntime::ClassDescriptor::IsPointerValid (lldb::addr_t value,
+ uint32_t ptr_size,
+ bool allow_NULLs,
+ bool allow_tagged,
+ bool check_version_specific) const
+{
+ if (!value)
+ return allow_NULLs;
+ if ( (value % 2) == 1 && allow_tagged)
+ return true;
+ if ((value % ptr_size) == 0)
+ return (check_version_specific ? CheckPointer(value,ptr_size) : true);
+ else
+ return false;
+}
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetISA(const ConstString &name)
+{
+ ISAToDescriptorIterator pos = GetDescriptorIterator (name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->first;
+ return 0;
+}
+
+ObjCLanguageRuntime::ISAToDescriptorIterator
+ObjCLanguageRuntime::GetDescriptorIterator (const ConstString &name)
+{
+ ISAToDescriptorIterator end = m_isa_to_descriptor.end();
+
+ if (name)
+ {
+ UpdateISAToDescriptorMap();
+ if (m_hash_to_isa_map.empty())
+ {
+ // No name hashes were provided, we need to just linearly power through the
+ // names and find a match
+ for (ISAToDescriptorIterator pos = m_isa_to_descriptor.begin(); pos != end; ++pos)
+ {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ }
+ else
+ {
+ // Name hashes were provided, so use them to efficiently lookup name to isa/descriptor
+ const uint32_t name_hash = MappedHash::HashStringUsingDJB (name.GetCString());
+ std::pair <HashToISAIterator, HashToISAIterator> range = m_hash_to_isa_map.equal_range(name_hash);
+ for (HashToISAIterator range_pos = range.first; range_pos != range.second; ++range_pos)
+ {
+ ISAToDescriptorIterator pos = m_isa_to_descriptor.find (range_pos->second);
+ if (pos != m_isa_to_descriptor.end())
+ {
+ if (pos->second->GetClassName() == name)
+ return pos;
+ }
+ }
+ }
+ }
+ return end;
+}
+
+
+ObjCLanguageRuntime::ObjCISA
+ObjCLanguageRuntime::GetParentClass(ObjCLanguageRuntime::ObjCISA isa)
+{
+ ClassDescriptorSP objc_class_sp (GetClassDescriptorFromISA(isa));
+ if (objc_class_sp)
+ {
+ ClassDescriptorSP objc_super_class_sp (objc_class_sp->GetSuperclass());
+ if (objc_super_class_sp)
+ return objc_super_class_sp->GetISA();
+ }
+ return 0;
+}
+
+ConstString
+ObjCLanguageRuntime::GetActualTypeName(ObjCLanguageRuntime::ObjCISA isa)
+{
+ ClassDescriptorSP objc_class_sp (GetNonKVOClassDescriptor(isa));
+ if (objc_class_sp)
+ return objc_class_sp->GetClassName();
+ return ConstString();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromClassName (const ConstString &class_name)
+{
+ ISAToDescriptorIterator pos = GetDescriptorIterator (class_name);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ return ClassDescriptorSP();
+
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptor (ValueObject& valobj)
+{
+ ClassDescriptorSP objc_class_sp;
+ // if we get an invalid VO (which might still happen when playing around
+ // with pointers returned by the expression parser, don't consider this
+ // a valid ObjC object)
+ if (valobj.GetClangType().IsValid())
+ {
+ addr_t isa_pointer = valobj.GetPointerValue();
+ if (isa_pointer != LLDB_INVALID_ADDRESS)
+ {
+ ExecutionContext exe_ctx (valobj.GetExecutionContextRef());
+
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process)
+ {
+ Error error;
+ ObjCISA isa = process->ReadPointerFromMemory(isa_pointer, error);
+ if (isa != LLDB_INVALID_ADDRESS)
+ objc_class_sp = GetClassDescriptorFromISA (isa);
+ }
+ }
+ }
+ return objc_class_sp;
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor (ValueObject& valobj)
+{
+ ObjCLanguageRuntime::ClassDescriptorSP objc_class_sp (GetClassDescriptor (valobj));
+ if (objc_class_sp)
+ {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ return ClassDescriptorSP();
+}
+
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetClassDescriptorFromISA (ObjCISA isa)
+{
+ if (isa)
+ {
+ UpdateISAToDescriptorMap();
+ ObjCLanguageRuntime::ISAToDescriptorIterator pos = m_isa_to_descriptor.find(isa);
+ if (pos != m_isa_to_descriptor.end())
+ return pos->second;
+ }
+ return ClassDescriptorSP();
+}
+
+ObjCLanguageRuntime::ClassDescriptorSP
+ObjCLanguageRuntime::GetNonKVOClassDescriptor (ObjCISA isa)
+{
+ if (isa)
+ {
+ ClassDescriptorSP objc_class_sp = GetClassDescriptorFromISA (isa);
+ if (objc_class_sp && objc_class_sp->IsValid())
+ {
+ if (!objc_class_sp->IsKVO())
+ return objc_class_sp;
+
+ ClassDescriptorSP non_kvo_objc_class_sp(objc_class_sp->GetSuperclass());
+ if (non_kvo_objc_class_sp && non_kvo_objc_class_sp->IsValid())
+ return non_kvo_objc_class_sp;
+ }
+ }
+ return ClassDescriptorSP();
+}
+
+
+
diff --git a/source/Target/OperatingSystem.cpp b/source/Target/OperatingSystem.cpp
new file mode 100644
index 000000000000..8ba526838777
--- /dev/null
+++ b/source/Target/OperatingSystem.cpp
@@ -0,0 +1,67 @@
+//===-- OperatingSystem.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/Target/OperatingSystem.h"
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+OperatingSystem*
+OperatingSystem::FindPlugin (Process *process, const char *plugin_name)
+{
+ OperatingSystemCreateInstance create_callback = NULL;
+ if (plugin_name)
+ {
+ ConstString const_plugin_name(plugin_name);
+ create_callback = PluginManager::GetOperatingSystemCreateCallbackForPluginName (const_plugin_name);
+ if (create_callback)
+ {
+ std::unique_ptr<OperatingSystem> instance_ap(create_callback(process, true));
+ if (instance_ap.get())
+ return instance_ap.release();
+ }
+ }
+ else
+ {
+ for (uint32_t idx = 0; (create_callback = PluginManager::GetOperatingSystemCreateCallbackAtIndex(idx)) != NULL; ++idx)
+ {
+ std::unique_ptr<OperatingSystem> instance_ap(create_callback(process, false));
+ if (instance_ap.get())
+ return instance_ap.release();
+ }
+ }
+ return NULL;
+}
+
+
+OperatingSystem::OperatingSystem (Process *process) :
+ m_process (process)
+{
+}
+
+OperatingSystem::~OperatingSystem()
+{
+}
+
+
+bool
+OperatingSystem::IsOperatingSystemPluginThread (const lldb::ThreadSP &thread_sp)
+{
+ if (thread_sp)
+ return thread_sp->IsOperatingSystemPluginThread();
+ return false;
+}
+
diff --git a/source/Target/PathMappingList.cpp b/source/Target/PathMappingList.cpp
new file mode 100644
index 000000000000..db23a0b27130
--- /dev/null
+++ b/source/Target/PathMappingList.cpp
@@ -0,0 +1,348 @@
+//===-- PathMappingList.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+#include <limits.h>
+#include <string.h>
+
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Target/PathMappingList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// PathMappingList constructor
+//----------------------------------------------------------------------
+PathMappingList::PathMappingList () :
+ m_pairs (),
+ m_callback (NULL),
+ m_callback_baton (NULL),
+ m_mod_id (0)
+{
+}
+
+PathMappingList::PathMappingList (ChangedCallback callback,
+ void *callback_baton) :
+ m_pairs (),
+ m_callback (callback),
+ m_callback_baton (callback_baton),
+ m_mod_id (0)
+{
+}
+
+
+PathMappingList::PathMappingList (const PathMappingList &rhs) :
+ m_pairs (rhs.m_pairs),
+ m_callback (NULL),
+ m_callback_baton (NULL),
+ m_mod_id (0)
+{
+
+}
+
+const PathMappingList &
+PathMappingList::operator =(const PathMappingList &rhs)
+{
+ if (this != &rhs)
+ {
+ m_pairs = rhs.m_pairs;
+ m_callback = NULL;
+ m_callback_baton = NULL;
+ m_mod_id = rhs.m_mod_id;
+ }
+ return *this;
+}
+
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+PathMappingList::~PathMappingList ()
+{
+}
+
+void
+PathMappingList::Append (const ConstString &path,
+ const ConstString &replacement,
+ bool notify)
+{
+ ++m_mod_id;
+ m_pairs.push_back(pair(path, replacement));
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+}
+
+void
+PathMappingList::Append (const PathMappingList &rhs, bool notify)
+{
+ ++m_mod_id;
+ if (!rhs.m_pairs.empty())
+ {
+ const_iterator pos, end = rhs.m_pairs.end();
+ for (pos = rhs.m_pairs.begin(); pos != end; ++pos)
+ m_pairs.push_back(*pos);
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+ }
+}
+
+void
+PathMappingList::Insert (const ConstString &path,
+ const ConstString &replacement,
+ uint32_t index,
+ bool notify)
+{
+ ++m_mod_id;
+ iterator insert_iter;
+ if (index >= m_pairs.size())
+ insert_iter = m_pairs.end();
+ else
+ insert_iter = m_pairs.begin() + index;
+ m_pairs.insert(insert_iter, pair(path, replacement));
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+}
+
+bool
+PathMappingList::Replace (const ConstString &path,
+ const ConstString &replacement,
+ uint32_t index,
+ bool notify)
+{
+ iterator insert_iter;
+ if (index >= m_pairs.size())
+ return false;
+ ++m_mod_id;
+ m_pairs[index] = pair(path, replacement);
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+ return true;
+}
+
+bool
+PathMappingList::Remove (off_t index, bool notify)
+{
+ if (index >= m_pairs.size())
+ return false;
+
+ ++m_mod_id;
+ iterator iter = m_pairs.begin() + index;
+ m_pairs.erase(iter);
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+ return true;
+}
+
+// For clients which do not need the pair index dumped, pass a pair_index >= 0
+// to only dump the indicated pair.
+void
+PathMappingList::Dump (Stream *s, int pair_index)
+{
+ unsigned int numPairs = m_pairs.size();
+
+ if (pair_index < 0)
+ {
+ unsigned int index;
+ for (index = 0; index < numPairs; ++index)
+ s->Printf("[%d] \"%s\" -> \"%s\"\n",
+ index, m_pairs[index].first.GetCString(), m_pairs[index].second.GetCString());
+ }
+ else
+ {
+ if (pair_index < numPairs)
+ s->Printf("%s -> %s",
+ m_pairs[pair_index].first.GetCString(), m_pairs[pair_index].second.GetCString());
+ }
+}
+
+void
+PathMappingList::Clear (bool notify)
+{
+ if (!m_pairs.empty())
+ ++m_mod_id;
+ m_pairs.clear();
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+}
+
+bool
+PathMappingList::RemapPath (const ConstString &path, ConstString &new_path) const
+{
+ const char *path_cstr = path.GetCString();
+
+ if (!path_cstr)
+ return false;
+
+ const_iterator pos, end = m_pairs.end();
+ for (pos = m_pairs.begin(); pos != end; ++pos)
+ {
+ const size_t prefixLen = pos->first.GetLength();
+
+ if (::strncmp (pos->first.GetCString(), path_cstr, prefixLen) == 0)
+ {
+ std::string new_path_str (pos->second.GetCString());
+ new_path_str.append(path.GetCString() + prefixLen);
+ new_path.SetCString(new_path_str.c_str());
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+PathMappingList::RemapPath (const char *path, std::string &new_path) const
+{
+ if (m_pairs.empty() || path == NULL || path[0] == '\0')
+ return false;
+
+ const_iterator pos, end = m_pairs.end();
+ for (pos = m_pairs.begin(); pos != end; ++pos)
+ {
+ const size_t prefix_len = pos->first.GetLength();
+
+ if (::strncmp (pos->first.GetCString(), path, prefix_len) == 0)
+ {
+ new_path = pos->second.GetCString();
+ new_path.append(path + prefix_len);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+PathMappingList::FindFile (const FileSpec &orig_spec, FileSpec &new_spec) const
+{
+ if (!m_pairs.empty())
+ {
+ char orig_path[PATH_MAX];
+ char new_path[PATH_MAX];
+ const size_t orig_path_len = orig_spec.GetPath (orig_path, sizeof(orig_path));
+ if (orig_path_len > 0)
+ {
+ const_iterator pos, end = m_pairs.end();
+ for (pos = m_pairs.begin(); pos != end; ++pos)
+ {
+ const size_t prefix_len = pos->first.GetLength();
+
+ if (orig_path_len >= prefix_len)
+ {
+ if (::strncmp (pos->first.GetCString(), orig_path, prefix_len) == 0)
+ {
+ const size_t new_path_len = snprintf(new_path, sizeof(new_path), "%s/%s", pos->second.GetCString(), orig_path + prefix_len);
+ if (new_path_len < sizeof(new_path))
+ {
+ new_spec.SetFile (new_path, true);
+ if (new_spec.Exists())
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ new_spec.Clear();
+ return false;
+}
+
+bool
+PathMappingList::Replace (const ConstString &path, const ConstString &new_path, bool notify)
+{
+ uint32_t idx = FindIndexForPath (path);
+ if (idx < m_pairs.size())
+ {
+ ++m_mod_id;
+ m_pairs[idx].second = new_path;
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+ return true;
+ }
+ return false;
+}
+
+bool
+PathMappingList::Remove (const ConstString &path, bool notify)
+{
+ iterator pos = FindIteratorForPath (path);
+ if (pos != m_pairs.end())
+ {
+ ++m_mod_id;
+ m_pairs.erase (pos);
+ if (notify && m_callback)
+ m_callback (*this, m_callback_baton);
+ return true;
+ }
+ return false;
+}
+
+PathMappingList::const_iterator
+PathMappingList::FindIteratorForPath (const ConstString &path) const
+{
+ const_iterator pos;
+ const_iterator begin = m_pairs.begin();
+ const_iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->first == path)
+ break;
+ }
+ return pos;
+}
+
+PathMappingList::iterator
+PathMappingList::FindIteratorForPath (const ConstString &path)
+{
+ iterator pos;
+ iterator begin = m_pairs.begin();
+ iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->first == path)
+ break;
+ }
+ return pos;
+}
+
+bool
+PathMappingList::GetPathsAtIndex (uint32_t idx, ConstString &path, ConstString &new_path) const
+{
+ if (idx < m_pairs.size())
+ {
+ path = m_pairs[idx].first;
+ new_path = m_pairs[idx].second;
+ return true;
+ }
+ return false;
+}
+
+
+
+uint32_t
+PathMappingList::FindIndexForPath (const ConstString &path) const
+{
+ const_iterator pos;
+ const_iterator begin = m_pairs.begin();
+ const_iterator end = m_pairs.end();
+
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->first == path)
+ return std::distance (begin, pos);
+ }
+ return UINT32_MAX;
+}
+
diff --git a/source/Target/Platform.cpp b/source/Target/Platform.cpp
new file mode 100644
index 000000000000..e6d3bc7a55d0
--- /dev/null
+++ b/source/Target/Platform.cpp
@@ -0,0 +1,779 @@
+//===-- Platform.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/Target/Platform.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/BreakpointIDList.h"
+#include "lldb/Core/Error.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSpec.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// Use a singleton function for g_local_platform_sp to avoid init
+// constructors since LLDB is often part of a shared library
+static PlatformSP&
+GetDefaultPlatformSP ()
+{
+ static PlatformSP g_default_platform_sp;
+ return g_default_platform_sp;
+}
+
+static Mutex &
+GetConnectedPlatformListMutex ()
+{
+ static Mutex g_remote_connected_platforms_mutex (Mutex::eMutexTypeRecursive);
+ return g_remote_connected_platforms_mutex;
+}
+static std::vector<PlatformSP> &
+GetConnectedPlatformList ()
+{
+ static std::vector<PlatformSP> g_remote_connected_platforms;
+ return g_remote_connected_platforms;
+}
+
+
+const char *
+Platform::GetHostPlatformName ()
+{
+ return "host";
+}
+
+//------------------------------------------------------------------
+/// Get the native host platform plug-in.
+///
+/// There should only be one of these for each host that LLDB runs
+/// upon that should be statically compiled in and registered using
+/// preprocessor macros or other similar build mechanisms.
+///
+/// This platform will be used as the default platform when launching
+/// or attaching to processes unless another platform is specified.
+//------------------------------------------------------------------
+PlatformSP
+Platform::GetDefaultPlatform ()
+{
+ return GetDefaultPlatformSP ();
+}
+
+void
+Platform::SetDefaultPlatform (const lldb::PlatformSP &platform_sp)
+{
+ // The native platform should use its static void Platform::Initialize()
+ // function to register itself as the native platform.
+ GetDefaultPlatformSP () = platform_sp;
+}
+
+Error
+Platform::GetFile (const FileSpec &platform_file,
+ const UUID *uuid_ptr,
+ FileSpec &local_file)
+{
+ // Default to the local case
+ local_file = platform_file;
+ return Error();
+}
+
+FileSpecList
+Platform::LocateExecutableScriptingResources (Target *target, Module &module)
+{
+ return FileSpecList();
+}
+
+Platform*
+Platform::FindPlugin (Process *process, const ConstString &plugin_name)
+{
+ PlatformCreateInstance create_callback = NULL;
+ if (plugin_name)
+ {
+ create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (plugin_name);
+ if (create_callback)
+ {
+ ArchSpec arch;
+ if (process)
+ {
+ arch = process->GetTarget().GetArchitecture();
+ }
+ std::unique_ptr<Platform> instance_ap(create_callback(process, &arch));
+ if (instance_ap.get())
+ return instance_ap.release();
+ }
+ }
+ else
+ {
+ for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx)
+ {
+ std::unique_ptr<Platform> instance_ap(create_callback(process, nullptr));
+ if (instance_ap.get())
+ return instance_ap.release();
+ }
+ }
+ return NULL;
+}
+
+Error
+Platform::GetSharedModule (const ModuleSpec &module_spec,
+ ModuleSP &module_sp,
+ const FileSpecList *module_search_paths_ptr,
+ ModuleSP *old_module_sp_ptr,
+ bool *did_create_ptr)
+{
+ // Don't do any path remapping for the default implementation
+ // of the platform GetSharedModule function, just call through
+ // to our static ModuleList function. Platform subclasses that
+ // implement remote debugging, might have a developer kits
+ // installed that have cached versions of the files for the
+ // remote target, or might implement a download and cache
+ // locally implementation.
+ const bool always_create = false;
+ return ModuleList::GetSharedModule (module_spec,
+ module_sp,
+ module_search_paths_ptr,
+ old_module_sp_ptr,
+ did_create_ptr,
+ always_create);
+}
+
+PlatformSP
+Platform::Create (const char *platform_name, Error &error)
+{
+ PlatformCreateInstance create_callback = NULL;
+ lldb::PlatformSP platform_sp;
+ if (platform_name && platform_name[0])
+ {
+ ConstString const_platform_name (platform_name);
+ create_callback = PluginManager::GetPlatformCreateCallbackForPluginName (const_platform_name);
+ if (create_callback)
+ platform_sp.reset(create_callback(true, NULL));
+ else
+ error.SetErrorStringWithFormat ("unable to find a plug-in for the platform named \"%s\"", platform_name);
+ }
+ else
+ error.SetErrorString ("invalid platform name");
+ return platform_sp;
+}
+
+
+PlatformSP
+Platform::Create (const ArchSpec &arch, ArchSpec *platform_arch_ptr, Error &error)
+{
+ lldb::PlatformSP platform_sp;
+ if (arch.IsValid())
+ {
+ uint32_t idx;
+ PlatformCreateInstance create_callback;
+ // First try exact arch matches across all platform plug-ins
+ bool exact = true;
+ for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx)
+ {
+ if (create_callback)
+ {
+ platform_sp.reset(create_callback(false, &arch));
+ if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr))
+ return platform_sp;
+ }
+ }
+ // Next try compatible arch matches across all platform plug-ins
+ exact = false;
+ for (idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex (idx)); ++idx)
+ {
+ if (create_callback)
+ {
+ platform_sp.reset(create_callback(false, &arch));
+ if (platform_sp && platform_sp->IsCompatibleArchitecture(arch, exact, platform_arch_ptr))
+ return platform_sp;
+ }
+ }
+ }
+ else
+ error.SetErrorString ("invalid platform name");
+ if (platform_arch_ptr)
+ platform_arch_ptr->Clear();
+ platform_sp.reset();
+ return platform_sp;
+}
+
+uint32_t
+Platform::GetNumConnectedRemotePlatforms ()
+{
+ Mutex::Locker locker (GetConnectedPlatformListMutex ());
+ return GetConnectedPlatformList().size();
+}
+
+PlatformSP
+Platform::GetConnectedRemotePlatformAtIndex (uint32_t idx)
+{
+ PlatformSP platform_sp;
+ {
+ Mutex::Locker locker (GetConnectedPlatformListMutex ());
+ if (idx < GetConnectedPlatformList().size())
+ platform_sp = GetConnectedPlatformList ()[idx];
+ }
+ return platform_sp;
+}
+
+//------------------------------------------------------------------
+/// Default Constructor
+//------------------------------------------------------------------
+Platform::Platform (bool is_host) :
+ m_is_host (is_host),
+ m_os_version_set_while_connected (false),
+ m_system_arch_set_while_connected (false),
+ m_sdk_sysroot (),
+ m_sdk_build (),
+ m_remote_url (),
+ m_name (),
+ m_major_os_version (UINT32_MAX),
+ m_minor_os_version (UINT32_MAX),
+ m_update_os_version (UINT32_MAX),
+ m_system_arch(),
+ m_uid_map_mutex (Mutex::eMutexTypeNormal),
+ m_gid_map_mutex (Mutex::eMutexTypeNormal),
+ m_uid_map(),
+ m_gid_map(),
+ m_max_uid_name_len (0),
+ m_max_gid_name_len (0)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Platform::Platform()", this);
+}
+
+//------------------------------------------------------------------
+/// Destructor.
+///
+/// The destructor is virtual since this class is designed to be
+/// inherited from by the plug-in instance.
+//------------------------------------------------------------------
+Platform::~Platform()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Platform::~Platform()", this);
+}
+
+void
+Platform::GetStatus (Stream &strm)
+{
+ uint32_t major = UINT32_MAX;
+ uint32_t minor = UINT32_MAX;
+ uint32_t update = UINT32_MAX;
+ std::string s;
+ strm.Printf (" Platform: %s\n", GetPluginName().GetCString());
+
+ ArchSpec arch (GetSystemArchitecture());
+ if (arch.IsValid())
+ {
+ if (!arch.GetTriple().str().empty())
+ strm.Printf(" Triple: %s\n", arch.GetTriple().str().c_str());
+ }
+
+ if (GetOSVersion(major, minor, update))
+ {
+ strm.Printf("OS Version: %u", major);
+ if (minor != UINT32_MAX)
+ strm.Printf(".%u", minor);
+ if (update != UINT32_MAX)
+ strm.Printf(".%u", update);
+
+ if (GetOSBuildString (s))
+ strm.Printf(" (%s)", s.c_str());
+
+ strm.EOL();
+ }
+
+ if (GetOSKernelDescription (s))
+ strm.Printf(" Kernel: %s\n", s.c_str());
+
+ if (IsHost())
+ {
+ strm.Printf(" Hostname: %s\n", GetHostname());
+ }
+ else
+ {
+ const bool is_connected = IsConnected();
+ if (is_connected)
+ strm.Printf(" Hostname: %s\n", GetHostname());
+ strm.Printf(" Connected: %s\n", is_connected ? "yes" : "no");
+ }
+}
+
+
+bool
+Platform::GetOSVersion (uint32_t &major,
+ uint32_t &minor,
+ uint32_t &update)
+{
+ bool success = m_major_os_version != UINT32_MAX;
+ if (IsHost())
+ {
+ if (!success)
+ {
+ // We have a local host platform
+ success = Host::GetOSVersion (m_major_os_version,
+ m_minor_os_version,
+ m_update_os_version);
+ m_os_version_set_while_connected = success;
+ }
+ }
+ else
+ {
+ // We have a remote platform. We can only fetch the remote
+ // OS version if we are connected, and we don't want to do it
+ // more than once.
+
+ const bool is_connected = IsConnected();
+
+ bool fetch = false;
+ if (success)
+ {
+ // We have valid OS version info, check to make sure it wasn't
+ // manually set prior to connecting. If it was manually set prior
+ // to connecting, then lets fetch the actual OS version info
+ // if we are now connected.
+ if (is_connected && !m_os_version_set_while_connected)
+ fetch = true;
+ }
+ else
+ {
+ // We don't have valid OS version info, fetch it if we are connected
+ fetch = is_connected;
+ }
+
+ if (fetch)
+ {
+ success = GetRemoteOSVersion ();
+ m_os_version_set_while_connected = success;
+ }
+ }
+
+ if (success)
+ {
+ major = m_major_os_version;
+ minor = m_minor_os_version;
+ update = m_update_os_version;
+ }
+ return success;
+}
+
+bool
+Platform::GetOSBuildString (std::string &s)
+{
+ if (IsHost())
+ return Host::GetOSBuildString (s);
+ else
+ return GetRemoteOSBuildString (s);
+}
+
+bool
+Platform::GetOSKernelDescription (std::string &s)
+{
+ if (IsHost())
+ return Host::GetOSKernelDescription (s);
+ else
+ return GetRemoteOSKernelDescription (s);
+}
+
+ConstString
+Platform::GetName ()
+{
+ const char *name = GetHostname();
+ if (name == NULL || name[0] == '\0')
+ return GetPluginName();
+ return ConstString (name);
+}
+
+const char *
+Platform::GetHostname ()
+{
+ if (IsHost())
+ return "localhost";
+
+ if (m_name.empty())
+ return NULL;
+ return m_name.c_str();
+}
+
+const char *
+Platform::GetUserName (uint32_t uid)
+{
+ const char *user_name = GetCachedUserName(uid);
+ if (user_name)
+ return user_name;
+ if (IsHost())
+ {
+ std::string name;
+ if (Host::GetUserName(uid, name))
+ return SetCachedUserName (uid, name.c_str(), name.size());
+ }
+ return NULL;
+}
+
+const char *
+Platform::GetGroupName (uint32_t gid)
+{
+ const char *group_name = GetCachedGroupName(gid);
+ if (group_name)
+ return group_name;
+ if (IsHost())
+ {
+ std::string name;
+ if (Host::GetGroupName(gid, name))
+ return SetCachedGroupName (gid, name.c_str(), name.size());
+ }
+ return NULL;
+}
+
+bool
+Platform::SetOSVersion (uint32_t major,
+ uint32_t minor,
+ uint32_t update)
+{
+ if (IsHost())
+ {
+ // We don't need anyone setting the OS version for the host platform,
+ // we should be able to figure it out by calling Host::GetOSVersion(...).
+ return false;
+ }
+ else
+ {
+ // We have a remote platform, allow setting the target OS version if
+ // we aren't connected, since if we are connected, we should be able to
+ // request the remote OS version from the connected platform.
+ if (IsConnected())
+ return false;
+ else
+ {
+ // We aren't connected and we might want to set the OS version
+ // ahead of time before we connect so we can peruse files and
+ // use a local SDK or PDK cache of support files to disassemble
+ // or do other things.
+ m_major_os_version = major;
+ m_minor_os_version = minor;
+ m_update_os_version = update;
+ return true;
+ }
+ }
+ return false;
+}
+
+
+Error
+Platform::ResolveExecutable (const FileSpec &exe_file,
+ const ArchSpec &exe_arch,
+ lldb::ModuleSP &exe_module_sp,
+ const FileSpecList *module_search_paths_ptr)
+{
+ Error error;
+ if (exe_file.Exists())
+ {
+ ModuleSpec module_spec (exe_file, exe_arch);
+ if (module_spec.GetArchitecture().IsValid())
+ {
+ error = ModuleList::GetSharedModule (module_spec,
+ exe_module_sp,
+ module_search_paths_ptr,
+ NULL,
+ NULL);
+ }
+ else
+ {
+ // No valid architecture was specified, ask the platform for
+ // the architectures that we should be using (in the correct order)
+ // and see if we can find a match that way
+ for (uint32_t idx = 0; GetSupportedArchitectureAtIndex (idx, module_spec.GetArchitecture()); ++idx)
+ {
+ error = ModuleList::GetSharedModule (module_spec,
+ exe_module_sp,
+ module_search_paths_ptr,
+ NULL,
+ NULL);
+ // Did we find an executable using one of the
+ if (error.Success() && exe_module_sp)
+ break;
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("'%s' does not exist",
+ exe_file.GetPath().c_str());
+ }
+ return error;
+}
+
+Error
+Platform::ResolveSymbolFile (Target &target,
+ const ModuleSpec &sym_spec,
+ FileSpec &sym_file)
+{
+ Error error;
+ if (sym_spec.GetSymbolFileSpec().Exists())
+ sym_file = sym_spec.GetSymbolFileSpec();
+ else
+ error.SetErrorString("unable to resolve symbol file");
+ return error;
+
+}
+
+
+
+bool
+Platform::ResolveRemotePath (const FileSpec &platform_path,
+ FileSpec &resolved_platform_path)
+{
+ resolved_platform_path = platform_path;
+ return resolved_platform_path.ResolvePath();
+}
+
+
+const ArchSpec &
+Platform::GetSystemArchitecture()
+{
+ if (IsHost())
+ {
+ if (!m_system_arch.IsValid())
+ {
+ // We have a local host platform
+ m_system_arch = Host::GetArchitecture();
+ m_system_arch_set_while_connected = m_system_arch.IsValid();
+ }
+ }
+ else
+ {
+ // We have a remote platform. We can only fetch the remote
+ // system architecture if we are connected, and we don't want to do it
+ // more than once.
+
+ const bool is_connected = IsConnected();
+
+ bool fetch = false;
+ if (m_system_arch.IsValid())
+ {
+ // We have valid OS version info, check to make sure it wasn't
+ // manually set prior to connecting. If it was manually set prior
+ // to connecting, then lets fetch the actual OS version info
+ // if we are now connected.
+ if (is_connected && !m_system_arch_set_while_connected)
+ fetch = true;
+ }
+ else
+ {
+ // We don't have valid OS version info, fetch it if we are connected
+ fetch = is_connected;
+ }
+
+ if (fetch)
+ {
+ m_system_arch = GetRemoteSystemArchitecture ();
+ m_system_arch_set_while_connected = m_system_arch.IsValid();
+ }
+ }
+ return m_system_arch;
+}
+
+
+Error
+Platform::ConnectRemote (Args& args)
+{
+ Error error;
+ if (IsHost())
+ error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString());
+ else
+ error.SetErrorStringWithFormat ("Platform::ConnectRemote() is not supported by %s", GetPluginName().GetCString());
+ return error;
+}
+
+Error
+Platform::DisconnectRemote ()
+{
+ Error error;
+ if (IsHost())
+ error.SetErrorStringWithFormat ("The currently selected platform (%s) is the host platform and is always connected.", GetPluginName().GetCString());
+ else
+ error.SetErrorStringWithFormat ("Platform::DisconnectRemote() is not supported by %s", GetPluginName().GetCString());
+ return error;
+}
+
+bool
+Platform::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info)
+{
+ // Take care of the host case so that each subclass can just
+ // call this function to get the host functionality.
+ if (IsHost())
+ return Host::GetProcessInfo (pid, process_info);
+ return false;
+}
+
+uint32_t
+Platform::FindProcesses (const ProcessInstanceInfoMatch &match_info,
+ ProcessInstanceInfoList &process_infos)
+{
+ // Take care of the host case so that each subclass can just
+ // call this function to get the host functionality.
+ uint32_t match_count = 0;
+ if (IsHost())
+ match_count = Host::FindProcesses (match_info, process_infos);
+ return match_count;
+}
+
+
+Error
+Platform::LaunchProcess (ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ // Take care of the host case so that each subclass can just
+ // call this function to get the host functionality.
+ if (IsHost())
+ {
+ if (::getenv ("LLDB_LAUNCH_FLAG_LAUNCH_IN_TTY"))
+ launch_info.GetFlags().Set (eLaunchFlagLaunchInTTY);
+
+ if (launch_info.GetFlags().Test (eLaunchFlagLaunchInShell))
+ {
+ const bool is_localhost = true;
+ const bool will_debug = launch_info.GetFlags().Test(eLaunchFlagDebug);
+ const bool first_arg_is_full_shell_command = false;
+ if (!launch_info.ConvertArgumentsForLaunchingInShell (error,
+ is_localhost,
+ will_debug,
+ first_arg_is_full_shell_command))
+ return error;
+ }
+
+ error = Host::LaunchProcess (launch_info);
+ }
+ else
+ error.SetErrorString ("base lldb_private::Platform class can't launch remote processes");
+ return error;
+}
+
+lldb::ProcessSP
+Platform::DebugProcess (ProcessLaunchInfo &launch_info,
+ Debugger &debugger,
+ Target *target, // Can be NULL, if NULL create a new target, else use existing one
+ Listener &listener,
+ Error &error)
+{
+ ProcessSP process_sp;
+ // Make sure we stop at the entry point
+ launch_info.GetFlags ().Set (eLaunchFlagDebug);
+ // We always launch the process we are going to debug in a separate process
+ // group, since then we can handle ^C interrupts ourselves w/o having to worry
+ // about the target getting them as well.
+ launch_info.SetLaunchInSeparateProcessGroup(true);
+
+ error = LaunchProcess (launch_info);
+ if (error.Success())
+ {
+ if (launch_info.GetProcessID() != LLDB_INVALID_PROCESS_ID)
+ {
+ ProcessAttachInfo attach_info (launch_info);
+ process_sp = Attach (attach_info, debugger, target, listener, error);
+ if (process_sp)
+ {
+ // Since we attached to the process, it will think it needs to detach
+ // if the process object just goes away without an explicit call to
+ // Process::Kill() or Process::Detach(), so let it know to kill the
+ // process if this happens.
+ process_sp->SetShouldDetach (false);
+
+ // If we didn't have any file actions, the pseudo terminal might
+ // have been used where the slave side was given as the file to
+ // open for stdin/out/err after we have already opened the master
+ // so we can read/write stdin/out/err.
+ int pty_fd = launch_info.GetPTY().ReleaseMasterFileDescriptor();
+ if (pty_fd != lldb_utility::PseudoTerminal::invalid_fd)
+ {
+ process_sp->SetSTDIOFileDescriptor(pty_fd);
+ }
+ }
+ }
+ }
+ return process_sp;
+}
+
+
+lldb::PlatformSP
+Platform::GetPlatformForArchitecture (const ArchSpec &arch, ArchSpec *platform_arch_ptr)
+{
+ lldb::PlatformSP platform_sp;
+ Error error;
+ if (arch.IsValid())
+ platform_sp = Platform::Create (arch, platform_arch_ptr, error);
+ return platform_sp;
+}
+
+
+//------------------------------------------------------------------
+/// Lets a platform answer if it is compatible with a given
+/// architecture and the target triple contained within.
+//------------------------------------------------------------------
+bool
+Platform::IsCompatibleArchitecture (const ArchSpec &arch, bool exact_arch_match, ArchSpec *compatible_arch_ptr)
+{
+ // If the architecture is invalid, we must answer true...
+ if (arch.IsValid())
+ {
+ ArchSpec platform_arch;
+ // Try for an exact architecture match first.
+ if (exact_arch_match)
+ {
+ for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx)
+ {
+ if (arch.IsExactMatch(platform_arch))
+ {
+ if (compatible_arch_ptr)
+ *compatible_arch_ptr = platform_arch;
+ return true;
+ }
+ }
+ }
+ else
+ {
+ for (uint32_t arch_idx=0; GetSupportedArchitectureAtIndex (arch_idx, platform_arch); ++arch_idx)
+ {
+ if (arch.IsCompatibleMatch(platform_arch))
+ {
+ if (compatible_arch_ptr)
+ *compatible_arch_ptr = platform_arch;
+ return true;
+ }
+ }
+ }
+ }
+ if (compatible_arch_ptr)
+ compatible_arch_ptr->Clear();
+ return false;
+
+}
+
+
+lldb::BreakpointSP
+Platform::SetThreadCreationBreakpoint (lldb_private::Target &target)
+{
+ return lldb::BreakpointSP();
+}
+
+size_t
+Platform::GetEnvironment (StringList &environment)
+{
+ environment.Clear();
+ return false;
+}
+
diff --git a/source/Target/Process.cpp b/source/Target/Process.cpp
new file mode 100644
index 000000000000..6edb9390d990
--- /dev/null
+++ b/source/Target/Process.cpp
@@ -0,0 +1,5595 @@
+//===-- Process.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/lldb-python.h"
+
+#include "lldb/Target/Process.h"
+
+#include "lldb/lldb-private-log.h"
+
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Event.h"
+#include "lldb/Core/ConnectionFileDescriptor.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/InputReader.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/State.h"
+#include "lldb/Expression/ClangUserExpression.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/OperatingSystem.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/CPPLanguageRuntime.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/TargetList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanBase.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+// Comment out line below to disable memory caching, overriding the process setting
+// target.process.disable-memory-cache
+#define ENABLE_MEMORY_CACHING
+
+#ifdef ENABLE_MEMORY_CACHING
+#define DISABLE_MEM_CACHE_DEFAULT false
+#else
+#define DISABLE_MEM_CACHE_DEFAULT true
+#endif
+
+class ProcessOptionValueProperties : public OptionValueProperties
+{
+public:
+ ProcessOptionValueProperties (const ConstString &name) :
+ OptionValueProperties (name)
+ {
+ }
+
+ // This constructor is used when creating ProcessOptionValueProperties when it
+ // is part of a new lldb_private::Process instance. It will copy all current
+ // global property values as needed
+ ProcessOptionValueProperties (ProcessProperties *global_properties) :
+ OptionValueProperties(*global_properties->GetValueProperties())
+ {
+ }
+
+ virtual const Property *
+ GetPropertyAtIndex (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const
+ {
+ // When gettings the value for a key from the process options, we will always
+ // try and grab the setting from the current process if there is one. Else we just
+ // use the one from this instance.
+ if (exe_ctx)
+ {
+ Process *process = exe_ctx->GetProcessPtr();
+ if (process)
+ {
+ ProcessOptionValueProperties *instance_properties = static_cast<ProcessOptionValueProperties *>(process->GetValueProperties().get());
+ if (this != instance_properties)
+ return instance_properties->ProtectedGetPropertyAtIndex (idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex (idx);
+ }
+};
+
+static PropertyDefinition
+g_properties[] =
+{
+ { "disable-memory-cache" , OptionValue::eTypeBoolean, false, DISABLE_MEM_CACHE_DEFAULT, NULL, NULL, "Disable reading and caching of memory in fixed-size units." },
+ { "extra-startup-command", OptionValue::eTypeArray , false, OptionValue::eTypeString, NULL, NULL, "A list containing extra commands understood by the particular process plugin used. "
+ "For instance, to turn on debugserver logging set this to \"QSetLogging:bitmask=LOG_DEFAULT;\"" },
+ { "ignore-breakpoints-in-expressions", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, breakpoints will be ignored during expression evaluation." },
+ { "unwind-on-error-in-expressions", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, errors in expression evaluation will unwind the stack back to the state before the call." },
+ { "python-os-plugin-path", OptionValue::eTypeFileSpec, false, true, NULL, NULL, "A path to a python OS plug-in module file that contains a OperatingSystemPlugIn class." },
+ { "stop-on-sharedlibrary-events" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, stop when a shared library is loaded or unloaded." },
+ { "detach-keeps-stopped" , OptionValue::eTypeBoolean, true, false, NULL, NULL, "If true, detach will attempt to keep the process stopped." },
+ { NULL , OptionValue::eTypeInvalid, false, 0, NULL, NULL, NULL }
+};
+
+enum {
+ ePropertyDisableMemCache,
+ ePropertyExtraStartCommand,
+ ePropertyIgnoreBreakpointsInExpressions,
+ ePropertyUnwindOnErrorInExpressions,
+ ePropertyPythonOSPluginPath,
+ ePropertyStopOnSharedLibraryEvents,
+ ePropertyDetachKeepsStopped
+};
+
+ProcessProperties::ProcessProperties (bool is_global) :
+ Properties ()
+{
+ if (is_global)
+ {
+ m_collection_sp.reset (new ProcessOptionValueProperties(ConstString("process")));
+ m_collection_sp->Initialize(g_properties);
+ m_collection_sp->AppendProperty(ConstString("thread"),
+ ConstString("Settings specific to threads."),
+ true,
+ Thread::GetGlobalProperties()->GetValueProperties());
+ }
+ else
+ m_collection_sp.reset (new ProcessOptionValueProperties(Process::GetGlobalProperties().get()));
+}
+
+ProcessProperties::~ProcessProperties()
+{
+}
+
+bool
+ProcessProperties::GetDisableMemoryCache() const
+{
+ const uint32_t idx = ePropertyDisableMemCache;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+Args
+ProcessProperties::GetExtraStartupCommands () const
+{
+ Args args;
+ const uint32_t idx = ePropertyExtraStartCommand;
+ m_collection_sp->GetPropertyAtIndexAsArgs(NULL, idx, args);
+ return args;
+}
+
+void
+ProcessProperties::SetExtraStartupCommands (const Args &args)
+{
+ const uint32_t idx = ePropertyExtraStartCommand;
+ m_collection_sp->SetPropertyAtIndexFromArgs(NULL, idx, args);
+}
+
+FileSpec
+ProcessProperties::GetPythonOSPluginPath () const
+{
+ const uint32_t idx = ePropertyPythonOSPluginPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(NULL, idx);
+}
+
+void
+ProcessProperties::SetPythonOSPluginPath (const FileSpec &file)
+{
+ const uint32_t idx = ePropertyPythonOSPluginPath;
+ m_collection_sp->SetPropertyAtIndexAsFileSpec(NULL, idx, file);
+}
+
+
+bool
+ProcessProperties::GetIgnoreBreakpointsInExpressions () const
+{
+ const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+ProcessProperties::SetIgnoreBreakpointsInExpressions (bool ignore)
+{
+ const uint32_t idx = ePropertyIgnoreBreakpointsInExpressions;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore);
+}
+
+bool
+ProcessProperties::GetUnwindOnErrorInExpressions () const
+{
+ const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+ProcessProperties::SetUnwindOnErrorInExpressions (bool ignore)
+{
+ const uint32_t idx = ePropertyUnwindOnErrorInExpressions;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, ignore);
+}
+
+bool
+ProcessProperties::GetStopOnSharedLibraryEvents () const
+{
+ const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+ProcessProperties::SetStopOnSharedLibraryEvents (bool stop)
+{
+ const uint32_t idx = ePropertyStopOnSharedLibraryEvents;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop);
+}
+
+bool
+ProcessProperties::GetDetachKeepsStopped () const
+{
+ const uint32_t idx = ePropertyDetachKeepsStopped;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean(NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+ProcessProperties::SetDetachKeepsStopped (bool stop)
+{
+ const uint32_t idx = ePropertyDetachKeepsStopped;
+ m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, stop);
+}
+
+void
+ProcessInstanceInfo::Dump (Stream &s, Platform *platform) const
+{
+ const char *cstr;
+ if (m_pid != LLDB_INVALID_PROCESS_ID)
+ s.Printf (" pid = %" PRIu64 "\n", m_pid);
+
+ if (m_parent_pid != LLDB_INVALID_PROCESS_ID)
+ s.Printf (" parent = %" PRIu64 "\n", m_parent_pid);
+
+ if (m_executable)
+ {
+ s.Printf (" name = %s\n", m_executable.GetFilename().GetCString());
+ s.PutCString (" file = ");
+ m_executable.Dump(&s);
+ s.EOL();
+ }
+ const uint32_t argc = m_arguments.GetArgumentCount();
+ if (argc > 0)
+ {
+ for (uint32_t i=0; i<argc; i++)
+ {
+ const char *arg = m_arguments.GetArgumentAtIndex(i);
+ if (i < 10)
+ s.Printf (" arg[%u] = %s\n", i, arg);
+ else
+ s.Printf ("arg[%u] = %s\n", i, arg);
+ }
+ }
+
+ const uint32_t envc = m_environment.GetArgumentCount();
+ if (envc > 0)
+ {
+ for (uint32_t i=0; i<envc; i++)
+ {
+ const char *env = m_environment.GetArgumentAtIndex(i);
+ if (i < 10)
+ s.Printf (" env[%u] = %s\n", i, env);
+ else
+ s.Printf ("env[%u] = %s\n", i, env);
+ }
+ }
+
+ if (m_arch.IsValid())
+ s.Printf (" arch = %s\n", m_arch.GetTriple().str().c_str());
+
+ if (m_uid != UINT32_MAX)
+ {
+ cstr = platform->GetUserName (m_uid);
+ s.Printf (" uid = %-5u (%s)\n", m_uid, cstr ? cstr : "");
+ }
+ if (m_gid != UINT32_MAX)
+ {
+ cstr = platform->GetGroupName (m_gid);
+ s.Printf (" gid = %-5u (%s)\n", m_gid, cstr ? cstr : "");
+ }
+ if (m_euid != UINT32_MAX)
+ {
+ cstr = platform->GetUserName (m_euid);
+ s.Printf (" euid = %-5u (%s)\n", m_euid, cstr ? cstr : "");
+ }
+ if (m_egid != UINT32_MAX)
+ {
+ cstr = platform->GetGroupName (m_egid);
+ s.Printf (" egid = %-5u (%s)\n", m_egid, cstr ? cstr : "");
+ }
+}
+
+void
+ProcessInstanceInfo::DumpTableHeader (Stream &s, Platform *platform, bool show_args, bool verbose)
+{
+ const char *label;
+ if (show_args || verbose)
+ label = "ARGUMENTS";
+ else
+ label = "NAME";
+
+ if (verbose)
+ {
+ s.Printf ("PID PARENT USER GROUP EFF USER EFF GROUP TRIPLE %s\n", label);
+ s.PutCString ("====== ====== ========== ========== ========== ========== ======================== ============================\n");
+ }
+ else
+ {
+ s.Printf ("PID PARENT USER ARCH %s\n", label);
+ s.PutCString ("====== ====== ========== ======= ============================\n");
+ }
+}
+
+void
+ProcessInstanceInfo::DumpAsTableRow (Stream &s, Platform *platform, bool show_args, bool verbose) const
+{
+ if (m_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ const char *cstr;
+ s.Printf ("%-6" PRIu64 " %-6" PRIu64 " ", m_pid, m_parent_pid);
+
+
+ if (verbose)
+ {
+ cstr = platform->GetUserName (m_uid);
+ if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed
+ s.Printf ("%-10s ", cstr);
+ else
+ s.Printf ("%-10u ", m_uid);
+
+ cstr = platform->GetGroupName (m_gid);
+ if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed
+ s.Printf ("%-10s ", cstr);
+ else
+ s.Printf ("%-10u ", m_gid);
+
+ cstr = platform->GetUserName (m_euid);
+ if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed
+ s.Printf ("%-10s ", cstr);
+ else
+ s.Printf ("%-10u ", m_euid);
+
+ cstr = platform->GetGroupName (m_egid);
+ if (cstr && cstr[0]) // Watch for empty string that indicates lookup failed
+ s.Printf ("%-10s ", cstr);
+ else
+ s.Printf ("%-10u ", m_egid);
+ s.Printf ("%-24s ", m_arch.IsValid() ? m_arch.GetTriple().str().c_str() : "");
+ }
+ else
+ {
+ s.Printf ("%-10s %-7d %s ",
+ platform->GetUserName (m_euid),
+ (int)m_arch.GetTriple().getArchName().size(),
+ m_arch.GetTriple().getArchName().data());
+ }
+
+ if (verbose || show_args)
+ {
+ const uint32_t argc = m_arguments.GetArgumentCount();
+ if (argc > 0)
+ {
+ for (uint32_t i=0; i<argc; i++)
+ {
+ if (i > 0)
+ s.PutChar (' ');
+ s.PutCString (m_arguments.GetArgumentAtIndex(i));
+ }
+ }
+ }
+ else
+ {
+ s.PutCString (GetName());
+ }
+
+ s.EOL();
+ }
+}
+
+
+void
+ProcessInfo::SetArguments (char const **argv, bool first_arg_is_executable)
+{
+ m_arguments.SetArguments (argv);
+
+ // Is the first argument the executable?
+ if (first_arg_is_executable)
+ {
+ const char *first_arg = m_arguments.GetArgumentAtIndex (0);
+ if (first_arg)
+ {
+ // Yes the first argument is an executable, set it as the executable
+ // in the launch options. Don't resolve the file path as the path
+ // could be a remote platform path
+ const bool resolve = false;
+ m_executable.SetFile(first_arg, resolve);
+ }
+ }
+}
+void
+ProcessInfo::SetArguments (const Args& args, bool first_arg_is_executable)
+{
+ // Copy all arguments
+ m_arguments = args;
+
+ // Is the first argument the executable?
+ if (first_arg_is_executable)
+ {
+ const char *first_arg = m_arguments.GetArgumentAtIndex (0);
+ if (first_arg)
+ {
+ // Yes the first argument is an executable, set it as the executable
+ // in the launch options. Don't resolve the file path as the path
+ // could be a remote platform path
+ const bool resolve = false;
+ m_executable.SetFile(first_arg, resolve);
+ }
+ }
+}
+
+void
+ProcessLaunchInfo::FinalizeFileActions (Target *target, bool default_to_use_pty)
+{
+ // If notthing was specified, then check the process for any default
+ // settings that were set with "settings set"
+ if (m_file_actions.empty())
+ {
+ if (m_flags.Test(eLaunchFlagDisableSTDIO))
+ {
+ AppendSuppressFileAction (STDIN_FILENO , true, false);
+ AppendSuppressFileAction (STDOUT_FILENO, false, true);
+ AppendSuppressFileAction (STDERR_FILENO, false, true);
+ }
+ else
+ {
+ // Check for any values that might have gotten set with any of:
+ // (lldb) settings set target.input-path
+ // (lldb) settings set target.output-path
+ // (lldb) settings set target.error-path
+ FileSpec in_path;
+ FileSpec out_path;
+ FileSpec err_path;
+ if (target)
+ {
+ in_path = target->GetStandardInputPath();
+ out_path = target->GetStandardOutputPath();
+ err_path = target->GetStandardErrorPath();
+ }
+
+ if (in_path || out_path || err_path)
+ {
+ char path[PATH_MAX];
+ if (in_path && in_path.GetPath(path, sizeof(path)))
+ AppendOpenFileAction(STDIN_FILENO, path, true, false);
+
+ if (out_path && out_path.GetPath(path, sizeof(path)))
+ AppendOpenFileAction(STDOUT_FILENO, path, false, true);
+
+ if (err_path && err_path.GetPath(path, sizeof(path)))
+ AppendOpenFileAction(STDERR_FILENO, path, false, true);
+ }
+ else if (default_to_use_pty)
+ {
+ if (m_pty.OpenFirstAvailableMaster (O_RDWR|O_NOCTTY, NULL, 0))
+ {
+ const char *slave_path = m_pty.GetSlaveName (NULL, 0);
+ AppendOpenFileAction(STDIN_FILENO, slave_path, true, false);
+ AppendOpenFileAction(STDOUT_FILENO, slave_path, false, true);
+ AppendOpenFileAction(STDERR_FILENO, slave_path, false, true);
+ }
+ }
+ }
+ }
+}
+
+
+bool
+ProcessLaunchInfo::ConvertArgumentsForLaunchingInShell (Error &error,
+ bool localhost,
+ bool will_debug,
+ bool first_arg_is_full_shell_command)
+{
+ error.Clear();
+
+ if (GetFlags().Test (eLaunchFlagLaunchInShell))
+ {
+ const char *shell_executable = GetShell();
+ if (shell_executable)
+ {
+ char shell_resolved_path[PATH_MAX];
+
+ if (localhost)
+ {
+ FileSpec shell_filespec (shell_executable, true);
+
+ if (!shell_filespec.Exists())
+ {
+ // Resolve the path in case we just got "bash", "sh" or "tcsh"
+ if (!shell_filespec.ResolveExecutableLocation ())
+ {
+ error.SetErrorStringWithFormat("invalid shell path '%s'", shell_executable);
+ return false;
+ }
+ }
+ shell_filespec.GetPath (shell_resolved_path, sizeof(shell_resolved_path));
+ shell_executable = shell_resolved_path;
+ }
+
+ const char **argv = GetArguments().GetConstArgumentVector ();
+ if (argv == NULL || argv[0] == NULL)
+ return false;
+ Args shell_arguments;
+ std::string safe_arg;
+ shell_arguments.AppendArgument (shell_executable);
+ shell_arguments.AppendArgument ("-c");
+ StreamString shell_command;
+ if (will_debug)
+ {
+ // Add a modified PATH environment variable in case argv[0]
+ // is a relative path
+ const char *argv0 = argv[0];
+ if (argv0 && (argv0[0] != '/' && argv0[0] != '~'))
+ {
+ // We have a relative path to our executable which may not work if
+ // we just try to run "a.out" (without it being converted to "./a.out")
+ const char *working_dir = GetWorkingDirectory();
+ // Be sure to put quotes around PATH's value in case any paths have spaces...
+ std::string new_path("PATH=\"");
+ const size_t empty_path_len = new_path.size();
+
+ if (working_dir && working_dir[0])
+ {
+ new_path += working_dir;
+ }
+ else
+ {
+ char current_working_dir[PATH_MAX];
+ const char *cwd = getcwd(current_working_dir, sizeof(current_working_dir));
+ if (cwd && cwd[0])
+ new_path += cwd;
+ }
+ const char *curr_path = getenv("PATH");
+ if (curr_path)
+ {
+ if (new_path.size() > empty_path_len)
+ new_path += ':';
+ new_path += curr_path;
+ }
+ new_path += "\" ";
+ shell_command.PutCString(new_path.c_str());
+ }
+
+ shell_command.PutCString ("exec");
+
+ // Only Apple supports /usr/bin/arch being able to specify the architecture
+ if (GetArchitecture().IsValid())
+ {
+ shell_command.Printf(" /usr/bin/arch -arch %s", GetArchitecture().GetArchitectureName());
+ // Set the resume count to 2:
+ // 1 - stop in shell
+ // 2 - stop in /usr/bin/arch
+ // 3 - then we will stop in our program
+ SetResumeCount(2);
+ }
+ else
+ {
+ // Set the resume count to 1:
+ // 1 - stop in shell
+ // 2 - then we will stop in our program
+ SetResumeCount(1);
+ }
+ }
+
+ if (first_arg_is_full_shell_command)
+ {
+ // There should only be one argument that is the shell command itself to be used as is
+ if (argv[0] && !argv[1])
+ shell_command.Printf("%s", argv[0]);
+ else
+ return false;
+ }
+ else
+ {
+ for (size_t i=0; argv[i] != NULL; ++i)
+ {
+ const char *arg = Args::GetShellSafeArgument (argv[i], safe_arg);
+ shell_command.Printf(" %s", arg);
+ }
+ }
+ shell_arguments.AppendArgument (shell_command.GetString().c_str());
+ m_executable.SetFile(shell_executable, false);
+ m_arguments = shell_arguments;
+ return true;
+ }
+ else
+ {
+ error.SetErrorString ("invalid shell path");
+ }
+ }
+ else
+ {
+ error.SetErrorString ("not launching in shell");
+ }
+ return false;
+}
+
+
+bool
+ProcessLaunchInfo::FileAction::Open (int fd, const char *path, bool read, bool write)
+{
+ if ((read || write) && fd >= 0 && path && path[0])
+ {
+ m_action = eFileActionOpen;
+ m_fd = fd;
+ if (read && write)
+ m_arg = O_NOCTTY | O_CREAT | O_RDWR;
+ else if (read)
+ m_arg = O_NOCTTY | O_RDONLY;
+ else
+ m_arg = O_NOCTTY | O_CREAT | O_WRONLY;
+ m_path.assign (path);
+ return true;
+ }
+ else
+ {
+ Clear();
+ }
+ return false;
+}
+
+bool
+ProcessLaunchInfo::FileAction::Close (int fd)
+{
+ Clear();
+ if (fd >= 0)
+ {
+ m_action = eFileActionClose;
+ m_fd = fd;
+ }
+ return m_fd >= 0;
+}
+
+
+bool
+ProcessLaunchInfo::FileAction::Duplicate (int fd, int dup_fd)
+{
+ Clear();
+ if (fd >= 0 && dup_fd >= 0)
+ {
+ m_action = eFileActionDuplicate;
+ m_fd = fd;
+ m_arg = dup_fd;
+ }
+ return m_fd >= 0;
+}
+
+
+
+bool
+ProcessLaunchInfo::FileAction::AddPosixSpawnFileAction (posix_spawn_file_actions_t *file_actions,
+ const FileAction *info,
+ Log *log,
+ Error& error)
+{
+ if (info == NULL)
+ return false;
+
+ switch (info->m_action)
+ {
+ case eFileActionNone:
+ error.Clear();
+ break;
+
+ case eFileActionClose:
+ if (info->m_fd == -1)
+ error.SetErrorString ("invalid fd for posix_spawn_file_actions_addclose(...)");
+ else
+ {
+ error.SetError (::posix_spawn_file_actions_addclose (file_actions, info->m_fd),
+ eErrorTypePOSIX);
+ if (log && (error.Fail() || log))
+ error.PutToLog(log, "posix_spawn_file_actions_addclose (action=%p, fd=%i)",
+ file_actions, info->m_fd);
+ }
+ break;
+
+ case eFileActionDuplicate:
+ if (info->m_fd == -1)
+ error.SetErrorString ("invalid fd for posix_spawn_file_actions_adddup2(...)");
+ else if (info->m_arg == -1)
+ error.SetErrorString ("invalid duplicate fd for posix_spawn_file_actions_adddup2(...)");
+ else
+ {
+ error.SetError (::posix_spawn_file_actions_adddup2 (file_actions, info->m_fd, info->m_arg),
+ eErrorTypePOSIX);
+ if (log && (error.Fail() || log))
+ error.PutToLog(log, "posix_spawn_file_actions_adddup2 (action=%p, fd=%i, dup_fd=%i)",
+ file_actions, info->m_fd, info->m_arg);
+ }
+ break;
+
+ case eFileActionOpen:
+ if (info->m_fd == -1)
+ error.SetErrorString ("invalid fd in posix_spawn_file_actions_addopen(...)");
+ else
+ {
+ int oflag = info->m_arg;
+
+ mode_t mode = 0;
+
+ if (oflag & O_CREAT)
+ mode = 0640;
+
+ error.SetError (::posix_spawn_file_actions_addopen (file_actions,
+ info->m_fd,
+ info->m_path.c_str(),
+ oflag,
+ mode),
+ eErrorTypePOSIX);
+ if (error.Fail() || log)
+ error.PutToLog(log,
+ "posix_spawn_file_actions_addopen (action=%p, fd=%i, path='%s', oflag=%i, mode=%i)",
+ file_actions, info->m_fd, info->m_path.c_str(), oflag, mode);
+ }
+ break;
+ }
+ return error.Success();
+}
+
+Error
+ProcessLaunchCommandOptions::SetOptionValue (uint32_t option_idx, const char *option_arg)
+{
+ Error error;
+ const int short_option = m_getopt_table[option_idx].val;
+
+ switch (short_option)
+ {
+ case 's': // Stop at program entry point
+ launch_info.GetFlags().Set (eLaunchFlagStopAtEntry);
+ break;
+
+ case 'i': // STDIN for read only
+ {
+ ProcessLaunchInfo::FileAction action;
+ if (action.Open (STDIN_FILENO, option_arg, true, false))
+ launch_info.AppendFileAction (action);
+ }
+ break;
+
+ case 'o': // Open STDOUT for write only
+ {
+ ProcessLaunchInfo::FileAction action;
+ if (action.Open (STDOUT_FILENO, option_arg, false, true))
+ launch_info.AppendFileAction (action);
+ }
+ break;
+
+ case 'e': // STDERR for write only
+ {
+ ProcessLaunchInfo::FileAction action;
+ if (action.Open (STDERR_FILENO, option_arg, false, true))
+ launch_info.AppendFileAction (action);
+ }
+ break;
+
+
+ case 'p': // Process plug-in name
+ launch_info.SetProcessPluginName (option_arg);
+ break;
+
+ case 'n': // Disable STDIO
+ {
+ ProcessLaunchInfo::FileAction action;
+ if (action.Open (STDIN_FILENO, "/dev/null", true, false))
+ launch_info.AppendFileAction (action);
+ if (action.Open (STDOUT_FILENO, "/dev/null", false, true))
+ launch_info.AppendFileAction (action);
+ if (action.Open (STDERR_FILENO, "/dev/null", false, true))
+ launch_info.AppendFileAction (action);
+ }
+ break;
+
+ case 'w':
+ launch_info.SetWorkingDirectory (option_arg);
+ break;
+
+ case 't': // Open process in new terminal window
+ launch_info.GetFlags().Set (eLaunchFlagLaunchInTTY);
+ break;
+
+ case 'a':
+ if (!launch_info.GetArchitecture().SetTriple (option_arg, m_interpreter.GetPlatform(true).get()))
+ launch_info.GetArchitecture().SetTriple (option_arg);
+ break;
+
+ case 'A':
+ launch_info.GetFlags().Set (eLaunchFlagDisableASLR);
+ break;
+
+ case 'c':
+ if (option_arg && option_arg[0])
+ launch_info.SetShell (option_arg);
+ else
+ launch_info.SetShell ("/bin/bash");
+ break;
+
+ case 'v':
+ launch_info.GetEnvironmentEntries().AppendArgument(option_arg);
+ break;
+
+ default:
+ error.SetErrorStringWithFormat("unrecognized short option character '%c'", short_option);
+ break;
+
+ }
+ return error;
+}
+
+OptionDefinition
+ProcessLaunchCommandOptions::g_option_table[] =
+{
+{ LLDB_OPT_SET_ALL, false, "stop-at-entry", 's', no_argument, NULL, 0, eArgTypeNone, "Stop at the entry point of the program when launching a process."},
+{ LLDB_OPT_SET_ALL, false, "disable-aslr", 'A', no_argument, NULL, 0, eArgTypeNone, "Disable address space layout randomization when launching a process."},
+{ LLDB_OPT_SET_ALL, false, "plugin", 'p', required_argument, NULL, 0, eArgTypePlugin, "Name of the process plugin you want to use."},
+{ LLDB_OPT_SET_ALL, false, "working-dir", 'w', required_argument, NULL, 0, eArgTypeDirectoryName, "Set the current working directory to <path> when running the inferior."},
+{ LLDB_OPT_SET_ALL, false, "arch", 'a', required_argument, NULL, 0, eArgTypeArchitecture, "Set the architecture for the process to launch when ambiguous."},
+{ LLDB_OPT_SET_ALL, false, "environment", 'v', required_argument, NULL, 0, eArgTypeNone, "Specify an environment variable name/value stirng (--environement NAME=VALUE). Can be specified multiple times for subsequent environment entries."},
+{ LLDB_OPT_SET_ALL, false, "shell", 'c', optional_argument, NULL, 0, eArgTypeFilename, "Run the process in a shell (not supported on all platforms)."},
+
+{ LLDB_OPT_SET_1 , false, "stdin", 'i', required_argument, NULL, 0, eArgTypeFilename, "Redirect stdin for the process to <filename>."},
+{ LLDB_OPT_SET_1 , false, "stdout", 'o', required_argument, NULL, 0, eArgTypeFilename, "Redirect stdout for the process to <filename>."},
+{ LLDB_OPT_SET_1 , false, "stderr", 'e', required_argument, NULL, 0, eArgTypeFilename, "Redirect stderr for the process to <filename>."},
+
+{ LLDB_OPT_SET_2 , false, "tty", 't', no_argument, NULL, 0, eArgTypeNone, "Start the process in a terminal (not supported on all platforms)."},
+
+{ LLDB_OPT_SET_3 , false, "no-stdio", 'n', no_argument, NULL, 0, eArgTypeNone, "Do not set up for terminal I/O to go to running process."},
+
+{ 0 , false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
+};
+
+
+
+bool
+ProcessInstanceInfoMatch::NameMatches (const char *process_name) const
+{
+ if (m_name_match_type == eNameMatchIgnore || process_name == NULL)
+ return true;
+ const char *match_name = m_match_info.GetName();
+ if (!match_name)
+ return true;
+
+ return lldb_private::NameMatches (process_name, m_name_match_type, match_name);
+}
+
+bool
+ProcessInstanceInfoMatch::Matches (const ProcessInstanceInfo &proc_info) const
+{
+ if (!NameMatches (proc_info.GetName()))
+ return false;
+
+ if (m_match_info.ProcessIDIsValid() &&
+ m_match_info.GetProcessID() != proc_info.GetProcessID())
+ return false;
+
+ if (m_match_info.ParentProcessIDIsValid() &&
+ m_match_info.GetParentProcessID() != proc_info.GetParentProcessID())
+ return false;
+
+ if (m_match_info.UserIDIsValid () &&
+ m_match_info.GetUserID() != proc_info.GetUserID())
+ return false;
+
+ if (m_match_info.GroupIDIsValid () &&
+ m_match_info.GetGroupID() != proc_info.GetGroupID())
+ return false;
+
+ if (m_match_info.EffectiveUserIDIsValid () &&
+ m_match_info.GetEffectiveUserID() != proc_info.GetEffectiveUserID())
+ return false;
+
+ if (m_match_info.EffectiveGroupIDIsValid () &&
+ m_match_info.GetEffectiveGroupID() != proc_info.GetEffectiveGroupID())
+ return false;
+
+ if (m_match_info.GetArchitecture().IsValid() &&
+ !m_match_info.GetArchitecture().IsCompatibleMatch(proc_info.GetArchitecture()))
+ return false;
+ return true;
+}
+
+bool
+ProcessInstanceInfoMatch::MatchAllProcesses () const
+{
+ if (m_name_match_type != eNameMatchIgnore)
+ return false;
+
+ if (m_match_info.ProcessIDIsValid())
+ return false;
+
+ if (m_match_info.ParentProcessIDIsValid())
+ return false;
+
+ if (m_match_info.UserIDIsValid ())
+ return false;
+
+ if (m_match_info.GroupIDIsValid ())
+ return false;
+
+ if (m_match_info.EffectiveUserIDIsValid ())
+ return false;
+
+ if (m_match_info.EffectiveGroupIDIsValid ())
+ return false;
+
+ if (m_match_info.GetArchitecture().IsValid())
+ return false;
+
+ if (m_match_all_users)
+ return false;
+
+ return true;
+
+}
+
+void
+ProcessInstanceInfoMatch::Clear()
+{
+ m_match_info.Clear();
+ m_name_match_type = eNameMatchIgnore;
+ m_match_all_users = false;
+}
+
+ProcessSP
+Process::FindPlugin (Target &target, const char *plugin_name, Listener &listener, const FileSpec *crash_file_path)
+{
+ static uint32_t g_process_unique_id = 0;
+
+ ProcessSP process_sp;
+ ProcessCreateInstance create_callback = NULL;
+ if (plugin_name)
+ {
+ ConstString const_plugin_name(plugin_name);
+ create_callback = PluginManager::GetProcessCreateCallbackForPluginName (const_plugin_name);
+ if (create_callback)
+ {
+ process_sp = create_callback(target, listener, crash_file_path);
+ if (process_sp)
+ {
+ if (process_sp->CanDebug(target, true))
+ {
+ process_sp->m_process_unique_id = ++g_process_unique_id;
+ }
+ else
+ process_sp.reset();
+ }
+ }
+ }
+ else
+ {
+ for (uint32_t idx = 0; (create_callback = PluginManager::GetProcessCreateCallbackAtIndex(idx)) != NULL; ++idx)
+ {
+ process_sp = create_callback(target, listener, crash_file_path);
+ if (process_sp)
+ {
+ if (process_sp->CanDebug(target, false))
+ {
+ process_sp->m_process_unique_id = ++g_process_unique_id;
+ break;
+ }
+ else
+ process_sp.reset();
+ }
+ }
+ }
+ return process_sp;
+}
+
+ConstString &
+Process::GetStaticBroadcasterClass ()
+{
+ static ConstString class_name ("lldb.process");
+ return class_name;
+}
+
+//----------------------------------------------------------------------
+// Process constructor
+//----------------------------------------------------------------------
+Process::Process(Target &target, Listener &listener) :
+ ProcessProperties (false),
+ UserID (LLDB_INVALID_PROCESS_ID),
+ Broadcaster (&(target.GetDebugger()), "lldb.process"),
+ m_target (target),
+ m_public_state (eStateUnloaded),
+ m_private_state (eStateUnloaded),
+ m_private_state_broadcaster (NULL, "lldb.process.internal_state_broadcaster"),
+ m_private_state_control_broadcaster (NULL, "lldb.process.internal_state_control_broadcaster"),
+ m_private_state_listener ("lldb.process.internal_state_listener"),
+ m_private_state_control_wait(),
+ m_private_state_thread (LLDB_INVALID_HOST_THREAD),
+ m_mod_id (),
+ m_process_unique_id(0),
+ m_thread_index_id (0),
+ m_thread_id_to_index_id_map (),
+ m_exit_status (-1),
+ m_exit_string (),
+ m_thread_mutex (Mutex::eMutexTypeRecursive),
+ m_thread_list_real (this),
+ m_thread_list (this),
+ m_notifications (),
+ m_image_tokens (),
+ m_listener (listener),
+ m_breakpoint_site_list (),
+ m_dynamic_checkers_ap (),
+ m_unix_signals (),
+ m_abi_sp (),
+ m_process_input_reader (),
+ m_stdio_communication ("process.stdio"),
+ m_stdio_communication_mutex (Mutex::eMutexTypeRecursive),
+ m_stdout_data (),
+ m_stderr_data (),
+ m_profile_data_comm_mutex (Mutex::eMutexTypeRecursive),
+ m_profile_data (),
+ m_memory_cache (*this),
+ m_allocated_memory_cache (*this),
+ m_should_detach (false),
+ m_next_event_action_ap(),
+ m_public_run_lock (),
+ m_private_run_lock (),
+ m_currently_handling_event(false),
+ m_finalize_called(false),
+ m_clear_thread_plans_on_stop (false),
+ m_last_broadcast_state (eStateInvalid),
+ m_destroy_in_process (false),
+ m_can_jit(eCanJITDontKnow)
+{
+ CheckInWithManager ();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Process::Process()", this);
+
+ SetEventName (eBroadcastBitStateChanged, "state-changed");
+ SetEventName (eBroadcastBitInterrupt, "interrupt");
+ SetEventName (eBroadcastBitSTDOUT, "stdout-available");
+ SetEventName (eBroadcastBitSTDERR, "stderr-available");
+ SetEventName (eBroadcastBitProfileData, "profile-data-available");
+
+ m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlStop , "control-stop" );
+ m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlPause , "control-pause" );
+ m_private_state_control_broadcaster.SetEventName (eBroadcastInternalStateControlResume, "control-resume");
+
+ listener.StartListeningForEvents (this,
+ eBroadcastBitStateChanged |
+ eBroadcastBitInterrupt |
+ eBroadcastBitSTDOUT |
+ eBroadcastBitSTDERR |
+ eBroadcastBitProfileData);
+
+ m_private_state_listener.StartListeningForEvents(&m_private_state_broadcaster,
+ eBroadcastBitStateChanged |
+ eBroadcastBitInterrupt);
+
+ m_private_state_listener.StartListeningForEvents(&m_private_state_control_broadcaster,
+ eBroadcastInternalStateControlStop |
+ eBroadcastInternalStateControlPause |
+ eBroadcastInternalStateControlResume);
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+Process::~Process()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Process::~Process()", this);
+ StopPrivateStateThread();
+}
+
+const ProcessPropertiesSP &
+Process::GetGlobalProperties()
+{
+ static ProcessPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp.reset (new ProcessProperties (true));
+ return g_settings_sp;
+}
+
+void
+Process::Finalize()
+{
+ switch (GetPrivateState())
+ {
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateStopped:
+ case eStateRunning:
+ case eStateStepping:
+ case eStateCrashed:
+ case eStateSuspended:
+ if (GetShouldDetach())
+ {
+ // FIXME: This will have to be a process setting:
+ bool keep_stopped = false;
+ Detach(keep_stopped);
+ }
+ else
+ Destroy();
+ break;
+
+ case eStateInvalid:
+ case eStateUnloaded:
+ case eStateDetached:
+ case eStateExited:
+ break;
+ }
+
+ // Clear our broadcaster before we proceed with destroying
+ Broadcaster::Clear();
+
+ // Do any cleanup needed prior to being destructed... Subclasses
+ // that override this method should call this superclass method as well.
+
+ // We need to destroy the loader before the derived Process class gets destroyed
+ // since it is very likely that undoing the loader will require access to the real process.
+ m_dynamic_checkers_ap.reset();
+ m_abi_sp.reset();
+ m_os_ap.reset();
+ m_dyld_ap.reset();
+ m_thread_list_real.Destroy();
+ m_thread_list.Destroy();
+ std::vector<Notifications> empty_notifications;
+ m_notifications.swap(empty_notifications);
+ m_image_tokens.clear();
+ m_memory_cache.Clear();
+ m_allocated_memory_cache.Clear();
+ m_language_runtimes.clear();
+ m_next_event_action_ap.reset();
+//#ifdef LLDB_CONFIGURATION_DEBUG
+// StreamFile s(stdout, false);
+// EventSP event_sp;
+// while (m_private_state_listener.GetNextEvent(event_sp))
+// {
+// event_sp->Dump (&s);
+// s.EOL();
+// }
+//#endif
+ // We have to be very careful here as the m_private_state_listener might
+ // contain events that have ProcessSP values in them which can keep this
+ // process around forever. These events need to be cleared out.
+ m_private_state_listener.Clear();
+ m_public_run_lock.TrySetRunning(); // This will do nothing if already locked
+ m_public_run_lock.SetStopped();
+ m_private_run_lock.TrySetRunning(); // This will do nothing if already locked
+ m_private_run_lock.SetStopped();
+ m_finalize_called = true;
+}
+
+void
+Process::RegisterNotificationCallbacks (const Notifications& callbacks)
+{
+ m_notifications.push_back(callbacks);
+ if (callbacks.initialize != NULL)
+ callbacks.initialize (callbacks.baton, this);
+}
+
+bool
+Process::UnregisterNotificationCallbacks(const Notifications& callbacks)
+{
+ std::vector<Notifications>::iterator pos, end = m_notifications.end();
+ for (pos = m_notifications.begin(); pos != end; ++pos)
+ {
+ if (pos->baton == callbacks.baton &&
+ pos->initialize == callbacks.initialize &&
+ pos->process_state_changed == callbacks.process_state_changed)
+ {
+ m_notifications.erase(pos);
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+Process::SynchronouslyNotifyStateChanged (StateType state)
+{
+ std::vector<Notifications>::iterator notification_pos, notification_end = m_notifications.end();
+ for (notification_pos = m_notifications.begin(); notification_pos != notification_end; ++notification_pos)
+ {
+ if (notification_pos->process_state_changed)
+ notification_pos->process_state_changed (notification_pos->baton, this, state);
+ }
+}
+
+// FIXME: We need to do some work on events before the general Listener sees them.
+// For instance if we are continuing from a breakpoint, we need to ensure that we do
+// the little "insert real insn, step & stop" trick. But we can't do that when the
+// event is delivered by the broadcaster - since that is done on the thread that is
+// waiting for new events, so if we needed more than one event for our handling, we would
+// stall. So instead we do it when we fetch the event off of the queue.
+//
+
+StateType
+Process::GetNextEvent (EventSP &event_sp)
+{
+ StateType state = eStateInvalid;
+
+ if (m_listener.GetNextEventForBroadcaster (this, event_sp) && event_sp)
+ state = Process::ProcessEventData::GetStateFromEvent (event_sp.get());
+
+ return state;
+}
+
+
+StateType
+Process::WaitForProcessToStop (const TimeValue *timeout, lldb::EventSP *event_sp_ptr)
+{
+ // We can't just wait for a "stopped" event, because the stopped event may have restarted the target.
+ // We have to actually check each event, and in the case of a stopped event check the restarted flag
+ // on the event.
+ if (event_sp_ptr)
+ event_sp_ptr->reset();
+ StateType state = GetState();
+ // If we are exited or detached, we won't ever get back to any
+ // other valid state...
+ if (state == eStateDetached || state == eStateExited)
+ return state;
+
+ while (state != eStateInvalid)
+ {
+ EventSP event_sp;
+ state = WaitForStateChangedEvents (timeout, event_sp);
+ if (event_sp_ptr && event_sp)
+ *event_sp_ptr = event_sp;
+
+ switch (state)
+ {
+ case eStateCrashed:
+ case eStateDetached:
+ case eStateExited:
+ case eStateUnloaded:
+ return state;
+ case eStateStopped:
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
+ continue;
+ else
+ return state;
+ default:
+ continue;
+ }
+ }
+ return state;
+}
+
+
+StateType
+Process::WaitForState
+(
+ const TimeValue *timeout,
+ const StateType *match_states, const uint32_t num_match_states
+)
+{
+ EventSP event_sp;
+ uint32_t i;
+ StateType state = GetState();
+ while (state != eStateInvalid)
+ {
+ // If we are exited or detached, we won't ever get back to any
+ // other valid state...
+ if (state == eStateDetached || state == eStateExited)
+ return state;
+
+ state = WaitForStateChangedEvents (timeout, event_sp);
+
+ for (i=0; i<num_match_states; ++i)
+ {
+ if (match_states[i] == state)
+ return state;
+ }
+ }
+ return state;
+}
+
+bool
+Process::HijackProcessEvents (Listener *listener)
+{
+ if (listener != NULL)
+ {
+ return HijackBroadcaster(listener, eBroadcastBitStateChanged | eBroadcastBitInterrupt);
+ }
+ else
+ return false;
+}
+
+void
+Process::RestoreProcessEvents ()
+{
+ RestoreBroadcaster();
+}
+
+bool
+Process::HijackPrivateProcessEvents (Listener *listener)
+{
+ if (listener != NULL)
+ {
+ return m_private_state_broadcaster.HijackBroadcaster(listener, eBroadcastBitStateChanged | eBroadcastBitInterrupt);
+ }
+ else
+ return false;
+}
+
+void
+Process::RestorePrivateProcessEvents ()
+{
+ m_private_state_broadcaster.RestoreBroadcaster();
+}
+
+StateType
+Process::WaitForStateChangedEvents (const TimeValue *timeout, EventSP &event_sp)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout);
+
+ StateType state = eStateInvalid;
+ if (m_listener.WaitForEventForBroadcasterWithType (timeout,
+ this,
+ eBroadcastBitStateChanged | eBroadcastBitInterrupt,
+ event_sp))
+ {
+ if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
+ state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ else if (log)
+ log->Printf ("Process::%s got no event or was interrupted.", __FUNCTION__);
+ }
+
+ if (log)
+ log->Printf ("Process::%s (timeout = %p, event_sp) => %s",
+ __FUNCTION__,
+ timeout,
+ StateAsCString(state));
+ return state;
+}
+
+Event *
+Process::PeekAtStateChangedEvents ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s...", __FUNCTION__);
+
+ Event *event_ptr;
+ event_ptr = m_listener.PeekAtNextEventForBroadcasterWithType (this,
+ eBroadcastBitStateChanged);
+ if (log)
+ {
+ if (event_ptr)
+ {
+ log->Printf ("Process::%s (event_ptr) => %s",
+ __FUNCTION__,
+ StateAsCString(ProcessEventData::GetStateFromEvent (event_ptr)));
+ }
+ else
+ {
+ log->Printf ("Process::%s no events found",
+ __FUNCTION__);
+ }
+ }
+ return event_ptr;
+}
+
+StateType
+Process::WaitForStateChangedEventsPrivate (const TimeValue *timeout, EventSP &event_sp)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout);
+
+ StateType state = eStateInvalid;
+ if (m_private_state_listener.WaitForEventForBroadcasterWithType (timeout,
+ &m_private_state_broadcaster,
+ eBroadcastBitStateChanged | eBroadcastBitInterrupt,
+ event_sp))
+ if (event_sp && event_sp->GetType() == eBroadcastBitStateChanged)
+ state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ // This is a bit of a hack, but when we wait here we could very well return
+ // to the command-line, and that could disable the log, which would render the
+ // log we got above invalid.
+ if (log)
+ {
+ if (state == eStateInvalid)
+ log->Printf ("Process::%s (timeout = %p, event_sp) => TIMEOUT", __FUNCTION__, timeout);
+ else
+ log->Printf ("Process::%s (timeout = %p, event_sp) => %s", __FUNCTION__, timeout, StateAsCString(state));
+ }
+ return state;
+}
+
+bool
+Process::WaitForEventsPrivate (const TimeValue *timeout, EventSP &event_sp, bool control_only)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s (timeout = %p, event_sp)...", __FUNCTION__, timeout);
+
+ if (control_only)
+ return m_private_state_listener.WaitForEventForBroadcaster(timeout, &m_private_state_control_broadcaster, event_sp);
+ else
+ return m_private_state_listener.WaitForEvent(timeout, event_sp);
+}
+
+bool
+Process::IsRunning () const
+{
+ return StateIsRunningState (m_public_state.GetValue());
+}
+
+int
+Process::GetExitStatus ()
+{
+ if (m_public_state.GetValue() == eStateExited)
+ return m_exit_status;
+ return -1;
+}
+
+
+const char *
+Process::GetExitDescription ()
+{
+ if (m_public_state.GetValue() == eStateExited && !m_exit_string.empty())
+ return m_exit_string.c_str();
+ return NULL;
+}
+
+bool
+Process::SetExitStatus (int status, const char *cstr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::SetExitStatus (status=%i (0x%8.8x), description=%s%s%s)",
+ status, status,
+ cstr ? "\"" : "",
+ cstr ? cstr : "NULL",
+ cstr ? "\"" : "");
+
+ // We were already in the exited state
+ if (m_private_state.GetValue() == eStateExited)
+ {
+ if (log)
+ log->Printf("Process::SetExitStatus () ignoring exit status because state was already set to eStateExited");
+ return false;
+ }
+
+ m_exit_status = status;
+ if (cstr)
+ m_exit_string = cstr;
+ else
+ m_exit_string.clear();
+
+ DidExit ();
+
+ SetPrivateState (eStateExited);
+ return true;
+}
+
+// This static callback can be used to watch for local child processes on
+// the current host. The the child process exits, the process will be
+// found in the global target list (we want to be completely sure that the
+// lldb_private::Process doesn't go away before we can deliver the signal.
+bool
+Process::SetProcessExitStatus (void *callback_baton,
+ lldb::pid_t pid,
+ bool exited,
+ int signo, // Zero for no signal
+ int exit_status // Exit value of process if signal is zero
+)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Process::SetProcessExitStatus (baton=%p, pid=%" PRIu64 ", exited=%i, signal=%i, exit_status=%i)\n",
+ callback_baton,
+ pid,
+ exited,
+ signo,
+ exit_status);
+
+ if (exited)
+ {
+ TargetSP target_sp(Debugger::FindTargetWithProcessID (pid));
+ if (target_sp)
+ {
+ ProcessSP process_sp (target_sp->GetProcessSP());
+ if (process_sp)
+ {
+ const char *signal_cstr = NULL;
+ if (signo)
+ signal_cstr = process_sp->GetUnixSignals().GetSignalAsCString (signo);
+
+ process_sp->SetExitStatus (exit_status, signal_cstr);
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+
+void
+Process::UpdateThreadListIfNeeded ()
+{
+ const uint32_t stop_id = GetStopID();
+ if (m_thread_list.GetSize(false) == 0 || stop_id != m_thread_list.GetStopID())
+ {
+ const StateType state = GetPrivateState();
+ if (StateIsStoppedState (state, true))
+ {
+ Mutex::Locker locker (m_thread_list.GetMutex ());
+ // m_thread_list does have its own mutex, but we need to
+ // hold onto the mutex between the call to UpdateThreadList(...)
+ // and the os->UpdateThreadList(...) so it doesn't change on us
+ ThreadList &old_thread_list = m_thread_list;
+ ThreadList real_thread_list(this);
+ ThreadList new_thread_list(this);
+ // Always update the thread list with the protocol specific
+ // thread list, but only update if "true" is returned
+ if (UpdateThreadList (m_thread_list_real, real_thread_list))
+ {
+ // Don't call into the OperatingSystem to update the thread list if we are shutting down, since
+ // that may call back into the SBAPI's, requiring the API lock which is already held by whoever is
+ // shutting us down, causing a deadlock.
+ if (!m_destroy_in_process)
+ {
+ OperatingSystem *os = GetOperatingSystem ();
+ if (os)
+ {
+ // Clear any old backing threads where memory threads might have been
+ // backed by actual threads from the lldb_private::Process subclass
+ size_t num_old_threads = old_thread_list.GetSize(false);
+ for (size_t i=0; i<num_old_threads; ++i)
+ old_thread_list.GetThreadAtIndex(i, false)->ClearBackingThread();
+
+ // Now let the OperatingSystem plug-in update the thread list
+ os->UpdateThreadList (old_thread_list, // Old list full of threads created by OS plug-in
+ real_thread_list, // The actual thread list full of threads created by each lldb_private::Process subclass
+ new_thread_list); // The new thread list that we will show to the user that gets filled in
+ }
+ else
+ {
+ // No OS plug-in, the new thread list is the same as the real thread list
+ new_thread_list = real_thread_list;
+ }
+ }
+
+ m_thread_list_real.Update(real_thread_list);
+ m_thread_list.Update (new_thread_list);
+ m_thread_list.SetStopID (stop_id);
+ }
+ }
+ }
+}
+
+ThreadSP
+Process::CreateOSPluginThread (lldb::tid_t tid, lldb::addr_t context)
+{
+ OperatingSystem *os = GetOperatingSystem ();
+ if (os)
+ return os->CreateThread(tid, context);
+ return ThreadSP();
+}
+
+uint32_t
+Process::GetNextThreadIndexID (uint64_t thread_id)
+{
+ return AssignIndexIDToThread(thread_id);
+}
+
+bool
+Process::HasAssignedIndexIDToThread(uint64_t thread_id)
+{
+ std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_index_id_map.find(thread_id);
+ if (iterator == m_thread_id_to_index_id_map.end())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+uint32_t
+Process::AssignIndexIDToThread(uint64_t thread_id)
+{
+ uint32_t result = 0;
+ std::map<uint64_t, uint32_t>::iterator iterator = m_thread_id_to_index_id_map.find(thread_id);
+ if (iterator == m_thread_id_to_index_id_map.end())
+ {
+ result = ++m_thread_index_id;
+ m_thread_id_to_index_id_map[thread_id] = result;
+ }
+ else
+ {
+ result = iterator->second;
+ }
+
+ return result;
+}
+
+StateType
+Process::GetState()
+{
+ // If any other threads access this we will need a mutex for it
+ return m_public_state.GetValue ();
+}
+
+void
+Process::SetPublicState (StateType new_state, bool restarted)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::SetPublicState (state = %s, restarted = %i)", StateAsCString(new_state), restarted);
+ const StateType old_state = m_public_state.GetValue();
+ m_public_state.SetValue (new_state);
+
+ // On the transition from Run to Stopped, we unlock the writer end of the
+ // run lock. The lock gets locked in Resume, which is the public API
+ // to tell the program to run.
+ if (!IsHijackedForEvent(eBroadcastBitStateChanged))
+ {
+ if (new_state == eStateDetached)
+ {
+ if (log)
+ log->Printf("Process::SetPublicState (%s) -- unlocking run lock for detach", StateAsCString(new_state));
+ m_public_run_lock.SetStopped();
+ }
+ else
+ {
+ const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
+ const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
+ if ((old_state_is_stopped != new_state_is_stopped))
+ {
+ if (new_state_is_stopped && !restarted)
+ {
+ if (log)
+ log->Printf("Process::SetPublicState (%s) -- unlocking run lock", StateAsCString(new_state));
+ m_public_run_lock.SetStopped();
+ }
+ }
+ }
+ }
+}
+
+Error
+Process::Resume ()
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::Resume -- locking run lock");
+ if (!m_public_run_lock.TrySetRunning())
+ {
+ Error error("Resume request failed - process still running.");
+ if (log)
+ log->Printf ("Process::Resume: -- TrySetRunning failed, not resuming.");
+ return error;
+ }
+ return PrivateResume();
+}
+
+StateType
+Process::GetPrivateState ()
+{
+ return m_private_state.GetValue();
+}
+
+void
+Process::SetPrivateState (StateType new_state)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STATE | LIBLLDB_LOG_PROCESS));
+ bool state_changed = false;
+
+ if (log)
+ log->Printf("Process::SetPrivateState (%s)", StateAsCString(new_state));
+
+ Mutex::Locker thread_locker(m_thread_list.GetMutex());
+ Mutex::Locker locker(m_private_state.GetMutex());
+
+ const StateType old_state = m_private_state.GetValueNoLock ();
+ state_changed = old_state != new_state;
+
+ const bool old_state_is_stopped = StateIsStoppedState(old_state, false);
+ const bool new_state_is_stopped = StateIsStoppedState(new_state, false);
+ if (old_state_is_stopped != new_state_is_stopped)
+ {
+ if (new_state_is_stopped)
+ m_private_run_lock.SetStopped();
+ else
+ m_private_run_lock.SetRunning();
+ }
+
+ if (state_changed)
+ {
+ m_private_state.SetValueNoLock (new_state);
+ if (StateIsStoppedState(new_state, false))
+ {
+ // Note, this currently assumes that all threads in the list
+ // stop when the process stops. In the future we will want to
+ // support a debugging model where some threads continue to run
+ // while others are stopped. When that happens we will either need
+ // a way for the thread list to identify which threads are stopping
+ // or create a special thread list containing only threads which
+ // actually stopped.
+ //
+ // The process plugin is responsible for managing the actual
+ // behavior of the threads and should have stopped any threads
+ // that are going to stop before we get here.
+ m_thread_list.DidStop();
+
+ m_mod_id.BumpStopID();
+ m_memory_cache.Clear();
+ if (log)
+ log->Printf("Process::SetPrivateState (%s) stop_id = %u", StateAsCString(new_state), m_mod_id.GetStopID());
+ }
+ // Use our target to get a shared pointer to ourselves...
+ if (m_finalize_called && PrivateStateThreadIsValid() == false)
+ BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state));
+ else
+ m_private_state_broadcaster.BroadcastEvent (eBroadcastBitStateChanged, new ProcessEventData (shared_from_this(), new_state));
+ }
+ else
+ {
+ if (log)
+ log->Printf("Process::SetPrivateState (%s) state didn't change. Ignoring...", StateAsCString(new_state));
+ }
+}
+
+void
+Process::SetRunningUserExpression (bool on)
+{
+ m_mod_id.SetRunningUserExpression (on);
+}
+
+addr_t
+Process::GetImageInfoAddress()
+{
+ return LLDB_INVALID_ADDRESS;
+}
+
+//----------------------------------------------------------------------
+// LoadImage
+//
+// This function provides a default implementation that works for most
+// unix variants. Any Process subclasses that need to do shared library
+// loading differently should override LoadImage and UnloadImage and
+// do what is needed.
+//----------------------------------------------------------------------
+uint32_t
+Process::LoadImage (const FileSpec &image_spec, Error &error)
+{
+ char path[PATH_MAX];
+ image_spec.GetPath(path, sizeof(path));
+
+ DynamicLoader *loader = GetDynamicLoader();
+ if (loader)
+ {
+ error = loader->CanLoadImage();
+ if (error.Fail())
+ return LLDB_INVALID_IMAGE_TOKEN;
+ }
+
+ if (error.Success())
+ {
+ ThreadSP thread_sp(GetThreadList ().GetSelectedThread());
+
+ if (thread_sp)
+ {
+ StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex (0));
+
+ if (frame_sp)
+ {
+ ExecutionContext exe_ctx;
+ frame_sp->CalculateExecutionContext (exe_ctx);
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ StreamString expr;
+ expr.Printf("dlopen (\"%s\", 2)", path);
+ const char *prefix = "extern \"C\" void* dlopen (const char *path, int mode);\n";
+ lldb::ValueObjectSP result_valobj_sp;
+ ClangUserExpression::Evaluate (exe_ctx,
+ eExecutionPolicyAlways,
+ lldb::eLanguageTypeUnknown,
+ ClangUserExpression::eResultTypeAny,
+ unwind_on_error,
+ ignore_breakpoints,
+ expr.GetData(),
+ prefix,
+ result_valobj_sp,
+ true,
+ ClangUserExpression::kDefaultTimeout);
+ error = result_valobj_sp->GetError();
+ if (error.Success())
+ {
+ Scalar scalar;
+ if (result_valobj_sp->ResolveValue (scalar))
+ {
+ addr_t image_ptr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
+ if (image_ptr != 0 && image_ptr != LLDB_INVALID_ADDRESS)
+ {
+ uint32_t image_token = m_image_tokens.size();
+ m_image_tokens.push_back (image_ptr);
+ return image_token;
+ }
+ }
+ }
+ }
+ }
+ }
+ if (!error.AsCString())
+ error.SetErrorStringWithFormat("unable to load '%s'", path);
+ return LLDB_INVALID_IMAGE_TOKEN;
+}
+
+//----------------------------------------------------------------------
+// UnloadImage
+//
+// This function provides a default implementation that works for most
+// unix variants. Any Process subclasses that need to do shared library
+// loading differently should override LoadImage and UnloadImage and
+// do what is needed.
+//----------------------------------------------------------------------
+Error
+Process::UnloadImage (uint32_t image_token)
+{
+ Error error;
+ if (image_token < m_image_tokens.size())
+ {
+ const addr_t image_addr = m_image_tokens[image_token];
+ if (image_addr == LLDB_INVALID_ADDRESS)
+ {
+ error.SetErrorString("image already unloaded");
+ }
+ else
+ {
+ DynamicLoader *loader = GetDynamicLoader();
+ if (loader)
+ error = loader->CanLoadImage();
+
+ if (error.Success())
+ {
+ ThreadSP thread_sp(GetThreadList ().GetSelectedThread());
+
+ if (thread_sp)
+ {
+ StackFrameSP frame_sp (thread_sp->GetStackFrameAtIndex (0));
+
+ if (frame_sp)
+ {
+ ExecutionContext exe_ctx;
+ frame_sp->CalculateExecutionContext (exe_ctx);
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ StreamString expr;
+ expr.Printf("dlclose ((void *)0x%" PRIx64 ")", image_addr);
+ const char *prefix = "extern \"C\" int dlclose(void* handle);\n";
+ lldb::ValueObjectSP result_valobj_sp;
+ ClangUserExpression::Evaluate (exe_ctx,
+ eExecutionPolicyAlways,
+ lldb::eLanguageTypeUnknown,
+ ClangUserExpression::eResultTypeAny,
+ unwind_on_error,
+ ignore_breakpoints,
+ expr.GetData(),
+ prefix,
+ result_valobj_sp,
+ true,
+ ClangUserExpression::kDefaultTimeout);
+ if (result_valobj_sp->GetError().Success())
+ {
+ Scalar scalar;
+ if (result_valobj_sp->ResolveValue (scalar))
+ {
+ if (scalar.UInt(1))
+ {
+ error.SetErrorStringWithFormat("expression failed: \"%s\"", expr.GetData());
+ }
+ else
+ {
+ m_image_tokens[image_token] = LLDB_INVALID_ADDRESS;
+ }
+ }
+ }
+ else
+ {
+ error = result_valobj_sp->GetError();
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorString("invalid image token");
+ }
+ return error;
+}
+
+const lldb::ABISP &
+Process::GetABI()
+{
+ if (!m_abi_sp)
+ m_abi_sp = ABI::FindPlugin(m_target.GetArchitecture());
+ return m_abi_sp;
+}
+
+LanguageRuntime *
+Process::GetLanguageRuntime(lldb::LanguageType language, bool retry_if_null)
+{
+ LanguageRuntimeCollection::iterator pos;
+ pos = m_language_runtimes.find (language);
+ if (pos == m_language_runtimes.end() || (retry_if_null && !(*pos).second))
+ {
+ lldb::LanguageRuntimeSP runtime_sp(LanguageRuntime::FindPlugin(this, language));
+
+ m_language_runtimes[language] = runtime_sp;
+ return runtime_sp.get();
+ }
+ else
+ return (*pos).second.get();
+}
+
+CPPLanguageRuntime *
+Process::GetCPPLanguageRuntime (bool retry_if_null)
+{
+ LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeC_plus_plus, retry_if_null);
+ if (runtime != NULL && runtime->GetLanguageType() == eLanguageTypeC_plus_plus)
+ return static_cast<CPPLanguageRuntime *> (runtime);
+ return NULL;
+}
+
+ObjCLanguageRuntime *
+Process::GetObjCLanguageRuntime (bool retry_if_null)
+{
+ LanguageRuntime *runtime = GetLanguageRuntime(eLanguageTypeObjC, retry_if_null);
+ if (runtime != NULL && runtime->GetLanguageType() == eLanguageTypeObjC)
+ return static_cast<ObjCLanguageRuntime *> (runtime);
+ return NULL;
+}
+
+bool
+Process::IsPossibleDynamicValue (ValueObject& in_value)
+{
+ if (in_value.IsDynamic())
+ return false;
+ LanguageType known_type = in_value.GetObjectRuntimeLanguage();
+
+ if (known_type != eLanguageTypeUnknown && known_type != eLanguageTypeC)
+ {
+ LanguageRuntime *runtime = GetLanguageRuntime (known_type);
+ return runtime ? runtime->CouldHaveDynamicValue(in_value) : false;
+ }
+
+ LanguageRuntime *cpp_runtime = GetLanguageRuntime (eLanguageTypeC_plus_plus);
+ if (cpp_runtime && cpp_runtime->CouldHaveDynamicValue(in_value))
+ return true;
+
+ LanguageRuntime *objc_runtime = GetLanguageRuntime (eLanguageTypeObjC);
+ return objc_runtime ? objc_runtime->CouldHaveDynamicValue(in_value) : false;
+}
+
+BreakpointSiteList &
+Process::GetBreakpointSiteList()
+{
+ return m_breakpoint_site_list;
+}
+
+const BreakpointSiteList &
+Process::GetBreakpointSiteList() const
+{
+ return m_breakpoint_site_list;
+}
+
+
+void
+Process::DisableAllBreakpointSites ()
+{
+ m_breakpoint_site_list.ForEach([this](BreakpointSite *bp_site) -> void {
+// bp_site->SetEnabled(true);
+ DisableBreakpointSite(bp_site);
+ });
+}
+
+Error
+Process::ClearBreakpointSiteByID (lldb::user_id_t break_id)
+{
+ Error error (DisableBreakpointSiteByID (break_id));
+
+ if (error.Success())
+ m_breakpoint_site_list.Remove(break_id);
+
+ return error;
+}
+
+Error
+Process::DisableBreakpointSiteByID (lldb::user_id_t break_id)
+{
+ Error error;
+ BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id);
+ if (bp_site_sp)
+ {
+ if (bp_site_sp->IsEnabled())
+ error = DisableBreakpointSite (bp_site_sp.get());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id);
+ }
+
+ return error;
+}
+
+Error
+Process::EnableBreakpointSiteByID (lldb::user_id_t break_id)
+{
+ Error error;
+ BreakpointSiteSP bp_site_sp = m_breakpoint_site_list.FindByID (break_id);
+ if (bp_site_sp)
+ {
+ if (!bp_site_sp->IsEnabled())
+ error = EnableBreakpointSite (bp_site_sp.get());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("invalid breakpoint site ID: %" PRIu64, break_id);
+ }
+ return error;
+}
+
+lldb::break_id_t
+Process::CreateBreakpointSite (const BreakpointLocationSP &owner, bool use_hardware)
+{
+ const addr_t load_addr = owner->GetAddress().GetOpcodeLoadAddress (&m_target);
+ if (load_addr != LLDB_INVALID_ADDRESS)
+ {
+ BreakpointSiteSP bp_site_sp;
+
+ // Look up this breakpoint site. If it exists, then add this new owner, otherwise
+ // create a new breakpoint site and add it.
+
+ bp_site_sp = m_breakpoint_site_list.FindByAddress (load_addr);
+
+ if (bp_site_sp)
+ {
+ bp_site_sp->AddOwner (owner);
+ owner->SetBreakpointSite (bp_site_sp);
+ return bp_site_sp->GetID();
+ }
+ else
+ {
+ bp_site_sp.reset (new BreakpointSite (&m_breakpoint_site_list, owner, load_addr, use_hardware));
+ if (bp_site_sp)
+ {
+ if (EnableBreakpointSite (bp_site_sp.get()).Success())
+ {
+ owner->SetBreakpointSite (bp_site_sp);
+ return m_breakpoint_site_list.Add (bp_site_sp);
+ }
+ }
+ }
+ }
+ // We failed to enable the breakpoint
+ return LLDB_INVALID_BREAK_ID;
+
+}
+
+void
+Process::RemoveOwnerFromBreakpointSite (lldb::user_id_t owner_id, lldb::user_id_t owner_loc_id, BreakpointSiteSP &bp_site_sp)
+{
+ uint32_t num_owners = bp_site_sp->RemoveOwner (owner_id, owner_loc_id);
+ if (num_owners == 0)
+ {
+ // Don't try to disable the site if we don't have a live process anymore.
+ if (IsAlive())
+ DisableBreakpointSite (bp_site_sp.get());
+ m_breakpoint_site_list.RemoveByAddress(bp_site_sp->GetLoadAddress());
+ }
+}
+
+
+size_t
+Process::RemoveBreakpointOpcodesFromBuffer (addr_t bp_addr, size_t size, uint8_t *buf) const
+{
+ size_t bytes_removed = 0;
+ BreakpointSiteList bp_sites_in_range;
+
+ if (m_breakpoint_site_list.FindInRange (bp_addr, bp_addr + size, bp_sites_in_range))
+ {
+ bp_sites_in_range.ForEach([bp_addr, size, buf, &bytes_removed](BreakpointSite *bp_site) -> void {
+ if (bp_site->GetType() == BreakpointSite::eSoftware)
+ {
+ addr_t intersect_addr;
+ size_t intersect_size;
+ size_t opcode_offset;
+ if (bp_site->IntersectsRange(bp_addr, size, &intersect_addr, &intersect_size, &opcode_offset))
+ {
+ assert(bp_addr <= intersect_addr && intersect_addr < bp_addr + size);
+ assert(bp_addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= bp_addr + size);
+ assert(opcode_offset + intersect_size <= bp_site->GetByteSize());
+ size_t buf_offset = intersect_addr - bp_addr;
+ ::memcpy(buf + buf_offset, bp_site->GetSavedOpcodeBytes() + opcode_offset, intersect_size);
+ }
+ }
+ });
+ }
+ return bytes_removed;
+}
+
+
+
+size_t
+Process::GetSoftwareBreakpointTrapOpcode (BreakpointSite* bp_site)
+{
+ PlatformSP platform_sp (m_target.GetPlatform());
+ if (platform_sp)
+ return platform_sp->GetSoftwareBreakpointTrapOpcode (m_target, bp_site);
+ return 0;
+}
+
+Error
+Process::EnableSoftwareBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ const addr_t bp_addr = bp_site->GetLoadAddress();
+ if (log)
+ log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64, bp_site->GetID(), (uint64_t)bp_addr);
+ if (bp_site->IsEnabled())
+ {
+ if (log)
+ log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already enabled", bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ }
+
+ if (bp_addr == LLDB_INVALID_ADDRESS)
+ {
+ error.SetErrorString("BreakpointSite contains an invalid load address.");
+ return error;
+ }
+ // Ask the lldb::Process subclass to fill in the correct software breakpoint
+ // trap for the breakpoint site
+ const size_t bp_opcode_size = GetSoftwareBreakpointTrapOpcode(bp_site);
+
+ if (bp_opcode_size == 0)
+ {
+ error.SetErrorStringWithFormat ("Process::GetSoftwareBreakpointTrapOpcode() returned zero, unable to get breakpoint trap for address 0x%" PRIx64, bp_addr);
+ }
+ else
+ {
+ const uint8_t * const bp_opcode_bytes = bp_site->GetTrapOpcodeBytes();
+
+ if (bp_opcode_bytes == NULL)
+ {
+ error.SetErrorString ("BreakpointSite doesn't contain a valid breakpoint trap opcode.");
+ return error;
+ }
+
+ // Save the original opcode by reading it
+ if (DoReadMemory(bp_addr, bp_site->GetSavedOpcodeBytes(), bp_opcode_size, error) == bp_opcode_size)
+ {
+ // Write a software breakpoint in place of the original opcode
+ if (DoWriteMemory(bp_addr, bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size)
+ {
+ uint8_t verify_bp_opcode_bytes[64];
+ if (DoReadMemory(bp_addr, verify_bp_opcode_bytes, bp_opcode_size, error) == bp_opcode_size)
+ {
+ if (::memcmp(bp_opcode_bytes, verify_bp_opcode_bytes, bp_opcode_size) == 0)
+ {
+ bp_site->SetEnabled(true);
+ bp_site->SetType (BreakpointSite::eSoftware);
+ if (log)
+ log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- SUCCESS",
+ bp_site->GetID(),
+ (uint64_t)bp_addr);
+ }
+ else
+ error.SetErrorString("failed to verify the breakpoint trap in memory.");
+ }
+ else
+ error.SetErrorString("Unable to read memory to verify breakpoint trap.");
+ }
+ else
+ error.SetErrorString("Unable to write breakpoint trap to memory.");
+ }
+ else
+ error.SetErrorString("Unable to read memory at breakpoint address.");
+ }
+ if (log && error.Fail())
+ log->Printf ("Process::EnableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s",
+ bp_site->GetID(),
+ (uint64_t)bp_addr,
+ error.AsCString());
+ return error;
+}
+
+Error
+Process::DisableSoftwareBreakpoint (BreakpointSite *bp_site)
+{
+ Error error;
+ assert (bp_site != NULL);
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ addr_t bp_addr = bp_site->GetLoadAddress();
+ lldb::user_id_t breakID = bp_site->GetID();
+ if (log)
+ log->Printf ("Process::DisableSoftwareBreakpoint (breakID = %" PRIu64 ") addr = 0x%" PRIx64, breakID, (uint64_t)bp_addr);
+
+ if (bp_site->IsHardware())
+ {
+ error.SetErrorString("Breakpoint site is a hardware breakpoint.");
+ }
+ else if (bp_site->IsEnabled())
+ {
+ const size_t break_op_size = bp_site->GetByteSize();
+ const uint8_t * const break_op = bp_site->GetTrapOpcodeBytes();
+ if (break_op_size > 0)
+ {
+ // Clear a software breakoint instruction
+ uint8_t curr_break_op[8];
+ assert (break_op_size <= sizeof(curr_break_op));
+ bool break_op_found = false;
+
+ // Read the breakpoint opcode
+ if (DoReadMemory (bp_addr, curr_break_op, break_op_size, error) == break_op_size)
+ {
+ bool verify = false;
+ // Make sure we have the a breakpoint opcode exists at this address
+ if (::memcmp (curr_break_op, break_op, break_op_size) == 0)
+ {
+ break_op_found = true;
+ // We found a valid breakpoint opcode at this address, now restore
+ // the saved opcode.
+ if (DoWriteMemory (bp_addr, bp_site->GetSavedOpcodeBytes(), break_op_size, error) == break_op_size)
+ {
+ verify = true;
+ }
+ else
+ error.SetErrorString("Memory write failed when restoring original opcode.");
+ }
+ else
+ {
+ error.SetErrorString("Original breakpoint trap is no longer in memory.");
+ // Set verify to true and so we can check if the original opcode has already been restored
+ verify = true;
+ }
+
+ if (verify)
+ {
+ uint8_t verify_opcode[8];
+ assert (break_op_size < sizeof(verify_opcode));
+ // Verify that our original opcode made it back to the inferior
+ if (DoReadMemory (bp_addr, verify_opcode, break_op_size, error) == break_op_size)
+ {
+ // compare the memory we just read with the original opcode
+ if (::memcmp (bp_site->GetSavedOpcodeBytes(), verify_opcode, break_op_size) == 0)
+ {
+ // SUCCESS
+ bp_site->SetEnabled(false);
+ if (log)
+ log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- SUCCESS", bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ }
+ else
+ {
+ if (break_op_found)
+ error.SetErrorString("Failed to restore original opcode.");
+ }
+ }
+ else
+ error.SetErrorString("Failed to read memory to verify that breakpoint trap was restored.");
+ }
+ }
+ else
+ error.SetErrorString("Unable to read memory that should contain the breakpoint trap.");
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- already disabled", bp_site->GetID(), (uint64_t)bp_addr);
+ return error;
+ }
+
+ if (log)
+ log->Printf ("Process::DisableSoftwareBreakpoint (site_id = %d) addr = 0x%" PRIx64 " -- FAILED: %s",
+ bp_site->GetID(),
+ (uint64_t)bp_addr,
+ error.AsCString());
+ return error;
+
+}
+
+// Uncomment to verify memory caching works after making changes to caching code
+//#define VERIFY_MEMORY_READS
+
+size_t
+Process::ReadMemory (addr_t addr, void *buf, size_t size, Error &error)
+{
+ if (!GetDisableMemoryCache())
+ {
+#if defined (VERIFY_MEMORY_READS)
+ // Memory caching is enabled, with debug verification
+
+ if (buf && size)
+ {
+ // Uncomment the line below to make sure memory caching is working.
+ // I ran this through the test suite and got no assertions, so I am
+ // pretty confident this is working well. If any changes are made to
+ // memory caching, uncomment the line below and test your changes!
+
+ // Verify all memory reads by using the cache first, then redundantly
+ // reading the same memory from the inferior and comparing to make sure
+ // everything is exactly the same.
+ std::string verify_buf (size, '\0');
+ assert (verify_buf.size() == size);
+ const size_t cache_bytes_read = m_memory_cache.Read (this, addr, buf, size, error);
+ Error verify_error;
+ const size_t verify_bytes_read = ReadMemoryFromInferior (addr, const_cast<char *>(verify_buf.data()), verify_buf.size(), verify_error);
+ assert (cache_bytes_read == verify_bytes_read);
+ assert (memcmp(buf, verify_buf.data(), verify_buf.size()) == 0);
+ assert (verify_error.Success() == error.Success());
+ return cache_bytes_read;
+ }
+ return 0;
+#else // !defined(VERIFY_MEMORY_READS)
+ // Memory caching is enabled, without debug verification
+
+ return m_memory_cache.Read (addr, buf, size, error);
+#endif // defined (VERIFY_MEMORY_READS)
+ }
+ else
+ {
+ // Memory caching is disabled
+
+ return ReadMemoryFromInferior (addr, buf, size, error);
+ }
+}
+
+size_t
+Process::ReadCStringFromMemory (addr_t addr, std::string &out_str, Error &error)
+{
+ char buf[256];
+ out_str.clear();
+ addr_t curr_addr = addr;
+ while (1)
+ {
+ size_t length = ReadCStringFromMemory (curr_addr, buf, sizeof(buf), error);
+ if (length == 0)
+ break;
+ out_str.append(buf, length);
+ // If we got "length - 1" bytes, we didn't get the whole C string, we
+ // need to read some more characters
+ if (length == sizeof(buf) - 1)
+ curr_addr += length;
+ else
+ break;
+ }
+ return out_str.size();
+}
+
+
+size_t
+Process::ReadStringFromMemory (addr_t addr, char *dst, size_t max_bytes, Error &error,
+ size_t type_width)
+{
+ size_t total_bytes_read = 0;
+ if (dst && max_bytes && type_width && max_bytes >= type_width)
+ {
+ // Ensure a null terminator independent of the number of bytes that is read.
+ memset (dst, 0, max_bytes);
+ size_t bytes_left = max_bytes - type_width;
+
+ const char terminator[4] = {'\0', '\0', '\0', '\0'};
+ assert(sizeof(terminator) >= type_width &&
+ "Attempting to validate a string with more than 4 bytes per character!");
+
+ addr_t curr_addr = addr;
+ const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
+ char *curr_dst = dst;
+
+ error.Clear();
+ while (bytes_left > 0 && error.Success())
+ {
+ addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read = std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read = ReadMemory (curr_addr, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0)
+ break;
+
+ // Search for a null terminator of correct size and alignment in bytes_read
+ size_t aligned_start = total_bytes_read - total_bytes_read % type_width;
+ for (size_t i = aligned_start; i + type_width <= total_bytes_read + bytes_read; i += type_width)
+ if (::strncmp(&dst[i], terminator, type_width) == 0)
+ {
+ error.Clear();
+ return i;
+ }
+
+ total_bytes_read += bytes_read;
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ }
+ }
+ else
+ {
+ if (max_bytes)
+ error.SetErrorString("invalid arguments");
+ }
+ return total_bytes_read;
+}
+
+// Deprecated in favor of ReadStringFromMemory which has wchar support and correct code to find
+// null terminators.
+size_t
+Process::ReadCStringFromMemory (addr_t addr, char *dst, size_t dst_max_len, Error &result_error)
+{
+ size_t total_cstr_len = 0;
+ if (dst && dst_max_len)
+ {
+ result_error.Clear();
+ // NULL out everything just to be safe
+ memset (dst, 0, dst_max_len);
+ Error error;
+ addr_t curr_addr = addr;
+ const size_t cache_line_size = m_memory_cache.GetMemoryCacheLineSize();
+ size_t bytes_left = dst_max_len - 1;
+ char *curr_dst = dst;
+
+ while (bytes_left > 0)
+ {
+ addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read = std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read = ReadMemory (curr_addr, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0)
+ {
+ result_error = error;
+ dst[total_cstr_len] = '\0';
+ break;
+ }
+ const size_t len = strlen(curr_dst);
+
+ total_cstr_len += len;
+
+ if (len < bytes_to_read)
+ break;
+
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ }
+ }
+ else
+ {
+ if (dst == NULL)
+ result_error.SetErrorString("invalid arguments");
+ else
+ result_error.Clear();
+ }
+ return total_cstr_len;
+}
+
+size_t
+Process::ReadMemoryFromInferior (addr_t addr, void *buf, size_t size, Error &error)
+{
+ if (buf == NULL || size == 0)
+ return 0;
+
+ size_t bytes_read = 0;
+ uint8_t *bytes = (uint8_t *)buf;
+
+ while (bytes_read < size)
+ {
+ const size_t curr_size = size - bytes_read;
+ const size_t curr_bytes_read = DoReadMemory (addr + bytes_read,
+ bytes + bytes_read,
+ curr_size,
+ error);
+ bytes_read += curr_bytes_read;
+ if (curr_bytes_read == curr_size || curr_bytes_read == 0)
+ break;
+ }
+
+ // Replace any software breakpoint opcodes that fall into this range back
+ // into "buf" before we return
+ if (bytes_read > 0)
+ RemoveBreakpointOpcodesFromBuffer (addr, bytes_read, (uint8_t *)buf);
+ return bytes_read;
+}
+
+uint64_t
+Process::ReadUnsignedIntegerFromMemory (lldb::addr_t vm_addr, size_t integer_byte_size, uint64_t fail_value, Error &error)
+{
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(vm_addr, integer_byte_size, false, scalar, error))
+ return scalar.ULongLong(fail_value);
+ return fail_value;
+}
+
+addr_t
+Process::ReadPointerFromMemory (lldb::addr_t vm_addr, Error &error)
+{
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory(vm_addr, GetAddressByteSize(), false, scalar, error))
+ return scalar.ULongLong(LLDB_INVALID_ADDRESS);
+ return LLDB_INVALID_ADDRESS;
+}
+
+
+bool
+Process::WritePointerToMemory (lldb::addr_t vm_addr,
+ lldb::addr_t ptr_value,
+ Error &error)
+{
+ Scalar scalar;
+ const uint32_t addr_byte_size = GetAddressByteSize();
+ if (addr_byte_size <= 4)
+ scalar = (uint32_t)ptr_value;
+ else
+ scalar = ptr_value;
+ return WriteScalarToMemory(vm_addr, scalar, addr_byte_size, error) == addr_byte_size;
+}
+
+size_t
+Process::WriteMemoryPrivate (addr_t addr, const void *buf, size_t size, Error &error)
+{
+ size_t bytes_written = 0;
+ const uint8_t *bytes = (const uint8_t *)buf;
+
+ while (bytes_written < size)
+ {
+ const size_t curr_size = size - bytes_written;
+ const size_t curr_bytes_written = DoWriteMemory (addr + bytes_written,
+ bytes + bytes_written,
+ curr_size,
+ error);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written == curr_size || curr_bytes_written == 0)
+ break;
+ }
+ return bytes_written;
+}
+
+size_t
+Process::WriteMemory (addr_t addr, const void *buf, size_t size, Error &error)
+{
+#if defined (ENABLE_MEMORY_CACHING)
+ m_memory_cache.Flush (addr, size);
+#endif
+
+ if (buf == NULL || size == 0)
+ return 0;
+
+ m_mod_id.BumpMemoryID();
+
+ // We need to write any data that would go where any current software traps
+ // (enabled software breakpoints) any software traps (breakpoints) that we
+ // may have placed in our tasks memory.
+
+ BreakpointSiteList bp_sites_in_range;
+
+ if (m_breakpoint_site_list.FindInRange (addr, addr + size, bp_sites_in_range))
+ {
+ // No breakpoint sites overlap
+ if (bp_sites_in_range.IsEmpty())
+ return WriteMemoryPrivate (addr, buf, size, error);
+ else
+ {
+ const uint8_t *ubuf = (const uint8_t *)buf;
+ uint64_t bytes_written = 0;
+
+ bp_sites_in_range.ForEach([this, addr, size, &bytes_written, &ubuf, &error](BreakpointSite *bp) -> void {
+
+ if (error.Success())
+ {
+ addr_t intersect_addr;
+ size_t intersect_size;
+ size_t opcode_offset;
+ const bool intersects = bp->IntersectsRange(addr, size, &intersect_addr, &intersect_size, &opcode_offset);
+ assert(intersects);
+ assert(addr <= intersect_addr && intersect_addr < addr + size);
+ assert(addr < intersect_addr + intersect_size && intersect_addr + intersect_size <= addr + size);
+ assert(opcode_offset + intersect_size <= bp->GetByteSize());
+
+ // Check for bytes before this breakpoint
+ const addr_t curr_addr = addr + bytes_written;
+ if (intersect_addr > curr_addr)
+ {
+ // There are some bytes before this breakpoint that we need to
+ // just write to memory
+ size_t curr_size = intersect_addr - curr_addr;
+ size_t curr_bytes_written = WriteMemoryPrivate (curr_addr,
+ ubuf + bytes_written,
+ curr_size,
+ error);
+ bytes_written += curr_bytes_written;
+ if (curr_bytes_written != curr_size)
+ {
+ // We weren't able to write all of the requested bytes, we
+ // are done looping and will return the number of bytes that
+ // we have written so far.
+ if (error.Success())
+ error.SetErrorToGenericError();
+ }
+ }
+ // Now write any bytes that would cover up any software breakpoints
+ // directly into the breakpoint opcode buffer
+ ::memcpy(bp->GetSavedOpcodeBytes() + opcode_offset, ubuf + bytes_written, intersect_size);
+ bytes_written += intersect_size;
+ }
+ });
+
+ if (bytes_written < size)
+ bytes_written += WriteMemoryPrivate (addr + bytes_written,
+ ubuf + bytes_written,
+ size - bytes_written,
+ error);
+ }
+ }
+ else
+ {
+ return WriteMemoryPrivate (addr, buf, size, error);
+ }
+
+ // Write any remaining bytes after the last breakpoint if we have any left
+ return 0; //bytes_written;
+}
+
+size_t
+Process::WriteScalarToMemory (addr_t addr, const Scalar &scalar, size_t byte_size, Error &error)
+{
+ if (byte_size == UINT32_MAX)
+ byte_size = scalar.GetByteSize();
+ if (byte_size > 0)
+ {
+ uint8_t buf[32];
+ const size_t mem_size = scalar.GetAsMemoryData (buf, byte_size, GetByteOrder(), error);
+ if (mem_size > 0)
+ return WriteMemory(addr, buf, mem_size, error);
+ else
+ error.SetErrorString ("failed to get scalar as memory data");
+ }
+ else
+ {
+ error.SetErrorString ("invalid scalar value");
+ }
+ return 0;
+}
+
+size_t
+Process::ReadScalarIntegerFromMemory (addr_t addr,
+ uint32_t byte_size,
+ bool is_signed,
+ Scalar &scalar,
+ Error &error)
+{
+ uint64_t uval = 0;
+ if (byte_size == 0)
+ {
+ error.SetErrorString ("byte size is zero");
+ }
+ else if (byte_size & (byte_size - 1))
+ {
+ error.SetErrorStringWithFormat ("byte size %u is not a power of 2", byte_size);
+ }
+ else if (byte_size <= sizeof(uval))
+ {
+ const size_t bytes_read = ReadMemory (addr, &uval, byte_size, error);
+ if (bytes_read == byte_size)
+ {
+ DataExtractor data (&uval, sizeof(uval), GetByteOrder(), GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ if (byte_size <= 4)
+ scalar = data.GetMaxU32 (&offset, byte_size);
+ else
+ scalar = data.GetMaxU64 (&offset, byte_size);
+ if (is_signed)
+ scalar.SignExtend(byte_size * 8);
+ return bytes_read;
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("byte size of %u is too large for integer scalar type", byte_size);
+ }
+ return 0;
+}
+
+#define USE_ALLOCATE_MEMORY_CACHE 1
+addr_t
+Process::AllocateMemory(size_t size, uint32_t permissions, Error &error)
+{
+ if (GetPrivateState() != eStateStopped)
+ return LLDB_INVALID_ADDRESS;
+
+#if defined (USE_ALLOCATE_MEMORY_CACHE)
+ return m_allocated_memory_cache.AllocateMemory(size, permissions, error);
+#else
+ addr_t allocated_addr = DoAllocateMemory (size, permissions, error);
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::AllocateMemory(size=%4zu, permissions=%s) => 0x%16.16" PRIx64 " (m_stop_id = %u m_memory_id = %u)",
+ size,
+ GetPermissionsAsCString (permissions),
+ (uint64_t)allocated_addr,
+ m_mod_id.GetStopID(),
+ m_mod_id.GetMemoryID());
+ return allocated_addr;
+#endif
+}
+
+bool
+Process::CanJIT ()
+{
+ if (m_can_jit == eCanJITDontKnow)
+ {
+ Error err;
+
+ uint64_t allocated_memory = AllocateMemory(8,
+ ePermissionsReadable | ePermissionsWritable | ePermissionsExecutable,
+ err);
+
+ if (err.Success())
+ m_can_jit = eCanJITYes;
+ else
+ m_can_jit = eCanJITNo;
+
+ DeallocateMemory (allocated_memory);
+ }
+
+ return m_can_jit == eCanJITYes;
+}
+
+void
+Process::SetCanJIT (bool can_jit)
+{
+ m_can_jit = (can_jit ? eCanJITYes : eCanJITNo);
+}
+
+Error
+Process::DeallocateMemory (addr_t ptr)
+{
+ Error error;
+#if defined (USE_ALLOCATE_MEMORY_CACHE)
+ if (!m_allocated_memory_cache.DeallocateMemory(ptr))
+ {
+ error.SetErrorStringWithFormat ("deallocation of memory at 0x%" PRIx64 " failed.", (uint64_t)ptr);
+ }
+#else
+ error = DoDeallocateMemory (ptr);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::DeallocateMemory(addr=0x%16.16" PRIx64 ") => err = %s (m_stop_id = %u, m_memory_id = %u)",
+ ptr,
+ error.AsCString("SUCCESS"),
+ m_mod_id.GetStopID(),
+ m_mod_id.GetMemoryID());
+#endif
+ return error;
+}
+
+
+ModuleSP
+Process::ReadModuleFromMemory (const FileSpec& file_spec,
+ lldb::addr_t header_addr)
+{
+ ModuleSP module_sp (new Module (file_spec, ArchSpec()));
+ if (module_sp)
+ {
+ Error error;
+ ObjectFile *objfile = module_sp->GetMemoryObjectFile (shared_from_this(), header_addr, error);
+ if (objfile)
+ return module_sp;
+ }
+ return ModuleSP();
+}
+
+Error
+Process::EnableWatchpoint (Watchpoint *watchpoint, bool notify)
+{
+ Error error;
+ error.SetErrorString("watchpoints are not supported");
+ return error;
+}
+
+Error
+Process::DisableWatchpoint (Watchpoint *watchpoint, bool notify)
+{
+ Error error;
+ error.SetErrorString("watchpoints are not supported");
+ return error;
+}
+
+StateType
+Process::WaitForProcessStopPrivate (const TimeValue *timeout, EventSP &event_sp)
+{
+ StateType state;
+ // Now wait for the process to launch and return control to us, and then
+ // call DidLaunch:
+ while (1)
+ {
+ event_sp.reset();
+ state = WaitForStateChangedEventsPrivate (timeout, event_sp);
+
+ if (StateIsStoppedState(state, false))
+ break;
+
+ // If state is invalid, then we timed out
+ if (state == eStateInvalid)
+ break;
+
+ if (event_sp)
+ HandlePrivateEvent (event_sp);
+ }
+ return state;
+}
+
+Error
+Process::Launch (const ProcessLaunchInfo &launch_info)
+{
+ Error error;
+ m_abi_sp.reset();
+ m_dyld_ap.reset();
+ m_os_ap.reset();
+ m_process_input_reader.reset();
+
+ Module *exe_module = m_target.GetExecutableModulePointer();
+ if (exe_module)
+ {
+ char local_exec_file_path[PATH_MAX];
+ char platform_exec_file_path[PATH_MAX];
+ exe_module->GetFileSpec().GetPath(local_exec_file_path, sizeof(local_exec_file_path));
+ exe_module->GetPlatformFileSpec().GetPath(platform_exec_file_path, sizeof(platform_exec_file_path));
+ if (exe_module->GetFileSpec().Exists())
+ {
+ if (PrivateStateThreadIsValid ())
+ PausePrivateStateThread ();
+
+ error = WillLaunch (exe_module);
+ if (error.Success())
+ {
+ const bool restarted = false;
+ SetPublicState (eStateLaunching, restarted);
+ m_should_detach = false;
+
+ if (m_public_run_lock.TrySetRunning())
+ {
+ // Now launch using these arguments.
+ error = DoLaunch (exe_module, launch_info);
+ }
+ else
+ {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Fail())
+ {
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ SetID (LLDB_INVALID_PROCESS_ID);
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "launch failed";
+ SetExitStatus (-1, error_string);
+ }
+ }
+ else
+ {
+ EventSP event_sp;
+ TimeValue timeout_time;
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(10);
+ StateType state = WaitForProcessStopPrivate(&timeout_time, event_sp);
+
+ if (state == eStateInvalid || event_sp.get() == NULL)
+ {
+ // We were able to launch the process, but we failed to
+ // catch the initial stop.
+ SetExitStatus (0, "failed to catch stop after launch");
+ Destroy();
+ }
+ else if (state == eStateStopped || state == eStateCrashed)
+ {
+
+ DidLaunch ();
+
+ DynamicLoader *dyld = GetDynamicLoader ();
+ if (dyld)
+ dyld->DidLaunch();
+
+ m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL));
+ // This delays passing the stopped event to listeners till DidLaunch gets
+ // a chance to complete...
+ HandlePrivateEvent (event_sp);
+
+ if (PrivateStateThreadIsValid ())
+ ResumePrivateStateThread ();
+ else
+ StartPrivateStateThread ();
+ }
+ else if (state == eStateExited)
+ {
+ // We exited while trying to launch somehow. Don't call DidLaunch as that's
+ // not likely to work, and return an invalid pid.
+ HandlePrivateEvent (event_sp);
+ }
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("file doesn't exist: '%s'", local_exec_file_path);
+ }
+ }
+ return error;
+}
+
+
+Error
+Process::LoadCore ()
+{
+ Error error = DoLoadCore();
+ if (error.Success())
+ {
+ if (PrivateStateThreadIsValid ())
+ ResumePrivateStateThread ();
+ else
+ StartPrivateStateThread ();
+
+ DynamicLoader *dyld = GetDynamicLoader ();
+ if (dyld)
+ dyld->DidAttach();
+
+ m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL));
+ // We successfully loaded a core file, now pretend we stopped so we can
+ // show all of the threads in the core file and explore the crashed
+ // state.
+ SetPrivateState (eStateStopped);
+
+ }
+ return error;
+}
+
+DynamicLoader *
+Process::GetDynamicLoader ()
+{
+ if (m_dyld_ap.get() == NULL)
+ m_dyld_ap.reset (DynamicLoader::FindPlugin(this, NULL));
+ return m_dyld_ap.get();
+}
+
+
+Process::NextEventAction::EventActionResult
+Process::AttachCompletionHandler::PerformAction (lldb::EventSP &event_sp)
+{
+ StateType state = ProcessEventData::GetStateFromEvent (event_sp.get());
+ switch (state)
+ {
+ case eStateRunning:
+ case eStateConnected:
+ return eEventActionRetry;
+
+ case eStateStopped:
+ case eStateCrashed:
+ {
+ // During attach, prior to sending the eStateStopped event,
+ // lldb_private::Process subclasses must set the new process ID.
+ assert (m_process->GetID() != LLDB_INVALID_PROCESS_ID);
+ // We don't want these events to be reported, so go set the ShouldReportStop here:
+ m_process->GetThreadList().SetShouldReportStop (eVoteNo);
+
+ if (m_exec_count > 0)
+ {
+ --m_exec_count;
+ RequestResume();
+ return eEventActionRetry;
+ }
+ else
+ {
+ m_process->CompleteAttach ();
+ return eEventActionSuccess;
+ }
+ }
+ break;
+
+ default:
+ case eStateExited:
+ case eStateInvalid:
+ break;
+ }
+
+ m_exit_string.assign ("No valid Process");
+ return eEventActionExit;
+}
+
+Process::NextEventAction::EventActionResult
+Process::AttachCompletionHandler::HandleBeingInterrupted()
+{
+ return eEventActionSuccess;
+}
+
+const char *
+Process::AttachCompletionHandler::GetExitString ()
+{
+ return m_exit_string.c_str();
+}
+
+Error
+Process::Attach (ProcessAttachInfo &attach_info)
+{
+ m_abi_sp.reset();
+ m_process_input_reader.reset();
+ m_dyld_ap.reset();
+ m_os_ap.reset();
+
+ lldb::pid_t attach_pid = attach_info.GetProcessID();
+ Error error;
+ if (attach_pid == LLDB_INVALID_PROCESS_ID)
+ {
+ char process_name[PATH_MAX];
+
+ if (attach_info.GetExecutableFile().GetPath (process_name, sizeof(process_name)))
+ {
+ const bool wait_for_launch = attach_info.GetWaitForLaunch();
+
+ if (wait_for_launch)
+ {
+ error = WillAttachToProcessWithName(process_name, wait_for_launch);
+ if (error.Success())
+ {
+ if (m_public_run_lock.TrySetRunning())
+ {
+ m_should_detach = true;
+ const bool restarted = false;
+ SetPublicState (eStateAttaching, restarted);
+ // Now attach using these arguments.
+ error = DoAttachToProcessWithName (process_name, wait_for_launch, attach_info);
+ }
+ else
+ {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Fail())
+ {
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ SetID (LLDB_INVALID_PROCESS_ID);
+ if (error.AsCString() == NULL)
+ error.SetErrorString("attach failed");
+
+ SetExitStatus(-1, error.AsCString());
+ }
+ }
+ else
+ {
+ SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount()));
+ StartPrivateStateThread();
+ }
+ return error;
+ }
+ }
+ else
+ {
+ ProcessInstanceInfoList process_infos;
+ PlatformSP platform_sp (m_target.GetPlatform ());
+
+ if (platform_sp)
+ {
+ ProcessInstanceInfoMatch match_info;
+ match_info.GetProcessInfo() = attach_info;
+ match_info.SetNameMatchType (eNameMatchEquals);
+ platform_sp->FindProcesses (match_info, process_infos);
+ const uint32_t num_matches = process_infos.GetSize();
+ if (num_matches == 1)
+ {
+ attach_pid = process_infos.GetProcessIDAtIndex(0);
+ // Fall through and attach using the above process ID
+ }
+ else
+ {
+ match_info.GetProcessInfo().GetExecutableFile().GetPath (process_name, sizeof(process_name));
+ if (num_matches > 1)
+ error.SetErrorStringWithFormat ("more than one process named %s", process_name);
+ else
+ error.SetErrorStringWithFormat ("could not find a process named %s", process_name);
+ }
+ }
+ else
+ {
+ error.SetErrorString ("invalid platform, can't find processes by name");
+ return error;
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorString ("invalid process name");
+ }
+ }
+
+ if (attach_pid != LLDB_INVALID_PROCESS_ID)
+ {
+ error = WillAttachToProcessWithID(attach_pid);
+ if (error.Success())
+ {
+
+ if (m_public_run_lock.TrySetRunning())
+ {
+ // Now attach using these arguments.
+ m_should_detach = true;
+ const bool restarted = false;
+ SetPublicState (eStateAttaching, restarted);
+ error = DoAttachToProcessWithID (attach_pid, attach_info);
+ }
+ else
+ {
+ // This shouldn't happen
+ error.SetErrorString("failed to acquire process run lock");
+ }
+
+ if (error.Success())
+ {
+
+ SetNextEventAction(new Process::AttachCompletionHandler(this, attach_info.GetResumeCount()));
+ StartPrivateStateThread();
+ }
+ else
+ {
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ SetID (LLDB_INVALID_PROCESS_ID);
+ const char *error_string = error.AsCString();
+ if (error_string == NULL)
+ error_string = "attach failed";
+
+ SetExitStatus(-1, error_string);
+ }
+ }
+ }
+ }
+ return error;
+}
+
+void
+Process::CompleteAttach ()
+{
+ // Let the process subclass figure out at much as it can about the process
+ // before we go looking for a dynamic loader plug-in.
+ DidAttach();
+
+ // We just attached. If we have a platform, ask it for the process architecture, and if it isn't
+ // the same as the one we've already set, switch architectures.
+ PlatformSP platform_sp (m_target.GetPlatform ());
+ assert (platform_sp.get());
+ if (platform_sp)
+ {
+ const ArchSpec &target_arch = m_target.GetArchitecture();
+ if (target_arch.IsValid() && !platform_sp->IsCompatibleArchitecture (target_arch, false, NULL))
+ {
+ ArchSpec platform_arch;
+ platform_sp = platform_sp->GetPlatformForArchitecture (target_arch, &platform_arch);
+ if (platform_sp)
+ {
+ m_target.SetPlatform (platform_sp);
+ m_target.SetArchitecture(platform_arch);
+ }
+ }
+ else
+ {
+ ProcessInstanceInfo process_info;
+ platform_sp->GetProcessInfo (GetID(), process_info);
+ const ArchSpec &process_arch = process_info.GetArchitecture();
+ if (process_arch.IsValid() && !m_target.GetArchitecture().IsExactMatch(process_arch))
+ m_target.SetArchitecture (process_arch);
+ }
+ }
+
+ // We have completed the attach, now it is time to find the dynamic loader
+ // plug-in
+ DynamicLoader *dyld = GetDynamicLoader ();
+ if (dyld)
+ dyld->DidAttach();
+
+ m_os_ap.reset (OperatingSystem::FindPlugin (this, NULL));
+ // Figure out which one is the executable, and set that in our target:
+ const ModuleList &target_modules = m_target.GetImages();
+ Mutex::Locker modules_locker(target_modules.GetMutex());
+ size_t num_modules = target_modules.GetSize();
+ ModuleSP new_executable_module_sp;
+
+ for (size_t i = 0; i < num_modules; i++)
+ {
+ ModuleSP module_sp (target_modules.GetModuleAtIndexUnlocked (i));
+ if (module_sp && module_sp->IsExecutable())
+ {
+ if (m_target.GetExecutableModulePointer() != module_sp.get())
+ new_executable_module_sp = module_sp;
+ break;
+ }
+ }
+ if (new_executable_module_sp)
+ m_target.SetExecutableModule (new_executable_module_sp, false);
+}
+
+Error
+Process::ConnectRemote (Stream *strm, const char *remote_url)
+{
+ m_abi_sp.reset();
+ m_process_input_reader.reset();
+
+ // Find the process and its architecture. Make sure it matches the architecture
+ // of the current Target, and if not adjust it.
+
+ Error error (DoConnectRemote (strm, remote_url));
+ if (error.Success())
+ {
+ if (GetID() != LLDB_INVALID_PROCESS_ID)
+ {
+ EventSP event_sp;
+ StateType state = WaitForProcessStopPrivate(NULL, event_sp);
+
+ if (state == eStateStopped || state == eStateCrashed)
+ {
+ // If we attached and actually have a process on the other end, then
+ // this ended up being the equivalent of an attach.
+ CompleteAttach ();
+
+ // This delays passing the stopped event to listeners till
+ // CompleteAttach gets a chance to complete...
+ HandlePrivateEvent (event_sp);
+
+ }
+ }
+
+ if (PrivateStateThreadIsValid ())
+ ResumePrivateStateThread ();
+ else
+ StartPrivateStateThread ();
+ }
+ return error;
+}
+
+
+Error
+Process::PrivateResume ()
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_PROCESS|LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Process::PrivateResume() m_stop_id = %u, public state: %s private state: %s",
+ m_mod_id.GetStopID(),
+ StateAsCString(m_public_state.GetValue()),
+ StateAsCString(m_private_state.GetValue()));
+
+ Error error (WillResume());
+ // Tell the process it is about to resume before the thread list
+ if (error.Success())
+ {
+ // Now let the thread list know we are about to resume so it
+ // can let all of our threads know that they are about to be
+ // resumed. Threads will each be called with
+ // Thread::WillResume(StateType) where StateType contains the state
+ // that they are supposed to have when the process is resumed
+ // (suspended/running/stepping). Threads should also check
+ // their resume signal in lldb::Thread::GetResumeSignal()
+ // to see if they are supposed to start back up with a signal.
+ if (m_thread_list.WillResume())
+ {
+ // Last thing, do the PreResumeActions.
+ if (!RunPreResumeActions())
+ {
+ error.SetErrorStringWithFormat ("Process::PrivateResume PreResumeActions failed, not resuming.");
+ }
+ else
+ {
+ m_mod_id.BumpResumeID();
+ error = DoResume();
+ if (error.Success())
+ {
+ DidResume();
+ m_thread_list.DidResume();
+ if (log)
+ log->Printf ("Process thinks the process has resumed.");
+ }
+ }
+ }
+ else
+ {
+ // Somebody wanted to run without running. So generate a continue & a stopped event,
+ // and let the world handle them.
+ if (log)
+ log->Printf ("Process::PrivateResume() asked to simulate a start & stop.");
+
+ SetPrivateState(eStateRunning);
+ SetPrivateState(eStateStopped);
+ }
+ }
+ else if (log)
+ log->Printf ("Process::PrivateResume() got an error \"%s\".", error.AsCString("<unknown error>"));
+ return error;
+}
+
+Error
+Process::Halt (bool clear_thread_plans)
+{
+ // Don't clear the m_clear_thread_plans_on_stop, only set it to true if
+ // in case it was already set and some thread plan logic calls halt on its
+ // own.
+ m_clear_thread_plans_on_stop |= clear_thread_plans;
+
+ // First make sure we aren't in the middle of handling an event, or we might restart. This is pretty weak, since
+ // we could just straightaway get another event. It just narrows the window...
+ m_currently_handling_event.WaitForValueEqualTo(false);
+
+
+ // Pause our private state thread so we can ensure no one else eats
+ // the stop event out from under us.
+ Listener halt_listener ("lldb.process.halt_listener");
+ HijackPrivateProcessEvents(&halt_listener);
+
+ EventSP event_sp;
+ Error error (WillHalt());
+
+ if (error.Success())
+ {
+
+ bool caused_stop = false;
+
+ // Ask the process subclass to actually halt our process
+ error = DoHalt(caused_stop);
+ if (error.Success())
+ {
+ if (m_public_state.GetValue() == eStateAttaching)
+ {
+ SetExitStatus(SIGKILL, "Cancelled async attach.");
+ Destroy ();
+ }
+ else
+ {
+ // If "caused_stop" is true, then DoHalt stopped the process. If
+ // "caused_stop" is false, the process was already stopped.
+ // If the DoHalt caused the process to stop, then we want to catch
+ // this event and set the interrupted bool to true before we pass
+ // this along so clients know that the process was interrupted by
+ // a halt command.
+ if (caused_stop)
+ {
+ // Wait for 1 second for the process to stop.
+ TimeValue timeout_time;
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(1);
+ bool got_event = halt_listener.WaitForEvent (&timeout_time, event_sp);
+ StateType state = ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ if (!got_event || state == eStateInvalid)
+ {
+ // We timeout out and didn't get a stop event...
+ error.SetErrorStringWithFormat ("Halt timed out. State = %s", StateAsCString(GetState()));
+ }
+ else
+ {
+ if (StateIsStoppedState (state, false))
+ {
+ // We caused the process to interrupt itself, so mark this
+ // as such in the stop event so clients can tell an interrupted
+ // process from a natural stop
+ ProcessEventData::SetInterruptedInEvent (event_sp.get(), true);
+ }
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::Halt() failed to stop, state is: %s", StateAsCString(state));
+ error.SetErrorString ("Did not get stopped event after halt.");
+ }
+ }
+ }
+ DidHalt();
+ }
+ }
+ }
+ // Resume our private state thread before we post the event (if any)
+ RestorePrivateProcessEvents();
+
+ // Post any event we might have consumed. If all goes well, we will have
+ // stopped the process, intercepted the event and set the interrupted
+ // bool in the event. Post it to the private event queue and that will end up
+ // correctly setting the state.
+ if (event_sp)
+ m_private_state_broadcaster.BroadcastEvent(event_sp);
+
+ return error;
+}
+
+Error
+Process::HaltForDestroyOrDetach(lldb::EventSP &exit_event_sp)
+{
+ Error error;
+ if (m_public_state.GetValue() == eStateRunning)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Process::Destroy() About to halt.");
+ error = Halt();
+ if (error.Success())
+ {
+ // Consume the halt event.
+ TimeValue timeout (TimeValue::Now());
+ timeout.OffsetWithSeconds(1);
+ StateType state = WaitForProcessToStop (&timeout, &exit_event_sp);
+
+ // If the process exited while we were waiting for it to stop, put the exited event into
+ // the shared pointer passed in and return. Our caller doesn't need to do anything else, since
+ // they don't have a process anymore...
+
+ if (state == eStateExited || m_private_state.GetValue() == eStateExited)
+ {
+ if (log)
+ log->Printf("Process::HaltForDestroyOrDetach() Process exited while waiting to Halt.");
+ return error;
+ }
+ else
+ exit_event_sp.reset(); // It is ok to consume any non-exit stop events
+
+ if (state != eStateStopped)
+ {
+ if (log)
+ log->Printf("Process::HaltForDestroyOrDetach() Halt failed to stop, state is: %s", StateAsCString(state));
+ // If we really couldn't stop the process then we should just error out here, but if the
+ // lower levels just bobbled sending the event and we really are stopped, then continue on.
+ StateType private_state = m_private_state.GetValue();
+ if (private_state != eStateStopped)
+ {
+ return error;
+ }
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("Process::HaltForDestroyOrDetach() Halt got error: %s", error.AsCString());
+ }
+ }
+ return error;
+}
+
+Error
+Process::Detach (bool keep_stopped)
+{
+ EventSP exit_event_sp;
+ Error error;
+ m_destroy_in_process = true;
+
+ error = WillDetach();
+
+ if (error.Success())
+ {
+ if (DetachRequiresHalt())
+ {
+ error = HaltForDestroyOrDetach (exit_event_sp);
+ if (!error.Success())
+ {
+ m_destroy_in_process = false;
+ return error;
+ }
+ else if (exit_event_sp)
+ {
+ // We shouldn't need to do anything else here. There's no process left to detach from...
+ StopPrivateStateThread();
+ m_destroy_in_process = false;
+ return error;
+ }
+ }
+
+ error = DoDetach(keep_stopped);
+ if (error.Success())
+ {
+ DidDetach();
+ StopPrivateStateThread();
+ }
+ else
+ {
+ return error;
+ }
+ }
+ m_destroy_in_process = false;
+
+ // If we exited when we were waiting for a process to stop, then
+ // forward the event here so we don't lose the event
+ if (exit_event_sp)
+ {
+ // Directly broadcast our exited event because we shut down our
+ // private state thread above
+ BroadcastEvent(exit_event_sp);
+ }
+
+ // If we have been interrupted (to kill us) in the middle of running, we may not end up propagating
+ // the last events through the event system, in which case we might strand the write lock. Unlock
+ // it here so when we do to tear down the process we don't get an error destroying the lock.
+
+ m_public_run_lock.SetStopped();
+ return error;
+}
+
+Error
+Process::Destroy ()
+{
+
+ // Tell ourselves we are in the process of destroying the process, so that we don't do any unnecessary work
+ // that might hinder the destruction. Remember to set this back to false when we are done. That way if the attempt
+ // failed and the process stays around for some reason it won't be in a confused state.
+
+ m_destroy_in_process = true;
+
+ Error error (WillDestroy());
+ if (error.Success())
+ {
+ EventSP exit_event_sp;
+ if (DestroyRequiresHalt())
+ {
+ error = HaltForDestroyOrDetach(exit_event_sp);
+ }
+
+ if (m_public_state.GetValue() != eStateRunning)
+ {
+ // Ditch all thread plans, and remove all our breakpoints: in case we have to restart the target to
+ // kill it, we don't want it hitting a breakpoint...
+ // Only do this if we've stopped, however, since if we didn't manage to halt it above, then
+ // we're not going to have much luck doing this now.
+ m_thread_list.DiscardThreadPlans();
+ DisableAllBreakpointSites();
+ }
+
+ error = DoDestroy();
+ if (error.Success())
+ {
+ DidDestroy();
+ StopPrivateStateThread();
+ }
+ m_stdio_communication.StopReadThread();
+ m_stdio_communication.Disconnect();
+ if (m_process_input_reader && m_process_input_reader->IsActive())
+ m_target.GetDebugger().PopInputReader (m_process_input_reader);
+ if (m_process_input_reader)
+ m_process_input_reader.reset();
+
+ // If we exited when we were waiting for a process to stop, then
+ // forward the event here so we don't lose the event
+ if (exit_event_sp)
+ {
+ // Directly broadcast our exited event because we shut down our
+ // private state thread above
+ BroadcastEvent(exit_event_sp);
+ }
+
+ // If we have been interrupted (to kill us) in the middle of running, we may not end up propagating
+ // the last events through the event system, in which case we might strand the write lock. Unlock
+ // it here so when we do to tear down the process we don't get an error destroying the lock.
+ m_public_run_lock.SetStopped();
+ }
+
+ m_destroy_in_process = false;
+
+ return error;
+}
+
+Error
+Process::Signal (int signal)
+{
+ Error error (WillSignal());
+ if (error.Success())
+ {
+ error = DoSignal(signal);
+ if (error.Success())
+ DidSignal();
+ }
+ return error;
+}
+
+lldb::ByteOrder
+Process::GetByteOrder () const
+{
+ return m_target.GetArchitecture().GetByteOrder();
+}
+
+uint32_t
+Process::GetAddressByteSize () const
+{
+ return m_target.GetArchitecture().GetAddressByteSize();
+}
+
+
+bool
+Process::ShouldBroadcastEvent (Event *event_ptr)
+{
+ const StateType state = Process::ProcessEventData::GetStateFromEvent (event_ptr);
+ bool return_value = true;
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_EVENTS | LIBLLDB_LOG_PROCESS));
+
+ switch (state)
+ {
+ case eStateConnected:
+ case eStateAttaching:
+ case eStateLaunching:
+ case eStateDetached:
+ case eStateExited:
+ case eStateUnloaded:
+ // These events indicate changes in the state of the debugging session, always report them.
+ return_value = true;
+ break;
+ case eStateInvalid:
+ // We stopped for no apparent reason, don't report it.
+ return_value = false;
+ break;
+ case eStateRunning:
+ case eStateStepping:
+ // If we've started the target running, we handle the cases where we
+ // are already running and where there is a transition from stopped to
+ // running differently.
+ // running -> running: Automatically suppress extra running events
+ // stopped -> running: Report except when there is one or more no votes
+ // and no yes votes.
+ SynchronouslyNotifyStateChanged (state);
+ switch (m_last_broadcast_state)
+ {
+ case eStateRunning:
+ case eStateStepping:
+ // We always suppress multiple runnings with no PUBLIC stop in between.
+ return_value = false;
+ break;
+ default:
+ // TODO: make this work correctly. For now always report
+ // run if we aren't running so we don't miss any runnning
+ // events. If I run the lldb/test/thread/a.out file and
+ // break at main.cpp:58, run and hit the breakpoints on
+ // multiple threads, then somehow during the stepping over
+ // of all breakpoints no run gets reported.
+
+ // This is a transition from stop to run.
+ switch (m_thread_list.ShouldReportRun (event_ptr))
+ {
+ case eVoteYes:
+ case eVoteNoOpinion:
+ return_value = true;
+ break;
+ case eVoteNo:
+ return_value = false;
+ break;
+ }
+ break;
+ }
+ break;
+ case eStateStopped:
+ case eStateCrashed:
+ case eStateSuspended:
+ {
+ // We've stopped. First see if we're going to restart the target.
+ // If we are going to stop, then we always broadcast the event.
+ // If we aren't going to stop, let the thread plans decide if we're going to report this event.
+ // If no thread has an opinion, we don't report it.
+
+ RefreshStateAfterStop ();
+ if (ProcessEventData::GetInterruptedFromEvent (event_ptr))
+ {
+ if (log)
+ log->Printf ("Process::ShouldBroadcastEvent (%p) stopped due to an interrupt, state: %s",
+ event_ptr,
+ StateAsCString(state));
+ return_value = true;
+ }
+ else
+ {
+ bool was_restarted = ProcessEventData::GetRestartedFromEvent (event_ptr);
+ bool should_resume = false;
+
+ // It makes no sense to ask "ShouldStop" if we've already been restarted...
+ // Asking the thread list is also not likely to go well, since we are running again.
+ // So in that case just report the event.
+
+ if (!was_restarted)
+ should_resume = m_thread_list.ShouldStop (event_ptr) == false;
+
+ if (was_restarted || should_resume || m_resume_requested)
+ {
+ Vote stop_vote = m_thread_list.ShouldReportStop (event_ptr);
+ if (log)
+ log->Printf ("Process::ShouldBroadcastEvent: should_stop: %i state: %s was_restarted: %i stop_vote: %d.",
+ should_resume,
+ StateAsCString(state),
+ was_restarted,
+ stop_vote);
+
+ switch (stop_vote)
+ {
+ case eVoteYes:
+ return_value = true;
+ break;
+ case eVoteNoOpinion:
+ case eVoteNo:
+ return_value = false;
+ break;
+ }
+
+ if (!was_restarted)
+ {
+ if (log)
+ log->Printf ("Process::ShouldBroadcastEvent (%p) Restarting process from state: %s", event_ptr, StateAsCString(state));
+ ProcessEventData::SetRestartedInEvent(event_ptr, true);
+ PrivateResume ();
+ }
+
+ }
+ else
+ {
+ return_value = true;
+ SynchronouslyNotifyStateChanged (state);
+ }
+ }
+ }
+ break;
+ }
+
+ // We do some coalescing of events (for instance two consecutive running events get coalesced.)
+ // But we only coalesce against events we actually broadcast. So we use m_last_broadcast_state
+ // to track that. NB - you can't use "m_public_state.GetValue()" for that purpose, as was originally done,
+ // because the PublicState reflects the last event pulled off the queue, and there may be several
+ // events stacked up on the queue unserviced. So the PublicState may not reflect the last broadcasted event
+ // yet. m_last_broadcast_state gets updated here.
+
+ if (return_value)
+ m_last_broadcast_state = state;
+
+ if (log)
+ log->Printf ("Process::ShouldBroadcastEvent (%p) => new state: %s, last broadcast state: %s - %s",
+ event_ptr,
+ StateAsCString(state),
+ StateAsCString(m_last_broadcast_state),
+ return_value ? "YES" : "NO");
+ return return_value;
+}
+
+
+bool
+Process::StartPrivateStateThread (bool force)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
+
+ bool already_running = PrivateStateThreadIsValid ();
+ if (log)
+ log->Printf ("Process::%s()%s ", __FUNCTION__, already_running ? " already running" : " starting private state thread");
+
+ if (!force && already_running)
+ return true;
+
+ // Create a thread that watches our internal state and controls which
+ // events make it to clients (into the DCProcess event queue).
+ char thread_name[1024];
+ if (already_running)
+ snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%" PRIu64 ")>", GetID());
+ else
+ snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%" PRIu64 ")>", GetID());
+
+ // Create the private state thread, and start it running.
+ m_private_state_thread = Host::ThreadCreate (thread_name, Process::PrivateStateThread, this, NULL);
+ bool success = IS_VALID_LLDB_HOST_THREAD(m_private_state_thread);
+ if (success)
+ {
+ ResumePrivateStateThread();
+ return true;
+ }
+ else
+ return false;
+}
+
+void
+Process::PausePrivateStateThread ()
+{
+ ControlPrivateStateThread (eBroadcastInternalStateControlPause);
+}
+
+void
+Process::ResumePrivateStateThread ()
+{
+ ControlPrivateStateThread (eBroadcastInternalStateControlResume);
+}
+
+void
+Process::StopPrivateStateThread ()
+{
+ if (PrivateStateThreadIsValid ())
+ ControlPrivateStateThread (eBroadcastInternalStateControlStop);
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Went to stop the private state thread, but it was already invalid.");
+ }
+}
+
+void
+Process::ControlPrivateStateThread (uint32_t signal)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_PROCESS));
+
+ assert (signal == eBroadcastInternalStateControlStop ||
+ signal == eBroadcastInternalStateControlPause ||
+ signal == eBroadcastInternalStateControlResume);
+
+ if (log)
+ log->Printf ("Process::%s (signal = %d)", __FUNCTION__, signal);
+
+ // Signal the private state thread. First we should copy this is case the
+ // thread starts exiting since the private state thread will NULL this out
+ // when it exits
+ const lldb::thread_t private_state_thread = m_private_state_thread;
+ if (IS_VALID_LLDB_HOST_THREAD(private_state_thread))
+ {
+ TimeValue timeout_time;
+ bool timed_out;
+
+ m_private_state_control_broadcaster.BroadcastEvent (signal, NULL);
+
+ timeout_time = TimeValue::Now();
+ timeout_time.OffsetWithSeconds(2);
+ if (log)
+ log->Printf ("Sending control event of type: %d.", signal);
+ m_private_state_control_wait.WaitForValueEqualTo (true, &timeout_time, &timed_out);
+ m_private_state_control_wait.SetValue (false, eBroadcastNever);
+
+ if (signal == eBroadcastInternalStateControlStop)
+ {
+ if (timed_out)
+ {
+ Error error;
+ Host::ThreadCancel (private_state_thread, &error);
+ if (log)
+ log->Printf ("Timed out responding to the control event, cancel got error: \"%s\".", error.AsCString());
+ }
+ else
+ {
+ if (log)
+ log->Printf ("The control event killed the private state thread without having to cancel.");
+ }
+
+ thread_result_t result = NULL;
+ Host::ThreadJoin (private_state_thread, &result, NULL);
+ m_private_state_thread = LLDB_INVALID_HOST_THREAD;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf ("Private state thread already dead, no need to signal it to stop.");
+ }
+}
+
+void
+Process::SendAsyncInterrupt ()
+{
+ if (PrivateStateThreadIsValid())
+ m_private_state_broadcaster.BroadcastEvent (Process::eBroadcastBitInterrupt, NULL);
+ else
+ BroadcastEvent (Process::eBroadcastBitInterrupt, NULL);
+}
+
+void
+Process::HandlePrivateEvent (EventSP &event_sp)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ m_resume_requested = false;
+
+ m_currently_handling_event.SetValue(true, eBroadcastNever);
+
+ const StateType new_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ // First check to see if anybody wants a shot at this event:
+ if (m_next_event_action_ap.get() != NULL)
+ {
+ NextEventAction::EventActionResult action_result = m_next_event_action_ap->PerformAction(event_sp);
+ if (log)
+ log->Printf ("Ran next event action, result was %d.", action_result);
+
+ switch (action_result)
+ {
+ case NextEventAction::eEventActionSuccess:
+ SetNextEventAction(NULL);
+ break;
+
+ case NextEventAction::eEventActionRetry:
+ break;
+
+ case NextEventAction::eEventActionExit:
+ // Handle Exiting Here. If we already got an exited event,
+ // we should just propagate it. Otherwise, swallow this event,
+ // and set our state to exit so the next event will kill us.
+ if (new_state != eStateExited)
+ {
+ // FIXME: should cons up an exited event, and discard this one.
+ SetExitStatus(0, m_next_event_action_ap->GetExitString());
+ m_currently_handling_event.SetValue(false, eBroadcastAlways);
+ SetNextEventAction(NULL);
+ return;
+ }
+ SetNextEventAction(NULL);
+ break;
+ }
+ }
+
+ // See if we should broadcast this state to external clients?
+ const bool should_broadcast = ShouldBroadcastEvent (event_sp.get());
+
+ if (should_broadcast)
+ {
+ if (log)
+ {
+ log->Printf ("Process::%s (pid = %" PRIu64 ") broadcasting new state %s (old state %s) to %s",
+ __FUNCTION__,
+ GetID(),
+ StateAsCString(new_state),
+ StateAsCString (GetState ()),
+ IsHijackedForEvent(eBroadcastBitStateChanged) ? "hijacked" : "public");
+ }
+ Process::ProcessEventData::SetUpdateStateOnRemoval(event_sp.get());
+ if (StateIsRunningState (new_state))
+ PushProcessInputReader ();
+ else if (!Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
+ PopProcessInputReader ();
+
+ BroadcastEvent (event_sp);
+ }
+ else
+ {
+ if (log)
+ {
+ log->Printf ("Process::%s (pid = %" PRIu64 ") suppressing state %s (old state %s): should_broadcast == false",
+ __FUNCTION__,
+ GetID(),
+ StateAsCString(new_state),
+ StateAsCString (GetState ()));
+ }
+ }
+ m_currently_handling_event.SetValue(false, eBroadcastAlways);
+}
+
+void *
+Process::PrivateStateThread (void *arg)
+{
+ Process *proc = static_cast<Process*> (arg);
+ void *result = proc->RunPrivateStateThread ();
+ return result;
+}
+
+void *
+Process::RunPrivateStateThread ()
+{
+ bool control_only = true;
+ m_private_state_control_wait.SetValue (false, eBroadcastNever);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") thread starting...", __FUNCTION__, this, GetID());
+
+ bool exit_now = false;
+ while (!exit_now)
+ {
+ EventSP event_sp;
+ WaitForEventsPrivate (NULL, event_sp, control_only);
+ if (event_sp->BroadcasterIs(&m_private_state_control_broadcaster))
+ {
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") got a control event: %d", __FUNCTION__, this, GetID(), event_sp->GetType());
+
+ switch (event_sp->GetType())
+ {
+ case eBroadcastInternalStateControlStop:
+ exit_now = true;
+ break; // doing any internal state managment below
+
+ case eBroadcastInternalStateControlPause:
+ control_only = true;
+ break;
+
+ case eBroadcastInternalStateControlResume:
+ control_only = false;
+ break;
+ }
+
+ m_private_state_control_wait.SetValue (true, eBroadcastAlways);
+ continue;
+ }
+ else if (event_sp->GetType() == eBroadcastBitInterrupt)
+ {
+ if (m_public_state.GetValue() == eStateAttaching)
+ {
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt while attaching - forwarding interrupt.", __FUNCTION__, this, GetID());
+ BroadcastEvent (eBroadcastBitInterrupt, NULL);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") woke up with an interrupt - Halting.", __FUNCTION__, this, GetID());
+ Halt();
+ }
+ continue;
+ }
+
+ const StateType internal_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ if (internal_state != eStateInvalid)
+ {
+ if (m_clear_thread_plans_on_stop &&
+ StateIsStoppedState(internal_state, true))
+ {
+ m_clear_thread_plans_on_stop = false;
+ m_thread_list.DiscardThreadPlans();
+ }
+ HandlePrivateEvent (event_sp);
+ }
+
+ if (internal_state == eStateInvalid ||
+ internal_state == eStateExited ||
+ internal_state == eStateDetached )
+ {
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") about to exit with internal state %s...", __FUNCTION__, this, GetID(), StateAsCString(internal_state));
+
+ break;
+ }
+ }
+
+ // Verify log is still enabled before attempting to write to it...
+ if (log)
+ log->Printf ("Process::%s (arg = %p, pid = %" PRIu64 ") thread exiting...", __FUNCTION__, this, GetID());
+
+ m_public_run_lock.SetStopped();
+ m_private_state_control_wait.SetValue (true, eBroadcastAlways);
+ m_private_state_thread = LLDB_INVALID_HOST_THREAD;
+ return NULL;
+}
+
+//------------------------------------------------------------------
+// Process Event Data
+//------------------------------------------------------------------
+
+Process::ProcessEventData::ProcessEventData () :
+ EventData (),
+ m_process_sp (),
+ m_state (eStateInvalid),
+ m_restarted (false),
+ m_update_state (0),
+ m_interrupted (false)
+{
+}
+
+Process::ProcessEventData::ProcessEventData (const ProcessSP &process_sp, StateType state) :
+ EventData (),
+ m_process_sp (process_sp),
+ m_state (state),
+ m_restarted (false),
+ m_update_state (0),
+ m_interrupted (false)
+{
+}
+
+Process::ProcessEventData::~ProcessEventData()
+{
+}
+
+const ConstString &
+Process::ProcessEventData::GetFlavorString ()
+{
+ static ConstString g_flavor ("Process::ProcessEventData");
+ return g_flavor;
+}
+
+const ConstString &
+Process::ProcessEventData::GetFlavor () const
+{
+ return ProcessEventData::GetFlavorString ();
+}
+
+void
+Process::ProcessEventData::DoOnRemoval (Event *event_ptr)
+{
+ // This function gets called twice for each event, once when the event gets pulled
+ // off of the private process event queue, and then any number of times, first when it gets pulled off of
+ // the public event queue, then other times when we're pretending that this is where we stopped at the
+ // end of expression evaluation. m_update_state is used to distinguish these
+ // three cases; it is 0 when we're just pulling it off for private handling,
+ // and > 1 for expression evaluation, and we don't want to do the breakpoint command handling then.
+ if (m_update_state != 1)
+ return;
+
+ m_process_sp->SetPublicState (m_state, Process::ProcessEventData::GetRestartedFromEvent(event_ptr));
+
+ // If we're stopped and haven't restarted, then do the breakpoint commands here:
+ if (m_state == eStateStopped && ! m_restarted)
+ {
+ ThreadList &curr_thread_list = m_process_sp->GetThreadList();
+ uint32_t num_threads = curr_thread_list.GetSize();
+ uint32_t idx;
+
+ // The actions might change one of the thread's stop_info's opinions about whether we should
+ // stop the process, so we need to query that as we go.
+
+ // One other complication here, is that we try to catch any case where the target has run (except for expressions)
+ // and immediately exit, but if we get that wrong (which is possible) then the thread list might have changed, and
+ // that would cause our iteration here to crash. We could make a copy of the thread list, but we'd really like
+ // to also know if it has changed at all, so we make up a vector of the thread ID's and check what we get back
+ // against this list & bag out if anything differs.
+ std::vector<uint32_t> thread_index_array(num_threads);
+ for (idx = 0; idx < num_threads; ++idx)
+ thread_index_array[idx] = curr_thread_list.GetThreadAtIndex(idx)->GetIndexID();
+
+ // Use this to track whether we should continue from here. We will only continue the target running if
+ // no thread says we should stop. Of course if some thread's PerformAction actually sets the target running,
+ // then it doesn't matter what the other threads say...
+
+ bool still_should_stop = false;
+
+ // Sometimes - for instance if we have a bug in the stub we are talking to, we stop but no thread has a
+ // valid stop reason. In that case we should just stop, because we have no way of telling what the right
+ // thing to do is, and it's better to let the user decide than continue behind their backs.
+
+ bool does_anybody_have_an_opinion = false;
+
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ curr_thread_list = m_process_sp->GetThreadList();
+ if (curr_thread_list.GetSize() != num_threads)
+ {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("Number of threads changed from %u to %u while processing event.", num_threads, curr_thread_list.GetSize());
+ break;
+ }
+
+ lldb::ThreadSP thread_sp = curr_thread_list.GetThreadAtIndex(idx);
+
+ if (thread_sp->GetIndexID() != thread_index_array[idx])
+ {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf("The thread at position %u changed from %u to %u while processing event.",
+ idx,
+ thread_index_array[idx],
+ thread_sp->GetIndexID());
+ break;
+ }
+
+ StopInfoSP stop_info_sp = thread_sp->GetStopInfo ();
+ if (stop_info_sp && stop_info_sp->IsValid())
+ {
+ does_anybody_have_an_opinion = true;
+ bool this_thread_wants_to_stop;
+ if (stop_info_sp->GetOverrideShouldStop())
+ {
+ this_thread_wants_to_stop = stop_info_sp->GetOverriddenShouldStopValue();
+ }
+ else
+ {
+ stop_info_sp->PerformAction(event_ptr);
+ // The stop action might restart the target. If it does, then we want to mark that in the
+ // event so that whoever is receiving it will know to wait for the running event and reflect
+ // that state appropriately.
+ // We also need to stop processing actions, since they aren't expecting the target to be running.
+
+ // FIXME: we might have run.
+ if (stop_info_sp->HasTargetRunSinceMe())
+ {
+ SetRestarted (true);
+ break;
+ }
+
+ this_thread_wants_to_stop = stop_info_sp->ShouldStop(event_ptr);
+ }
+
+ if (still_should_stop == false)
+ still_should_stop = this_thread_wants_to_stop;
+ }
+ }
+
+
+ if (!GetRestarted())
+ {
+ if (!still_should_stop && does_anybody_have_an_opinion)
+ {
+ // We've been asked to continue, so do that here.
+ SetRestarted(true);
+ // Use the public resume method here, since this is just
+ // extending a public resume.
+ m_process_sp->PrivateResume();
+ }
+ else
+ {
+ // If we didn't restart, run the Stop Hooks here:
+ // They might also restart the target, so watch for that.
+ m_process_sp->GetTarget().RunStopHooks();
+ if (m_process_sp->GetPrivateState() == eStateRunning)
+ SetRestarted(true);
+ }
+ }
+ }
+}
+
+void
+Process::ProcessEventData::Dump (Stream *s) const
+{
+ if (m_process_sp)
+ s->Printf(" process = %p (pid = %" PRIu64 "), ", m_process_sp.get(), m_process_sp->GetID());
+
+ s->Printf("state = %s", StateAsCString(GetState()));
+}
+
+const Process::ProcessEventData *
+Process::ProcessEventData::GetEventDataFromEvent (const Event *event_ptr)
+{
+ if (event_ptr)
+ {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data && event_data->GetFlavor() == ProcessEventData::GetFlavorString())
+ return static_cast <const ProcessEventData *> (event_ptr->GetData());
+ }
+ return NULL;
+}
+
+ProcessSP
+Process::ProcessEventData::GetProcessFromEvent (const Event *event_ptr)
+{
+ ProcessSP process_sp;
+ const ProcessEventData *data = GetEventDataFromEvent (event_ptr);
+ if (data)
+ process_sp = data->GetProcessSP();
+ return process_sp;
+}
+
+StateType
+Process::ProcessEventData::GetStateFromEvent (const Event *event_ptr)
+{
+ const ProcessEventData *data = GetEventDataFromEvent (event_ptr);
+ if (data == NULL)
+ return eStateInvalid;
+ else
+ return data->GetState();
+}
+
+bool
+Process::ProcessEventData::GetRestartedFromEvent (const Event *event_ptr)
+{
+ const ProcessEventData *data = GetEventDataFromEvent (event_ptr);
+ if (data == NULL)
+ return false;
+ else
+ return data->GetRestarted();
+}
+
+void
+Process::ProcessEventData::SetRestartedInEvent (Event *event_ptr, bool new_value)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data != NULL)
+ data->SetRestarted(new_value);
+}
+
+size_t
+Process::ProcessEventData::GetNumRestartedReasons(const Event *event_ptr)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data != NULL)
+ return data->GetNumRestartedReasons();
+ else
+ return 0;
+}
+
+const char *
+Process::ProcessEventData::GetRestartedReasonAtIndex(const Event *event_ptr, size_t idx)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data != NULL)
+ return data->GetRestartedReasonAtIndex(idx);
+ else
+ return NULL;
+}
+
+void
+Process::ProcessEventData::AddRestartedReason (Event *event_ptr, const char *reason)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data != NULL)
+ data->AddRestartedReason(reason);
+}
+
+bool
+Process::ProcessEventData::GetInterruptedFromEvent (const Event *event_ptr)
+{
+ const ProcessEventData *data = GetEventDataFromEvent (event_ptr);
+ if (data == NULL)
+ return false;
+ else
+ return data->GetInterrupted ();
+}
+
+void
+Process::ProcessEventData::SetInterruptedInEvent (Event *event_ptr, bool new_value)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data != NULL)
+ data->SetInterrupted(new_value);
+}
+
+bool
+Process::ProcessEventData::SetUpdateStateOnRemoval (Event *event_ptr)
+{
+ ProcessEventData *data = const_cast<ProcessEventData *>(GetEventDataFromEvent (event_ptr));
+ if (data)
+ {
+ data->SetUpdateStateOnRemoval();
+ return true;
+ }
+ return false;
+}
+
+lldb::TargetSP
+Process::CalculateTarget ()
+{
+ return m_target.shared_from_this();
+}
+
+void
+Process::CalculateExecutionContext (ExecutionContext &exe_ctx)
+{
+ exe_ctx.SetTargetPtr (&m_target);
+ exe_ctx.SetProcessPtr (this);
+ exe_ctx.SetThreadPtr(NULL);
+ exe_ctx.SetFramePtr (NULL);
+}
+
+//uint32_t
+//Process::ListProcessesMatchingName (const char *name, StringList &matches, std::vector<lldb::pid_t> &pids)
+//{
+// return 0;
+//}
+//
+//ArchSpec
+//Process::GetArchSpecForExistingProcess (lldb::pid_t pid)
+//{
+// return Host::GetArchSpecForExistingProcess (pid);
+//}
+//
+//ArchSpec
+//Process::GetArchSpecForExistingProcess (const char *process_name)
+//{
+// return Host::GetArchSpecForExistingProcess (process_name);
+//}
+//
+void
+Process::AppendSTDOUT (const char * s, size_t len)
+{
+ Mutex::Locker locker (m_stdio_communication_mutex);
+ m_stdout_data.append (s, len);
+ BroadcastEventIfUnique (eBroadcastBitSTDOUT, new ProcessEventData (shared_from_this(), GetState()));
+}
+
+void
+Process::AppendSTDERR (const char * s, size_t len)
+{
+ Mutex::Locker locker (m_stdio_communication_mutex);
+ m_stderr_data.append (s, len);
+ BroadcastEventIfUnique (eBroadcastBitSTDERR, new ProcessEventData (shared_from_this(), GetState()));
+}
+
+void
+Process::BroadcastAsyncProfileData(const std::string &one_profile_data)
+{
+ Mutex::Locker locker (m_profile_data_comm_mutex);
+ m_profile_data.push_back(one_profile_data);
+ BroadcastEventIfUnique (eBroadcastBitProfileData, new ProcessEventData (shared_from_this(), GetState()));
+}
+
+size_t
+Process::GetAsyncProfileData (char *buf, size_t buf_size, Error &error)
+{
+ Mutex::Locker locker(m_profile_data_comm_mutex);
+ if (m_profile_data.empty())
+ return 0;
+
+ std::string &one_profile_data = m_profile_data.front();
+ size_t bytes_available = one_profile_data.size();
+ if (bytes_available > 0)
+ {
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Process::GetProfileData (buf = %p, size = %" PRIu64 ")", buf, (uint64_t)buf_size);
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, one_profile_data.c_str(), buf_size);
+ one_profile_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, one_profile_data.c_str(), bytes_available);
+ m_profile_data.erase(m_profile_data.begin());
+ }
+ }
+ return bytes_available;
+}
+
+
+//------------------------------------------------------------------
+// Process STDIO
+//------------------------------------------------------------------
+
+size_t
+Process::GetSTDOUT (char *buf, size_t buf_size, Error &error)
+{
+ Mutex::Locker locker(m_stdio_communication_mutex);
+ size_t bytes_available = m_stdout_data.size();
+ if (bytes_available > 0)
+ {
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Process::GetSTDOUT (buf = %p, size = %" PRIu64 ")", buf, (uint64_t)buf_size);
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stdout_data.c_str(), buf_size);
+ m_stdout_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stdout_data.c_str(), bytes_available);
+ m_stdout_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+
+size_t
+Process::GetSTDERR (char *buf, size_t buf_size, Error &error)
+{
+ Mutex::Locker locker(m_stdio_communication_mutex);
+ size_t bytes_available = m_stderr_data.size();
+ if (bytes_available > 0)
+ {
+ Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+ if (log)
+ log->Printf ("Process::GetSTDERR (buf = %p, size = %" PRIu64 ")", buf, (uint64_t)buf_size);
+ if (bytes_available > buf_size)
+ {
+ memcpy(buf, m_stderr_data.c_str(), buf_size);
+ m_stderr_data.erase(0, buf_size);
+ bytes_available = buf_size;
+ }
+ else
+ {
+ memcpy(buf, m_stderr_data.c_str(), bytes_available);
+ m_stderr_data.clear();
+ }
+ }
+ return bytes_available;
+}
+
+void
+Process::STDIOReadThreadBytesReceived (void *baton, const void *src, size_t src_len)
+{
+ Process *process = (Process *) baton;
+ process->AppendSTDOUT (static_cast<const char *>(src), src_len);
+}
+
+size_t
+Process::ProcessInputReaderCallback (void *baton,
+ InputReader &reader,
+ lldb::InputReaderAction notification,
+ const char *bytes,
+ size_t bytes_len)
+{
+ Process *process = (Process *) baton;
+
+ switch (notification)
+ {
+ case eInputReaderActivate:
+ break;
+
+ case eInputReaderDeactivate:
+ break;
+
+ case eInputReaderReactivate:
+ break;
+
+ case eInputReaderAsynchronousOutputWritten:
+ break;
+
+ case eInputReaderGotToken:
+ {
+ Error error;
+ process->PutSTDIN (bytes, bytes_len, error);
+ }
+ break;
+
+ case eInputReaderInterrupt:
+ process->SendAsyncInterrupt();
+ break;
+
+ case eInputReaderEndOfFile:
+ process->AppendSTDOUT ("^D", 2);
+ break;
+
+ case eInputReaderDone:
+ break;
+
+ }
+
+ return bytes_len;
+}
+
+void
+Process::ResetProcessInputReader ()
+{
+ m_process_input_reader.reset();
+}
+
+void
+Process::SetSTDIOFileDescriptor (int file_descriptor)
+{
+ // First set up the Read Thread for reading/handling process I/O
+
+ std::unique_ptr<ConnectionFileDescriptor> conn_ap (new ConnectionFileDescriptor (file_descriptor, true));
+
+ if (conn_ap.get())
+ {
+ m_stdio_communication.SetConnection (conn_ap.release());
+ if (m_stdio_communication.IsConnected())
+ {
+ m_stdio_communication.SetReadThreadBytesReceivedCallback (STDIOReadThreadBytesReceived, this);
+ m_stdio_communication.StartReadThread();
+
+ // Now read thread is set up, set up input reader.
+
+ if (!m_process_input_reader.get())
+ {
+ m_process_input_reader.reset (new InputReader(m_target.GetDebugger()));
+ Error err (m_process_input_reader->Initialize (Process::ProcessInputReaderCallback,
+ this,
+ eInputReaderGranularityByte,
+ NULL,
+ NULL,
+ false));
+
+ if (err.Fail())
+ m_process_input_reader.reset();
+ }
+ }
+ }
+}
+
+void
+Process::PushProcessInputReader ()
+{
+ if (m_process_input_reader && !m_process_input_reader->IsActive())
+ m_target.GetDebugger().PushInputReader (m_process_input_reader);
+}
+
+void
+Process::PopProcessInputReader ()
+{
+ if (m_process_input_reader && m_process_input_reader->IsActive())
+ m_target.GetDebugger().PopInputReader (m_process_input_reader);
+}
+
+// The process needs to know about installed plug-ins
+void
+Process::SettingsInitialize ()
+{
+// static std::vector<OptionEnumValueElement> g_plugins;
+//
+// int i=0;
+// const char *name;
+// OptionEnumValueElement option_enum;
+// while ((name = PluginManager::GetProcessPluginNameAtIndex (i)) != NULL)
+// {
+// if (name)
+// {
+// option_enum.value = i;
+// option_enum.string_value = name;
+// option_enum.usage = PluginManager::GetProcessPluginDescriptionAtIndex (i);
+// g_plugins.push_back (option_enum);
+// }
+// ++i;
+// }
+// option_enum.value = 0;
+// option_enum.string_value = NULL;
+// option_enum.usage = NULL;
+// g_plugins.push_back (option_enum);
+//
+// for (i=0; (name = SettingsController::instance_settings_table[i].var_name); ++i)
+// {
+// if (::strcmp (name, "plugin") == 0)
+// {
+// SettingsController::instance_settings_table[i].enum_values = &g_plugins[0];
+// break;
+// }
+// }
+//
+ Thread::SettingsInitialize ();
+}
+
+void
+Process::SettingsTerminate ()
+{
+ Thread::SettingsTerminate ();
+}
+
+ExecutionResults
+Process::RunThreadPlan (ExecutionContext &exe_ctx,
+ lldb::ThreadPlanSP &thread_plan_sp,
+ bool stop_others,
+ bool run_others,
+ bool unwind_on_error,
+ bool ignore_breakpoints,
+ uint32_t timeout_usec,
+ Stream &errors)
+{
+ ExecutionResults return_value = eExecutionSetupError;
+
+ if (thread_plan_sp.get() == NULL)
+ {
+ errors.Printf("RunThreadPlan called with empty thread plan.");
+ return eExecutionSetupError;
+ }
+
+ if (!thread_plan_sp->ValidatePlan(NULL))
+ {
+ errors.Printf ("RunThreadPlan called with an invalid thread plan.");
+ return eExecutionSetupError;
+ }
+
+ if (exe_ctx.GetProcessPtr() != this)
+ {
+ errors.Printf("RunThreadPlan called on wrong process.");
+ return eExecutionSetupError;
+ }
+
+ Thread *thread = exe_ctx.GetThreadPtr();
+ if (thread == NULL)
+ {
+ errors.Printf("RunThreadPlan called with invalid thread.");
+ return eExecutionSetupError;
+ }
+
+ // We rely on the thread plan we are running returning "PlanCompleted" if when it successfully completes.
+ // For that to be true the plan can't be private - since private plans suppress themselves in the
+ // GetCompletedPlan call.
+
+ bool orig_plan_private = thread_plan_sp->GetPrivate();
+ thread_plan_sp->SetPrivate(false);
+
+ if (m_private_state.GetValue() != eStateStopped)
+ {
+ errors.Printf ("RunThreadPlan called while the private state was not stopped.");
+ return eExecutionSetupError;
+ }
+
+ // Save the thread & frame from the exe_ctx for restoration after we run
+ const uint32_t thread_idx_id = thread->GetIndexID();
+ StackFrameSP selected_frame_sp = thread->GetSelectedFrame();
+ if (!selected_frame_sp)
+ {
+ thread->SetSelectedFrame(0);
+ selected_frame_sp = thread->GetSelectedFrame();
+ if (!selected_frame_sp)
+ {
+ errors.Printf("RunThreadPlan called without a selected frame on thread %d", thread_idx_id);
+ return eExecutionSetupError;
+ }
+ }
+
+ StackID ctx_frame_id = selected_frame_sp->GetStackID();
+
+ // N.B. Running the target may unset the currently selected thread and frame. We don't want to do that either,
+ // so we should arrange to reset them as well.
+
+ lldb::ThreadSP selected_thread_sp = GetThreadList().GetSelectedThread();
+
+ uint32_t selected_tid;
+ StackID selected_stack_id;
+ if (selected_thread_sp)
+ {
+ selected_tid = selected_thread_sp->GetIndexID();
+ selected_stack_id = selected_thread_sp->GetSelectedFrame()->GetStackID();
+ }
+ else
+ {
+ selected_tid = LLDB_INVALID_THREAD_ID;
+ }
+
+ lldb::thread_t backup_private_state_thread = LLDB_INVALID_HOST_THREAD;
+ lldb::StateType old_state;
+ lldb::ThreadPlanSP stopper_base_plan_sp;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_PROCESS));
+ if (Host::GetCurrentThread() == m_private_state_thread)
+ {
+ // Yikes, we are running on the private state thread! So we can't wait for public events on this thread, since
+ // we are the thread that is generating public events.
+ // The simplest thing to do is to spin up a temporary thread to handle private state thread events while
+ // we are fielding public events here.
+ if (log)
+ log->Printf ("Running thread plan on private state thread, spinning up another state thread to handle the events.");
+
+
+ backup_private_state_thread = m_private_state_thread;
+
+ // One other bit of business: we want to run just this thread plan and anything it pushes, and then stop,
+ // returning control here.
+ // But in the normal course of things, the plan above us on the stack would be given a shot at the stop
+ // event before deciding to stop, and we don't want that. So we insert a "stopper" base plan on the stack
+ // before the plan we want to run. Since base plans always stop and return control to the user, that will
+ // do just what we want.
+ stopper_base_plan_sp.reset(new ThreadPlanBase (*thread));
+ thread->QueueThreadPlan (stopper_base_plan_sp, false);
+ // Have to make sure our public state is stopped, since otherwise the reporting logic below doesn't work correctly.
+ old_state = m_public_state.GetValue();
+ m_public_state.SetValueNoLock(eStateStopped);
+
+ // Now spin up the private state thread:
+ StartPrivateStateThread(true);
+ }
+
+ thread->QueueThreadPlan(thread_plan_sp, false); // This used to pass "true" does that make sense?
+
+ Listener listener("lldb.process.listener.run-thread-plan");
+
+ lldb::EventSP event_to_broadcast_sp;
+
+ {
+ // This process event hijacker Hijacks the Public events and its destructor makes sure that the process events get
+ // restored on exit to the function.
+ //
+ // If the event needs to propagate beyond the hijacker (e.g., the process exits during execution), then the event
+ // is put into event_to_broadcast_sp for rebroadcasting.
+
+ ProcessEventHijacker run_thread_plan_hijacker (*this, &listener);
+
+ if (log)
+ {
+ StreamString s;
+ thread_plan_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf ("Process::RunThreadPlan(): Resuming thread %u - 0x%4.4" PRIx64 " to run thread plan \"%s\".",
+ thread->GetIndexID(),
+ thread->GetID(),
+ s.GetData());
+ }
+
+ bool got_event;
+ lldb::EventSP event_sp;
+ lldb::StateType stop_state = lldb::eStateInvalid;
+
+ TimeValue* timeout_ptr = NULL;
+ TimeValue real_timeout;
+
+ bool before_first_timeout = true; // This is set to false the first time that we have to halt the target.
+ bool do_resume = true;
+ bool handle_running_event = true;
+ const uint64_t default_one_thread_timeout_usec = 250000;
+
+ // This is just for accounting:
+ uint32_t num_resumes = 0;
+
+ TimeValue one_thread_timeout = TimeValue::Now();
+ TimeValue final_timeout = one_thread_timeout;
+
+ if (run_others)
+ {
+ // If we are running all threads then we take half the time to run all threads, bounded by
+ // .25 sec.
+ if (timeout_usec == 0)
+ one_thread_timeout.OffsetWithMicroSeconds(default_one_thread_timeout_usec);
+ else
+ {
+ uint64_t computed_timeout = timeout_usec / 2;
+ if (computed_timeout > default_one_thread_timeout_usec)
+ computed_timeout = default_one_thread_timeout_usec;
+ one_thread_timeout.OffsetWithMicroSeconds(computed_timeout);
+ }
+ final_timeout.OffsetWithMicroSeconds (timeout_usec);
+ }
+ else
+ {
+ if (timeout_usec != 0)
+ final_timeout.OffsetWithMicroSeconds(timeout_usec);
+ }
+
+ // This while loop must exit out the bottom, there's cleanup that we need to do when we are done.
+ // So don't call return anywhere within it.
+
+ while (1)
+ {
+ // We usually want to resume the process if we get to the top of the loop.
+ // The only exception is if we get two running events with no intervening
+ // stop, which can happen, we will just wait for then next stop event.
+ if (log)
+ log->Printf ("Top of while loop: do_resume: %i handle_running_event: %i before_first_timeout: %i.",
+ do_resume,
+ handle_running_event,
+ before_first_timeout);
+
+ if (do_resume || handle_running_event)
+ {
+ // Do the initial resume and wait for the running event before going further.
+
+ if (do_resume)
+ {
+ num_resumes++;
+ Error resume_error = PrivateResume ();
+ if (!resume_error.Success())
+ {
+ errors.Printf("Error resuming inferior the %d time: \"%s\".\n",
+ num_resumes,
+ resume_error.AsCString());
+ return_value = eExecutionSetupError;
+ break;
+ }
+ }
+
+ TimeValue resume_timeout = TimeValue::Now();
+ resume_timeout.OffsetWithMicroSeconds(500000);
+
+ got_event = listener.WaitForEvent(&resume_timeout, event_sp);
+ if (!got_event)
+ {
+ if (log)
+ log->Printf ("Process::RunThreadPlan(): didn't get any event after resume %d, exiting.",
+ num_resumes);
+
+ errors.Printf("Didn't get any event after resume %d, exiting.", num_resumes);
+ return_value = eExecutionSetupError;
+ break;
+ }
+
+ stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+
+ if (stop_state != eStateRunning)
+ {
+ bool restarted = false;
+
+ if (stop_state == eStateStopped)
+ {
+ restarted = Process::ProcessEventData::GetRestartedFromEvent(event_sp.get());
+ if (log)
+ log->Printf("Process::RunThreadPlan(): didn't get running event after "
+ "resume %d, got %s instead (restarted: %i, do_resume: %i, handle_running_event: %i).",
+ num_resumes,
+ StateAsCString(stop_state),
+ restarted,
+ do_resume,
+ handle_running_event);
+ }
+
+ if (restarted)
+ {
+ // This is probably an overabundance of caution, I don't think I should ever get a stopped & restarted
+ // event here. But if I do, the best thing is to Halt and then get out of here.
+ Halt();
+ }
+
+ errors.Printf("Didn't get running event after initial resume, got %s instead.",
+ StateAsCString(stop_state));
+ return_value = eExecutionSetupError;
+ break;
+ }
+
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): resuming succeeded.");
+ // We need to call the function synchronously, so spin waiting for it to return.
+ // If we get interrupted while executing, we're going to lose our context, and
+ // won't be able to gather the result at this point.
+ // We set the timeout AFTER the resume, since the resume takes some time and we
+ // don't want to charge that to the timeout.
+ }
+ else
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): waiting for next event.");
+ }
+
+ if (before_first_timeout)
+ {
+ if (run_others)
+ timeout_ptr = &one_thread_timeout;
+ else
+ {
+ if (timeout_usec == 0)
+ timeout_ptr = NULL;
+ else
+ timeout_ptr = &final_timeout;
+ }
+ }
+ else
+ {
+ if (timeout_usec == 0)
+ timeout_ptr = NULL;
+ else
+ timeout_ptr = &final_timeout;
+ }
+
+ do_resume = true;
+ handle_running_event = true;
+
+ // Now wait for the process to stop again:
+ event_sp.reset();
+
+ if (log)
+ {
+ if (timeout_ptr)
+ {
+ log->Printf ("Process::RunThreadPlan(): about to wait - now is %" PRIu64 " - endpoint is %" PRIu64,
+ TimeValue::Now().GetAsMicroSecondsSinceJan1_1970(),
+ timeout_ptr->GetAsMicroSecondsSinceJan1_1970());
+ }
+ else
+ {
+ log->Printf ("Process::RunThreadPlan(): about to wait forever.");
+ }
+ }
+
+ got_event = listener.WaitForEvent (timeout_ptr, event_sp);
+
+ if (got_event)
+ {
+ if (event_sp.get())
+ {
+ bool keep_going = false;
+ if (event_sp->GetType() == eBroadcastBitInterrupt)
+ {
+ Halt();
+ return_value = eExecutionInterrupted;
+ errors.Printf ("Execution halted by user interrupt.");
+ if (log)
+ log->Printf ("Process::RunThreadPlan(): Got interrupted by eBroadcastBitInterrupted, exiting.");
+ break;
+ }
+ else
+ {
+ stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (log)
+ log->Printf("Process::RunThreadPlan(): in while loop, got event: %s.", StateAsCString(stop_state));
+
+ switch (stop_state)
+ {
+ case lldb::eStateStopped:
+ {
+ // We stopped, figure out what we are going to do now.
+ ThreadSP thread_sp = GetThreadList().FindThreadByIndexID (thread_idx_id);
+ if (!thread_sp)
+ {
+ // Ooh, our thread has vanished. Unlikely that this was successful execution...
+ if (log)
+ log->Printf ("Process::RunThreadPlan(): execution completed but our thread (index-id=%u) has vanished.", thread_idx_id);
+ return_value = eExecutionInterrupted;
+ }
+ else
+ {
+ // If we were restarted, we just need to go back up to fetch another event.
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
+ {
+ if (log)
+ {
+ log->Printf ("Process::RunThreadPlan(): Got a stop and restart, so we'll continue waiting.");
+ }
+ keep_going = true;
+ do_resume = false;
+ handle_running_event = true;
+
+ }
+ else
+ {
+
+ StopInfoSP stop_info_sp (thread_sp->GetStopInfo ());
+ StopReason stop_reason = eStopReasonInvalid;
+ if (stop_info_sp)
+ stop_reason = stop_info_sp->GetStopReason();
+
+
+ // FIXME: We only check if the stop reason is plan complete, should we make sure that
+ // it is OUR plan that is complete?
+ if (stop_reason == eStopReasonPlanComplete)
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): execution completed successfully.");
+ // Now mark this plan as private so it doesn't get reported as the stop reason
+ // after this point.
+ if (thread_plan_sp)
+ thread_plan_sp->SetPrivate (orig_plan_private);
+ return_value = eExecutionCompleted;
+ }
+ else
+ {
+ // Something restarted the target, so just wait for it to stop for real.
+ if (stop_reason == eStopReasonBreakpoint)
+ {
+ if (log)
+ log->Printf ("Process::RunThreadPlan() stopped for breakpoint: %s.", stop_info_sp->GetDescription());
+ return_value = eExecutionHitBreakpoint;
+ if (!ignore_breakpoints)
+ {
+ event_to_broadcast_sp = event_sp;
+ }
+ }
+ else
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): thread plan didn't successfully complete.");
+ if (!unwind_on_error)
+ event_to_broadcast_sp = event_sp;
+ return_value = eExecutionInterrupted;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case lldb::eStateRunning:
+ // This shouldn't really happen, but sometimes we do get two running events without an
+ // intervening stop, and in that case we should just go back to waiting for the stop.
+ do_resume = false;
+ keep_going = true;
+ handle_running_event = false;
+ break;
+
+ default:
+ if (log)
+ log->Printf("Process::RunThreadPlan(): execution stopped with unexpected state: %s.", StateAsCString(stop_state));
+
+ if (stop_state == eStateExited)
+ event_to_broadcast_sp = event_sp;
+
+ errors.Printf ("Execution stopped with unexpected state.\n");
+ return_value = eExecutionInterrupted;
+ break;
+ }
+ }
+
+ if (keep_going)
+ continue;
+ else
+ break;
+ }
+ else
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): got_event was true, but the event pointer was null. How odd...");
+ return_value = eExecutionInterrupted;
+ break;
+ }
+ }
+ else
+ {
+ // If we didn't get an event that means we've timed out...
+ // We will interrupt the process here. Depending on what we were asked to do we will
+ // either exit, or try with all threads running for the same timeout.
+
+ if (log) {
+ if (run_others)
+ {
+ uint64_t remaining_time = final_timeout - TimeValue::Now();
+ if (before_first_timeout)
+ log->Printf ("Process::RunThreadPlan(): Running function with one thread timeout timed out, "
+ "running till for %" PRId64 " usec with all threads enabled.",
+ remaining_time);
+ else
+ log->Printf ("Process::RunThreadPlan(): Restarting function with all threads enabled "
+ "and timeout: %d timed out, abandoning execution.",
+ timeout_usec);
+ }
+ else
+ log->Printf ("Process::RunThreadPlan(): Running function with timeout: %d timed out, "
+ "abandoning execution.",
+ timeout_usec);
+ }
+
+ // It is possible that between the time we issued the Halt, and we get around to calling Halt the target
+ // could have stopped. That's fine, Halt will figure that out and send the appropriate Stopped event.
+ // BUT it is also possible that we stopped & restarted (e.g. hit a signal with "stop" set to false.) In
+ // that case, we'll get the stopped & restarted event, and we should go back to waiting for the Halt's
+ // stopped event. That's what this while loop does.
+
+ bool back_to_top = true;
+ uint32_t try_halt_again = 0;
+ bool do_halt = true;
+ const uint32_t num_retries = 5;
+ while (try_halt_again < num_retries)
+ {
+ Error halt_error;
+ if (do_halt)
+ {
+ if (log)
+ log->Printf ("Process::RunThreadPlan(): Running Halt.");
+ halt_error = Halt();
+ }
+ if (halt_error.Success())
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): Halt succeeded.");
+
+ real_timeout = TimeValue::Now();
+ real_timeout.OffsetWithMicroSeconds(500000);
+
+ got_event = listener.WaitForEvent(&real_timeout, event_sp);
+
+ if (got_event)
+ {
+ stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
+ if (log)
+ {
+ log->Printf ("Process::RunThreadPlan(): Stopped with event: %s", StateAsCString(stop_state));
+ if (stop_state == lldb::eStateStopped
+ && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get()))
+ log->PutCString (" Event was the Halt interruption event.");
+ }
+
+ if (stop_state == lldb::eStateStopped)
+ {
+ // Between the time we initiated the Halt and the time we delivered it, the process could have
+ // already finished its job. Check that here:
+
+ if (thread->IsThreadPlanDone (thread_plan_sp.get()))
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): Even though we timed out, the call plan was done. "
+ "Exiting wait loop.");
+ return_value = eExecutionCompleted;
+ back_to_top = false;
+ break;
+ }
+
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_sp.get()))
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): Went to halt but got a restarted event, there must be an un-restarted stopped event so try again... "
+ "Exiting wait loop.");
+ try_halt_again++;
+ do_halt = false;
+ continue;
+ }
+
+ if (!run_others)
+ {
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): try_all_threads was false, we stopped so now we're quitting.");
+ return_value = eExecutionInterrupted;
+ back_to_top = false;
+ break;
+ }
+
+ if (before_first_timeout)
+ {
+ // Set all the other threads to run, and return to the top of the loop, which will continue;
+ before_first_timeout = false;
+ thread_plan_sp->SetStopOthers (false);
+ if (log)
+ log->PutCString ("Process::RunThreadPlan(): about to resume.");
+
+ back_to_top = true;
+ break;
+ }
+ else
+ {
+ // Running all threads failed, so return Interrupted.
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): running all threads timed out.");
+ return_value = eExecutionInterrupted;
+ back_to_top = false;
+ break;
+ }
+ }
+ }
+ else
+ { if (log)
+ log->PutCString("Process::RunThreadPlan(): halt said it succeeded, but I got no event. "
+ "I'm getting out of here passing Interrupted.");
+ return_value = eExecutionInterrupted;
+ back_to_top = false;
+ break;
+ }
+ }
+ else
+ {
+ try_halt_again++;
+ continue;
+ }
+ }
+
+ if (!back_to_top || try_halt_again > num_retries)
+ break;
+ else
+ continue;
+ }
+ } // END WAIT LOOP
+
+ // If we had to start up a temporary private state thread to run this thread plan, shut it down now.
+ if (IS_VALID_LLDB_HOST_THREAD(backup_private_state_thread))
+ {
+ StopPrivateStateThread();
+ Error error;
+ m_private_state_thread = backup_private_state_thread;
+ if (stopper_base_plan_sp)
+ {
+ thread->DiscardThreadPlansUpToPlan(stopper_base_plan_sp);
+ }
+ m_public_state.SetValueNoLock(old_state);
+
+ }
+
+ // Restore the thread state if we are going to discard the plan execution. There are three cases where this
+ // could happen:
+ // 1) The execution successfully completed
+ // 2) We hit a breakpoint, and ignore_breakpoints was true
+ // 3) We got some other error, and discard_on_error was true
+ bool should_unwind = (return_value == eExecutionInterrupted && unwind_on_error)
+ || (return_value == eExecutionHitBreakpoint && ignore_breakpoints);
+
+ if (return_value == eExecutionCompleted
+ || should_unwind)
+ {
+ thread_plan_sp->RestoreThreadState();
+ }
+
+ // Now do some processing on the results of the run:
+ if (return_value == eExecutionInterrupted || return_value == eExecutionHitBreakpoint)
+ {
+ if (log)
+ {
+ StreamString s;
+ if (event_sp)
+ event_sp->Dump (&s);
+ else
+ {
+ log->PutCString ("Process::RunThreadPlan(): Stop event that interrupted us is NULL.");
+ }
+
+ StreamString ts;
+
+ const char *event_explanation = NULL;
+
+ do
+ {
+ if (!event_sp)
+ {
+ event_explanation = "<no event>";
+ break;
+ }
+ else if (event_sp->GetType() == eBroadcastBitInterrupt)
+ {
+ event_explanation = "<user interrupt>";
+ break;
+ }
+ else
+ {
+ const Process::ProcessEventData *event_data = Process::ProcessEventData::GetEventDataFromEvent (event_sp.get());
+
+ if (!event_data)
+ {
+ event_explanation = "<no event data>";
+ break;
+ }
+
+ Process *process = event_data->GetProcessSP().get();
+
+ if (!process)
+ {
+ event_explanation = "<no process>";
+ break;
+ }
+
+ ThreadList &thread_list = process->GetThreadList();
+
+ uint32_t num_threads = thread_list.GetSize();
+ uint32_t thread_index;
+
+ ts.Printf("<%u threads> ", num_threads);
+
+ for (thread_index = 0;
+ thread_index < num_threads;
+ ++thread_index)
+ {
+ Thread *thread = thread_list.GetThreadAtIndex(thread_index).get();
+
+ if (!thread)
+ {
+ ts.Printf("<?> ");
+ continue;
+ }
+
+ ts.Printf("<0x%4.4" PRIx64 " ", thread->GetID());
+ RegisterContext *register_context = thread->GetRegisterContext().get();
+
+ if (register_context)
+ ts.Printf("[ip 0x%" PRIx64 "] ", register_context->GetPC());
+ else
+ ts.Printf("[ip unknown] ");
+
+ lldb::StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp)
+ {
+ const char *stop_desc = stop_info_sp->GetDescription();
+ if (stop_desc)
+ ts.PutCString (stop_desc);
+ }
+ ts.Printf(">");
+ }
+
+ event_explanation = ts.GetData();
+ }
+ } while (0);
+
+ if (event_explanation)
+ log->Printf("Process::RunThreadPlan(): execution interrupted: %s %s", s.GetData(), event_explanation);
+ else
+ log->Printf("Process::RunThreadPlan(): execution interrupted: %s", s.GetData());
+ }
+
+ if (should_unwind && thread_plan_sp)
+ {
+ if (log)
+ log->Printf ("Process::RunThreadPlan: ExecutionInterrupted - discarding thread plans up to %p.", thread_plan_sp.get());
+ thread->DiscardThreadPlansUpToPlan (thread_plan_sp);
+ thread_plan_sp->SetPrivate (orig_plan_private);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("Process::RunThreadPlan: ExecutionInterrupted - for plan: %p not discarding.", thread_plan_sp.get());
+ }
+ }
+ else if (return_value == eExecutionSetupError)
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): execution set up error.");
+
+ if (unwind_on_error && thread_plan_sp)
+ {
+ thread->DiscardThreadPlansUpToPlan (thread_plan_sp);
+ thread_plan_sp->SetPrivate (orig_plan_private);
+ }
+ }
+ else
+ {
+ if (thread->IsThreadPlanDone (thread_plan_sp.get()))
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): thread plan is done");
+ return_value = eExecutionCompleted;
+ }
+ else if (thread->WasThreadPlanDiscarded (thread_plan_sp.get()))
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): thread plan was discarded");
+ return_value = eExecutionDiscarded;
+ }
+ else
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): thread plan stopped in mid course");
+ if (unwind_on_error && thread_plan_sp)
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): discarding thread plan 'cause unwind_on_error is set.");
+ thread->DiscardThreadPlansUpToPlan (thread_plan_sp);
+ thread_plan_sp->SetPrivate (orig_plan_private);
+ }
+ }
+ }
+
+ // Thread we ran the function in may have gone away because we ran the target
+ // Check that it's still there, and if it is put it back in the context. Also restore the
+ // frame in the context if it is still present.
+ thread = GetThreadList().FindThreadByIndexID(thread_idx_id, true).get();
+ if (thread)
+ {
+ exe_ctx.SetFrameSP (thread->GetFrameWithStackID (ctx_frame_id));
+ }
+
+ // Also restore the current process'es selected frame & thread, since this function calling may
+ // be done behind the user's back.
+
+ if (selected_tid != LLDB_INVALID_THREAD_ID)
+ {
+ if (GetThreadList().SetSelectedThreadByIndexID (selected_tid) && selected_stack_id.IsValid())
+ {
+ // We were able to restore the selected thread, now restore the frame:
+ Mutex::Locker lock(GetThreadList().GetMutex());
+ StackFrameSP old_frame_sp = GetThreadList().GetSelectedThread()->GetFrameWithStackID(selected_stack_id);
+ if (old_frame_sp)
+ GetThreadList().GetSelectedThread()->SetSelectedFrame(old_frame_sp.get());
+ }
+ }
+ }
+
+ // If the process exited during the run of the thread plan, notify everyone.
+
+ if (event_to_broadcast_sp)
+ {
+ if (log)
+ log->PutCString("Process::RunThreadPlan(): rebroadcasting event.");
+ BroadcastEvent(event_to_broadcast_sp);
+ }
+
+ return return_value;
+}
+
+const char *
+Process::ExecutionResultAsCString (ExecutionResults result)
+{
+ const char *result_name;
+
+ switch (result)
+ {
+ case eExecutionCompleted:
+ result_name = "eExecutionCompleted";
+ break;
+ case eExecutionDiscarded:
+ result_name = "eExecutionDiscarded";
+ break;
+ case eExecutionInterrupted:
+ result_name = "eExecutionInterrupted";
+ break;
+ case eExecutionHitBreakpoint:
+ result_name = "eExecutionHitBreakpoint";
+ break;
+ case eExecutionSetupError:
+ result_name = "eExecutionSetupError";
+ break;
+ case eExecutionTimedOut:
+ result_name = "eExecutionTimedOut";
+ break;
+ }
+ return result_name;
+}
+
+void
+Process::GetStatus (Stream &strm)
+{
+ const StateType state = GetState();
+ if (StateIsStoppedState(state, false))
+ {
+ if (state == eStateExited)
+ {
+ int exit_status = GetExitStatus();
+ const char *exit_description = GetExitDescription();
+ strm.Printf ("Process %" PRIu64 " exited with status = %i (0x%8.8x) %s\n",
+ GetID(),
+ exit_status,
+ exit_status,
+ exit_description ? exit_description : "");
+ }
+ else
+ {
+ if (state == eStateConnected)
+ strm.Printf ("Connected to remote target.\n");
+ else
+ strm.Printf ("Process %" PRIu64 " %s\n", GetID(), StateAsCString (state));
+ }
+ }
+ else
+ {
+ strm.Printf ("Process %" PRIu64 " is running.\n", GetID());
+ }
+}
+
+size_t
+Process::GetThreadStatus (Stream &strm,
+ bool only_threads_with_stop_reason,
+ uint32_t start_frame,
+ uint32_t num_frames,
+ uint32_t num_frames_with_source)
+{
+ size_t num_thread_infos_dumped = 0;
+
+ Mutex::Locker locker (GetThreadList().GetMutex());
+ const size_t num_threads = GetThreadList().GetSize();
+ for (uint32_t i = 0; i < num_threads; i++)
+ {
+ Thread *thread = GetThreadList().GetThreadAtIndex(i).get();
+ if (thread)
+ {
+ if (only_threads_with_stop_reason)
+ {
+ StopInfoSP stop_info_sp = thread->GetStopInfo();
+ if (stop_info_sp.get() == NULL || !stop_info_sp->IsValid())
+ continue;
+ }
+ thread->GetStatus (strm,
+ start_frame,
+ num_frames,
+ num_frames_with_source);
+ ++num_thread_infos_dumped;
+ }
+ }
+ return num_thread_infos_dumped;
+}
+
+void
+Process::AddInvalidMemoryRegion (const LoadRange &region)
+{
+ m_memory_cache.AddInvalidRange(region.GetRangeBase(), region.GetByteSize());
+}
+
+bool
+Process::RemoveInvalidMemoryRange (const LoadRange &region)
+{
+ return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), region.GetByteSize());
+}
+
+void
+Process::AddPreResumeAction (PreResumeActionCallback callback, void *baton)
+{
+ m_pre_resume_actions.push_back(PreResumeCallbackAndBaton (callback, baton));
+}
+
+bool
+Process::RunPreResumeActions ()
+{
+ bool result = true;
+ while (!m_pre_resume_actions.empty())
+ {
+ struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back();
+ m_pre_resume_actions.pop_back();
+ bool this_result = action.callback (action.baton);
+ if (result == true) result = this_result;
+ }
+ return result;
+}
+
+void
+Process::ClearPreResumeActions ()
+{
+ m_pre_resume_actions.clear();
+}
+
+void
+Process::Flush ()
+{
+ m_thread_list.Flush();
+}
+
+void
+Process::DidExec ()
+{
+ Target &target = GetTarget();
+ target.CleanupProcess ();
+ ModuleList unloaded_modules (target.GetImages());
+ target.ModulesDidUnload (unloaded_modules);
+ target.GetSectionLoadList().Clear();
+ m_dynamic_checkers_ap.reset();
+ m_abi_sp.reset();
+ m_os_ap.reset();
+ m_dyld_ap.reset();
+ m_image_tokens.clear();
+ m_allocated_memory_cache.Clear();
+ m_language_runtimes.clear();
+ m_thread_list.DiscardThreadPlans();
+ m_memory_cache.Clear(true);
+ DoDidExec();
+ CompleteAttach ();
+}
diff --git a/source/Target/RegisterContext.cpp b/source/Target/RegisterContext.cpp
new file mode 100644
index 000000000000..0d89db724ab4
--- /dev/null
+++ b/source/Target/RegisterContext.cpp
@@ -0,0 +1,600 @@
+//===-- RegisterContext.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Core/DataExtractor.h"
+#include "lldb/Core/RegisterValue.h"
+#include "lldb/Core/Scalar.h"
+#include "lldb/Host/Endian.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+RegisterContext::RegisterContext (Thread &thread, uint32_t concrete_frame_idx) :
+ m_thread (thread),
+ m_concrete_frame_idx (concrete_frame_idx),
+ m_stop_id (thread.GetProcess()->GetStopID())
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+RegisterContext::~RegisterContext()
+{
+}
+
+void
+RegisterContext::InvalidateIfNeeded (bool force)
+{
+ ProcessSP process_sp (m_thread.GetProcess());
+ bool invalidate = force;
+ uint32_t process_stop_id = UINT32_MAX;
+
+ if (process_sp)
+ process_stop_id = process_sp->GetStopID();
+ else
+ invalidate = true;
+
+ if (!invalidate)
+ invalidate = process_stop_id != GetStopID();
+
+ if (invalidate)
+ {
+ InvalidateAllRegisters ();
+ SetStopID (process_stop_id);
+ }
+}
+
+
+const RegisterInfo *
+RegisterContext::GetRegisterInfoByName (const char *reg_name, uint32_t start_idx)
+{
+ if (reg_name && reg_name[0])
+ {
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = start_idx; reg < num_registers; ++reg)
+ {
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg);
+
+ if ((reg_info->name != NULL && ::strcasecmp (reg_info->name, reg_name) == 0) ||
+ (reg_info->alt_name != NULL && ::strcasecmp (reg_info->alt_name, reg_name) == 0))
+ {
+ return reg_info;
+ }
+ }
+ }
+ return NULL;
+}
+
+const char *
+RegisterContext::GetRegisterName (uint32_t reg)
+{
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex(reg);
+ if (reg_info)
+ return reg_info->name;
+ return NULL;
+}
+
+uint64_t
+RegisterContext::GetPC(uint64_t fail_value)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ return ReadRegisterAsUnsigned (reg, fail_value);
+}
+
+bool
+RegisterContext::SetPC(uint64_t pc)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
+ bool success = WriteRegisterFromUnsigned (reg, pc);
+ if (success)
+ {
+ StackFrameSP frame_sp(m_thread.GetFrameWithConcreteFrameIndex (m_concrete_frame_idx));
+ if (frame_sp)
+ frame_sp->ChangePC(pc);
+ else
+ m_thread.ClearStackFrames ();
+ }
+ return success;
+}
+
+uint64_t
+RegisterContext::GetSP(uint64_t fail_value)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ return ReadRegisterAsUnsigned (reg, fail_value);
+}
+
+bool
+RegisterContext::SetSP(uint64_t sp)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP);
+ return WriteRegisterFromUnsigned (reg, sp);
+}
+
+uint64_t
+RegisterContext::GetFP(uint64_t fail_value)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP);
+ return ReadRegisterAsUnsigned (reg, fail_value);
+}
+
+bool
+RegisterContext::SetFP(uint64_t fp)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FP);
+ return WriteRegisterFromUnsigned (reg, fp);
+}
+
+uint64_t
+RegisterContext::GetReturnAddress (uint64_t fail_value)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA);
+ return ReadRegisterAsUnsigned (reg, fail_value);
+}
+
+uint64_t
+RegisterContext::GetFlags (uint64_t fail_value)
+{
+ uint32_t reg = ConvertRegisterKindToRegisterNumber (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS);
+ return ReadRegisterAsUnsigned (reg, fail_value);
+}
+
+
+uint64_t
+RegisterContext::ReadRegisterAsUnsigned (uint32_t reg, uint64_t fail_value)
+{
+ if (reg != LLDB_INVALID_REGNUM)
+ return ReadRegisterAsUnsigned (GetRegisterInfoAtIndex (reg), fail_value);
+ return fail_value;
+}
+
+uint64_t
+RegisterContext::ReadRegisterAsUnsigned (const RegisterInfo *reg_info, uint64_t fail_value)
+{
+ if (reg_info)
+ {
+ RegisterValue value;
+ if (ReadRegister (reg_info, value))
+ return value.GetAsUInt64();
+ }
+ return fail_value;
+}
+
+bool
+RegisterContext::WriteRegisterFromUnsigned (uint32_t reg, uint64_t uval)
+{
+ if (reg == LLDB_INVALID_REGNUM)
+ return false;
+ return WriteRegisterFromUnsigned (GetRegisterInfoAtIndex (reg), uval);
+}
+
+bool
+RegisterContext::WriteRegisterFromUnsigned (const RegisterInfo *reg_info, uint64_t uval)
+{
+ if (reg_info)
+ {
+ RegisterValue value;
+ if (value.SetUInt(uval, reg_info->byte_size))
+ return WriteRegister (reg_info, value);
+ }
+ return false;
+}
+
+bool
+RegisterContext::CopyFromRegisterContext (lldb::RegisterContextSP context)
+{
+ uint32_t num_register_sets = context->GetRegisterSetCount();
+ // We don't know that two threads have the same register context, so require the threads to be the same.
+ if (context->GetThreadID() != GetThreadID())
+ return false;
+
+ if (num_register_sets != GetRegisterSetCount())
+ return false;
+
+ RegisterContextSP frame_zero_context = m_thread.GetRegisterContext();
+
+ for (uint32_t set_idx = 0; set_idx < num_register_sets; ++set_idx)
+ {
+ const RegisterSet * const reg_set = GetRegisterSet(set_idx);
+
+ const uint32_t num_registers = reg_set->num_registers;
+ for (uint32_t reg_idx = 0; reg_idx < num_registers; ++reg_idx)
+ {
+ const uint32_t reg = reg_set->registers[reg_idx];
+ const RegisterInfo *reg_info = GetRegisterInfoAtIndex(reg);
+ if (!reg_info || reg_info->value_regs)
+ continue;
+ RegisterValue reg_value;
+
+ // If we can reconstruct the register from the frame we are copying from, then do so, otherwise
+ // use the value from frame 0.
+ if (context->ReadRegister(reg_info, reg_value))
+ {
+ WriteRegister(reg_info, reg_value);
+ }
+ else if (frame_zero_context->ReadRegister(reg_info, reg_value))
+ {
+ WriteRegister(reg_info, reg_value);
+ }
+ }
+ }
+ return true;
+}
+
+lldb::tid_t
+RegisterContext::GetThreadID() const
+{
+ return m_thread.GetID();
+}
+
+uint32_t
+RegisterContext::NumSupportedHardwareBreakpoints ()
+{
+ return 0;
+}
+
+uint32_t
+RegisterContext::SetHardwareBreakpoint (lldb::addr_t addr, size_t size)
+{
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContext::ClearHardwareBreakpoint (uint32_t hw_idx)
+{
+ return false;
+}
+
+
+uint32_t
+RegisterContext::NumSupportedHardwareWatchpoints ()
+{
+ return 0;
+}
+
+uint32_t
+RegisterContext::SetHardwareWatchpoint (lldb::addr_t addr, size_t size, bool read, bool write)
+{
+ return LLDB_INVALID_INDEX32;
+}
+
+bool
+RegisterContext::ClearHardwareWatchpoint (uint32_t hw_index)
+{
+ return false;
+}
+
+bool
+RegisterContext::HardwareSingleStep (bool enable)
+{
+ return false;
+}
+
+Error
+RegisterContext::ReadRegisterValueFromMemory (const RegisterInfo *reg_info,
+ lldb::addr_t src_addr,
+ uint32_t src_len,
+ RegisterValue &reg_value)
+{
+ Error error;
+ if (reg_info == NULL)
+ {
+ error.SetErrorString ("invalid register info argument.");
+ return error;
+ }
+
+
+ // Moving from addr into a register
+ //
+ // Case 1: src_len == dst_len
+ //
+ // |AABBCCDD| Address contents
+ // |AABBCCDD| Register contents
+ //
+ // Case 2: src_len > dst_len
+ //
+ // Error! (The register should always be big enough to hold the data)
+ //
+ // Case 3: src_len < dst_len
+ //
+ // |AABB| Address contents
+ // |AABB0000| Register contents [on little-endian hardware]
+ // |0000AABB| Register contents [on big-endian hardware]
+ if (src_len > RegisterValue::kMaxRegisterByteSize)
+ {
+ error.SetErrorString ("register too small to receive memory data");
+ return error;
+ }
+
+ const uint32_t dst_len = reg_info->byte_size;
+
+ if (src_len > dst_len)
+ {
+ error.SetErrorStringWithFormat("%u bytes is too big to store in register %s (%u bytes)", src_len, reg_info->name, dst_len);
+ return error;
+ }
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+ uint8_t src[RegisterValue::kMaxRegisterByteSize];
+
+ // Read the memory
+ const uint32_t bytes_read = process_sp->ReadMemory (src_addr, src, src_len, error);
+
+ // Make sure the memory read succeeded...
+ if (bytes_read != src_len)
+ {
+ if (error.Success())
+ {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("read %u of %u bytes", bytes_read, src_len);
+ }
+ return error;
+ }
+
+ // We now have a memory buffer that contains the part or all of the register
+ // value. Set the register value using this memory data.
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are assuming
+ // they are the same.
+ reg_value.SetFromMemoryData (reg_info,
+ src,
+ src_len,
+ process_sp->GetByteOrder(),
+ error);
+ }
+ else
+ error.SetErrorString("invalid process");
+
+ return error;
+}
+
+Error
+RegisterContext::WriteRegisterValueToMemory (const RegisterInfo *reg_info,
+ lldb::addr_t dst_addr,
+ uint32_t dst_len,
+ const RegisterValue &reg_value)
+{
+
+ uint8_t dst[RegisterValue::kMaxRegisterByteSize];
+
+ Error error;
+
+ ProcessSP process_sp (m_thread.GetProcess());
+ if (process_sp)
+ {
+
+ // TODO: we might need to add a parameter to this function in case the byte
+ // order of the memory data doesn't match the process. For now we are assuming
+ // they are the same.
+
+ const uint32_t bytes_copied = reg_value.GetAsMemoryData (reg_info,
+ dst,
+ dst_len,
+ process_sp->GetByteOrder(),
+ error);
+
+ if (error.Success())
+ {
+ if (bytes_copied == 0)
+ {
+ error.SetErrorString("byte copy failed.");
+ }
+ else
+ {
+ const uint32_t bytes_written = process_sp->WriteMemory (dst_addr, dst, bytes_copied, error);
+ if (bytes_written != bytes_copied)
+ {
+ if (error.Success())
+ {
+ // This might happen if we read _some_ bytes but not all
+ error.SetErrorStringWithFormat("only wrote %u of %u bytes", bytes_written, bytes_copied);
+ }
+ }
+ }
+ }
+ }
+ else
+ error.SetErrorString("invalid process");
+
+ return error;
+
+}
+
+TargetSP
+RegisterContext::CalculateTarget ()
+{
+ return m_thread.CalculateTarget();
+}
+
+
+ProcessSP
+RegisterContext::CalculateProcess ()
+{
+ return m_thread.CalculateProcess ();
+}
+
+ThreadSP
+RegisterContext::CalculateThread ()
+{
+ return m_thread.shared_from_this();
+}
+
+StackFrameSP
+RegisterContext::CalculateStackFrame ()
+{
+ // Register contexts might belong to many frames if we have inlined
+ // functions inside a frame since all inlined functions share the
+ // same registers, so we can't definitively say which frame we come from...
+ return StackFrameSP();
+}
+
+void
+RegisterContext::CalculateExecutionContext (ExecutionContext &exe_ctx)
+{
+ m_thread.CalculateExecutionContext (exe_ctx);
+}
+
+
+bool
+RegisterContext::ConvertBetweenRegisterKinds (int source_rk, uint32_t source_regnum, int target_rk, uint32_t& target_regnum)
+{
+ const uint32_t num_registers = GetRegisterCount();
+ for (uint32_t reg = 0; reg < num_registers; ++reg)
+ {
+ const RegisterInfo * reg_info = GetRegisterInfoAtIndex (reg);
+
+ if (reg_info->kinds[source_rk] == source_regnum)
+ {
+ target_regnum = reg_info->kinds[target_rk];
+ if (target_regnum == LLDB_INVALID_REGNUM)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//bool
+//RegisterContext::ReadRegisterValue (uint32_t reg, Scalar &value)
+//{
+// DataExtractor data;
+// if (!ReadRegisterBytes (reg, data))
+// return false;
+//
+// const RegisterInfo *reg_info = GetRegisterInfoAtIndex (reg);
+// uint32_t offset = 0;
+// switch (reg_info->encoding)
+// {
+// case eEncodingInvalid:
+// case eEncodingVector:
+// break;
+//
+// case eEncodingUint:
+// switch (reg_info->byte_size)
+// {
+// case 1:
+// {
+// value = data.GetU8 (&offset);
+// return true;
+// }
+// case 2:
+// {
+// value = data.GetU16 (&offset);
+// return true;
+// }
+// case 4:
+// {
+// value = data.GetU32 (&offset);
+// return true;
+// }
+// case 8:
+// {
+// value = data.GetU64 (&offset);
+// return true;
+// }
+// }
+// break;
+// case eEncodingSint:
+// switch (reg_info->byte_size)
+// {
+// case 1:
+// {
+// int8_t v;
+// if (data.ExtractBytes (0, sizeof (int8_t), lldb::endian::InlHostByteOrder(), &v) != sizeof (int8_t))
+// return false;
+// value = v;
+// return true;
+// }
+// case 2:
+// {
+// int16_t v;
+// if (data.ExtractBytes (0, sizeof (int16_t), lldb::endian::InlHostByteOrder(), &v) != sizeof (int16_t))
+// return false;
+// value = v;
+// return true;
+// }
+// case 4:
+// {
+// int32_t v;
+// if (data.ExtractBytes (0, sizeof (int32_t), lldb::endian::InlHostByteOrder(), &v) != sizeof (int32_t))
+// return false;
+// value = v;
+// return true;
+// }
+// case 8:
+// {
+// int64_t v;
+// if (data.ExtractBytes (0, sizeof (int64_t), lldb::endian::InlHostByteOrder(), &v) != sizeof (int64_t))
+// return false;
+// value = v;
+// return true;
+// }
+// }
+// break;
+// case eEncodingIEEE754:
+// switch (reg_info->byte_size)
+// {
+// case sizeof (float):
+// {
+// float v;
+// if (data.ExtractBytes (0, sizeof (float), lldb::endian::InlHostByteOrder(), &v) != sizeof (float))
+// return false;
+// value = v;
+// return true;
+// }
+// case sizeof (double):
+// {
+// double v;
+// if (data.ExtractBytes (0, sizeof (double), lldb::endian::InlHostByteOrder(), &v) != sizeof (double))
+// return false;
+// value = v;
+// return true;
+// }
+// case sizeof (long double):
+// {
+// double v;
+// if (data.ExtractBytes (0, sizeof (long double), lldb::endian::InlHostByteOrder(), &v) != sizeof (long double))
+// return false;
+// value = v;
+// return true;
+// }
+// }
+// break;
+// }
+// return false;
+//}
+//
+//bool
+//RegisterContext::WriteRegisterValue (uint32_t reg, const Scalar &value)
+//{
+// DataExtractor data;
+// if (!value.IsValid())
+// return false;
+// if (!value.GetData (data))
+// return false;
+//
+// return WriteRegisterBytes (reg, data);
+//}
diff --git a/source/Target/SectionLoadList.cpp b/source/Target/SectionLoadList.cpp
new file mode 100644
index 000000000000..96713c6ea797
--- /dev/null
+++ b/source/Target/SectionLoadList.cpp
@@ -0,0 +1,276 @@
+//===-- SectionLoadList.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/Target/SectionLoadList.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+bool
+SectionLoadList::IsEmpty() const
+{
+ Mutex::Locker locker(m_mutex);
+ return m_addr_to_sect.empty();
+}
+
+void
+SectionLoadList::Clear ()
+{
+ Mutex::Locker locker(m_mutex);
+ m_addr_to_sect.clear();
+ m_sect_to_addr.clear();
+}
+
+addr_t
+SectionLoadList::GetSectionLoadAddress (const lldb::SectionSP &section) const
+{
+ // TODO: add support for the same section having multiple load addresses
+ addr_t section_load_addr = LLDB_INVALID_ADDRESS;
+ if (section)
+ {
+ Mutex::Locker locker(m_mutex);
+ sect_to_addr_collection::const_iterator pos = m_sect_to_addr.find (section.get());
+
+ if (pos != m_sect_to_addr.end())
+ section_load_addr = pos->second;
+ }
+ return section_load_addr;
+}
+
+bool
+SectionLoadList::SetSectionLoadAddress (const lldb::SectionSP &section, addr_t load_addr, bool warn_multiple)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER | LIBLLDB_LOG_VERBOSE));
+
+ ModuleSP module_sp (section->GetModule());
+
+ if (module_sp)
+ {
+ if (log)
+ {
+ const FileSpec &module_file_spec (module_sp->GetFileSpec());
+ log->Printf ("SectionLoadList::%s (section = %p (%s.%s), load_addr = 0x%16.16" PRIx64 ") module = %p",
+ __FUNCTION__,
+ section.get(),
+ module_file_spec.GetPath().c_str(),
+ section->GetName().AsCString(),
+ load_addr,
+ module_sp.get());
+ }
+
+ if (section->GetByteSize() == 0)
+ return false; // No change
+
+ // Fill in the section -> load_addr map
+ Mutex::Locker locker(m_mutex);
+ sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section.get());
+ if (sta_pos != m_sect_to_addr.end())
+ {
+ if (load_addr == sta_pos->second)
+ return false; // No change...
+ else
+ sta_pos->second = load_addr;
+ }
+ else
+ m_sect_to_addr[section.get()] = load_addr;
+
+ // Fill in the load_addr -> section map
+ addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end())
+ {
+ // Some sections are ok to overlap, and for others we should warn. When
+ // we have multiple load addresses that correspond to a section, we will
+ // allways attribute the section to the be last section that claims it
+ // exists at that address. Sometimes it is ok for more that one section
+ // to be loaded at a specific load address, and other times it isn't.
+ // The "warn_multiple" parameter tells us if we should warn in this case
+ // or not. The DynamicLoader plug-in subclasses should know which
+ // sections should warn and which shouldn't (darwin shared cache modules
+ // all shared the same "__LINKEDIT" sections, so the dynamic loader can
+ // pass false for "warn_multiple").
+ if (warn_multiple && section != ats_pos->second)
+ {
+ ModuleSP module_sp (section->GetModule());
+ if (module_sp)
+ {
+ ModuleSP curr_module_sp (ats_pos->second->GetModule());
+ if (curr_module_sp)
+ {
+ module_sp->ReportWarning ("address 0x%16.16" PRIx64 " maps to more than one section: %s.%s and %s.%s",
+ load_addr,
+ module_sp->GetFileSpec().GetFilename().GetCString(),
+ section->GetName().GetCString(),
+ curr_module_sp->GetFileSpec().GetFilename().GetCString(),
+ ats_pos->second->GetName().GetCString());
+ }
+ }
+ }
+ ats_pos->second = section;
+ }
+ else
+ m_addr_to_sect[load_addr] = section;
+ return true; // Changed
+
+ }
+ else
+ {
+ if (log)
+ {
+ log->Printf ("SectionLoadList::%s (section = %p (%s), load_addr = 0x%16.16" PRIx64 ") error: module has been deleted",
+ __FUNCTION__,
+ section.get(),
+ section->GetName().AsCString(),
+ load_addr);
+ }
+ }
+ return false;
+}
+
+size_t
+SectionLoadList::SetSectionUnloaded (const lldb::SectionSP &section_sp)
+{
+ size_t unload_count = 0;
+
+ if (section_sp)
+ {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ {
+ const FileSpec &module_file_spec (section_sp->GetModule()->GetFileSpec());
+ log->Printf ("SectionLoadList::%s (section = %p (%s.%s))",
+ __FUNCTION__,
+ section_sp.get(),
+ module_file_spec.GetPath().c_str(),
+ section_sp->GetName().AsCString());
+ }
+
+ Mutex::Locker locker(m_mutex);
+
+ sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section_sp.get());
+ if (sta_pos != m_sect_to_addr.end())
+ {
+ ++unload_count;
+ addr_t load_addr = sta_pos->second;
+ m_sect_to_addr.erase (sta_pos);
+
+ addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end())
+ m_addr_to_sect.erase (ats_pos);
+ }
+ }
+ return unload_count;
+}
+
+bool
+SectionLoadList::SetSectionUnloaded (const lldb::SectionSP &section_sp, addr_t load_addr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_DYNAMIC_LOADER | LIBLLDB_LOG_VERBOSE));
+
+ if (log)
+ {
+ const FileSpec &module_file_spec (section_sp->GetModule()->GetFileSpec());
+ log->Printf ("SectionLoadList::%s (section = %p (%s.%s), load_addr = 0x%16.16" PRIx64 ")",
+ __FUNCTION__,
+ section_sp.get(),
+ module_file_spec.GetPath().c_str(),
+ section_sp->GetName().AsCString(),
+ load_addr);
+ }
+ bool erased = false;
+ Mutex::Locker locker(m_mutex);
+ sect_to_addr_collection::iterator sta_pos = m_sect_to_addr.find(section_sp.get());
+ if (sta_pos != m_sect_to_addr.end())
+ {
+ erased = true;
+ m_sect_to_addr.erase (sta_pos);
+ }
+
+ addr_to_sect_collection::iterator ats_pos = m_addr_to_sect.find(load_addr);
+ if (ats_pos != m_addr_to_sect.end())
+ {
+ erased = true;
+ m_addr_to_sect.erase (ats_pos);
+ }
+
+ return erased;
+}
+
+
+bool
+SectionLoadList::ResolveLoadAddress (addr_t load_addr, Address &so_addr) const
+{
+ // First find the top level section that this load address exists in
+ Mutex::Locker locker(m_mutex);
+ if (!m_addr_to_sect.empty())
+ {
+ addr_to_sect_collection::const_iterator pos = m_addr_to_sect.lower_bound (load_addr);
+ if (pos != m_addr_to_sect.end())
+ {
+ if (load_addr != pos->first && pos != m_addr_to_sect.begin())
+ --pos;
+ const addr_t pos_load_addr = pos->first;
+ if (load_addr >= pos_load_addr)
+ {
+ addr_t offset = load_addr - pos_load_addr;
+ if (offset < pos->second->GetByteSize())
+ {
+ // We have found the top level section, now we need to find the
+ // deepest child section.
+ return pos->second->ResolveContainedAddress (offset, so_addr);
+ }
+ }
+ }
+ else
+ {
+ // There are no entries that have an address that is >= load_addr,
+ // so we need to check the last entry on our collection.
+ addr_to_sect_collection::const_reverse_iterator rpos = m_addr_to_sect.rbegin();
+ if (load_addr >= rpos->first)
+ {
+ addr_t offset = load_addr - rpos->first;
+ if (offset < rpos->second->GetByteSize())
+ {
+ // We have found the top level section, now we need to find the
+ // deepest child section.
+ return rpos->second->ResolveContainedAddress (offset, so_addr);
+ }
+ }
+ }
+ }
+ so_addr.Clear();
+ return false;
+}
+
+void
+SectionLoadList::Dump (Stream &s, Target *target)
+{
+ Mutex::Locker locker(m_mutex);
+ addr_to_sect_collection::const_iterator pos, end;
+ for (pos = m_addr_to_sect.begin(), end = m_addr_to_sect.end(); pos != end; ++pos)
+ {
+ s.Printf("addr = 0x%16.16" PRIx64 ", section = %p: ", pos->first, pos->second.get());
+ pos->second->Dump (&s, target, 0);
+ }
+}
+
+
diff --git a/source/Target/StackFrame.cpp b/source/Target/StackFrame.cpp
new file mode 100644
index 000000000000..3c4c43d9f44c
--- /dev/null
+++ b/source/Target/StackFrame.cpp
@@ -0,0 +1,1449 @@
+//===-- StackFrame.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/lldb-python.h"
+
+#include "lldb/Target/StackFrame.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectVariable.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContextScope.h"
+#include "lldb/Symbol/VariableList.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// The first bits in the flags are reserved for the SymbolContext::Scope bits
+// so we know if we have tried to look up information in our internal symbol
+// context (m_sc) already.
+#define RESOLVED_FRAME_CODE_ADDR (uint32_t(eSymbolContextEverything + 1))
+#define RESOLVED_FRAME_ID_SYMBOL_SCOPE (RESOLVED_FRAME_CODE_ADDR << 1)
+#define GOT_FRAME_BASE (RESOLVED_FRAME_ID_SYMBOL_SCOPE << 1)
+#define RESOLVED_VARIABLES (GOT_FRAME_BASE << 1)
+#define RESOLVED_GLOBAL_VARIABLES (RESOLVED_VARIABLES << 1)
+
+StackFrame::StackFrame (const ThreadSP &thread_sp,
+ user_id_t frame_idx,
+ user_id_t unwind_frame_index,
+ addr_t cfa,
+ addr_t pc,
+ const SymbolContext *sc_ptr) :
+ m_thread_wp (thread_sp),
+ m_frame_index (frame_idx),
+ m_concrete_frame_index (unwind_frame_index),
+ m_reg_context_sp (),
+ m_id (pc, cfa, NULL),
+ m_frame_code_addr (pc),
+ m_sc (),
+ m_flags (),
+ m_frame_base (),
+ m_frame_base_error (),
+ m_variable_list_sp (),
+ m_variable_list_value_objects (),
+ m_disassembly ()
+{
+ if (sc_ptr != NULL)
+ {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask ());
+ }
+}
+
+StackFrame::StackFrame (const ThreadSP &thread_sp,
+ user_id_t frame_idx,
+ user_id_t unwind_frame_index,
+ const RegisterContextSP &reg_context_sp,
+ addr_t cfa,
+ addr_t pc,
+ const SymbolContext *sc_ptr) :
+ m_thread_wp (thread_sp),
+ m_frame_index (frame_idx),
+ m_concrete_frame_index (unwind_frame_index),
+ m_reg_context_sp (reg_context_sp),
+ m_id (pc, cfa, NULL),
+ m_frame_code_addr (pc),
+ m_sc (),
+ m_flags (),
+ m_frame_base (),
+ m_frame_base_error (),
+ m_variable_list_sp (),
+ m_variable_list_value_objects (),
+ m_disassembly ()
+{
+ if (sc_ptr != NULL)
+ {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask ());
+ }
+
+ if (reg_context_sp && !m_sc.target_sp)
+ {
+ m_sc.target_sp = reg_context_sp->CalculateTarget();
+ if (m_sc.target_sp)
+ m_flags.Set (eSymbolContextTarget);
+ }
+}
+
+StackFrame::StackFrame (const ThreadSP &thread_sp,
+ user_id_t frame_idx,
+ user_id_t unwind_frame_index,
+ const RegisterContextSP &reg_context_sp,
+ addr_t cfa,
+ const Address& pc_addr,
+ const SymbolContext *sc_ptr) :
+ m_thread_wp (thread_sp),
+ m_frame_index (frame_idx),
+ m_concrete_frame_index (unwind_frame_index),
+ m_reg_context_sp (reg_context_sp),
+ m_id (pc_addr.GetLoadAddress (thread_sp->CalculateTarget().get()), cfa, NULL),
+ m_frame_code_addr (pc_addr),
+ m_sc (),
+ m_flags (),
+ m_frame_base (),
+ m_frame_base_error (),
+ m_variable_list_sp (),
+ m_variable_list_value_objects (),
+ m_disassembly ()
+{
+ if (sc_ptr != NULL)
+ {
+ m_sc = *sc_ptr;
+ m_flags.Set(m_sc.GetResolvedMask ());
+ }
+
+ if (m_sc.target_sp.get() == NULL && reg_context_sp)
+ {
+ m_sc.target_sp = reg_context_sp->CalculateTarget();
+ if (m_sc.target_sp)
+ m_flags.Set (eSymbolContextTarget);
+ }
+
+ ModuleSP pc_module_sp (pc_addr.GetModule());
+ if (!m_sc.module_sp || m_sc.module_sp != pc_module_sp)
+ {
+ if (pc_module_sp)
+ {
+ m_sc.module_sp = pc_module_sp;
+ m_flags.Set (eSymbolContextModule);
+ }
+ else
+ {
+ m_sc.module_sp.reset();
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+StackFrame::~StackFrame()
+{
+}
+
+StackID&
+StackFrame::GetStackID()
+{
+ // Make sure we have resolved the StackID object's symbol context scope if
+ // we already haven't looked it up.
+
+ if (m_flags.IsClear (RESOLVED_FRAME_ID_SYMBOL_SCOPE))
+ {
+ if (m_id.GetSymbolContextScope ())
+ {
+ // We already have a symbol context scope, we just don't have our
+ // flag bit set.
+ m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
+ }
+ else
+ {
+ // Calculate the frame block and use this for the stack ID symbol
+ // context scope if we have one.
+ SymbolContextScope *scope = GetFrameBlock ();
+ if (scope == NULL)
+ {
+ // We don't have a block, so use the symbol
+ if (m_flags.IsClear (eSymbolContextSymbol))
+ GetSymbolContext (eSymbolContextSymbol);
+
+ // It is ok if m_sc.symbol is NULL here
+ scope = m_sc.symbol;
+ }
+ // Set the symbol context scope (the accessor will set the
+ // RESOLVED_FRAME_ID_SYMBOL_SCOPE bit in m_flags).
+ SetSymbolContextScope (scope);
+ }
+ }
+ return m_id;
+}
+
+uint32_t
+StackFrame::GetFrameIndex () const
+{
+ ThreadSP thread_sp = GetThread();
+ if (thread_sp)
+ return thread_sp->GetStackFrameList()->GetVisibleStackFrameIndex(m_frame_index);
+ else
+ return m_frame_index;
+}
+
+void
+StackFrame::SetSymbolContextScope (SymbolContextScope *symbol_scope)
+{
+ m_flags.Set (RESOLVED_FRAME_ID_SYMBOL_SCOPE);
+ m_id.SetSymbolContextScope (symbol_scope);
+}
+
+const Address&
+StackFrame::GetFrameCodeAddress()
+{
+ if (m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR) && !m_frame_code_addr.IsSectionOffset())
+ {
+ m_flags.Set (RESOLVED_FRAME_CODE_ADDR);
+
+ // Resolve the PC into a temporary address because if ResolveLoadAddress
+ // fails to resolve the address, it will clear the address object...
+ ThreadSP thread_sp (GetThread());
+ if (thread_sp)
+ {
+ TargetSP target_sp (thread_sp->CalculateTarget());
+ if (target_sp)
+ {
+ if (m_frame_code_addr.SetOpcodeLoadAddress (m_frame_code_addr.GetOffset(), target_sp.get()))
+ {
+ ModuleSP module_sp (m_frame_code_addr.GetModule());
+ if (module_sp)
+ {
+ m_sc.module_sp = module_sp;
+ m_flags.Set(eSymbolContextModule);
+ }
+ }
+ }
+ }
+ }
+ return m_frame_code_addr;
+}
+
+void
+StackFrame::ChangePC (addr_t pc)
+{
+ m_frame_code_addr.SetRawAddress(pc);
+ m_sc.Clear(false);
+ m_flags.Reset(0);
+ ThreadSP thread_sp (GetThread());
+ if (thread_sp)
+ thread_sp->ClearStackFrames ();
+}
+
+const char *
+StackFrame::Disassemble ()
+{
+ if (m_disassembly.GetSize() == 0)
+ {
+ ExecutionContext exe_ctx (shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target)
+ {
+ const char *plugin_name = NULL;
+ const char *flavor = NULL;
+ Disassembler::Disassemble (target->GetDebugger(),
+ target->GetArchitecture(),
+ plugin_name,
+ flavor,
+ exe_ctx,
+ 0,
+ 0,
+ 0,
+ m_disassembly);
+ }
+ if (m_disassembly.GetSize() == 0)
+ return NULL;
+ }
+ return m_disassembly.GetData();
+}
+
+Block *
+StackFrame::GetFrameBlock ()
+{
+ if (m_sc.block == NULL && m_flags.IsClear (eSymbolContextBlock))
+ GetSymbolContext (eSymbolContextBlock);
+
+ if (m_sc.block)
+ {
+ Block *inline_block = m_sc.block->GetContainingInlinedBlock();
+ if (inline_block)
+ {
+ // Use the block with the inlined function info
+ // as the frame block we want this frame to have only the variables
+ // for the inlined function and its non-inlined block child blocks.
+ return inline_block;
+ }
+ else
+ {
+ // This block is not contained withing any inlined function blocks
+ // with so we want to use the top most function block.
+ return &m_sc.function->GetBlock (false);
+ }
+ }
+ return NULL;
+}
+
+//----------------------------------------------------------------------
+// Get the symbol context if we already haven't done so by resolving the
+// PC address as much as possible. This way when we pass around a
+// StackFrame object, everyone will have as much information as
+// possible and no one will ever have to look things up manually.
+//----------------------------------------------------------------------
+const SymbolContext&
+StackFrame::GetSymbolContext (uint32_t resolve_scope)
+{
+ // Copy our internal symbol context into "sc".
+ if ((m_flags.Get() & resolve_scope) != resolve_scope)
+ {
+ uint32_t resolved = 0;
+
+ // If the target was requested add that:
+ if (!m_sc.target_sp)
+ {
+ m_sc.target_sp = CalculateTarget();
+ if (m_sc.target_sp)
+ resolved |= eSymbolContextTarget;
+ }
+
+
+ // Resolve our PC to section offset if we haven't alreday done so
+ // and if we don't have a module. The resolved address section will
+ // contain the module to which it belongs
+ if (!m_sc.module_sp && m_flags.IsClear(RESOLVED_FRAME_CODE_ADDR))
+ GetFrameCodeAddress();
+
+ // If this is not frame zero, then we need to subtract 1 from the PC
+ // value when doing address lookups since the PC will be on the
+ // instruction following the function call instruction...
+
+ Address lookup_addr(GetFrameCodeAddress());
+ if (m_frame_index > 0 && lookup_addr.IsValid())
+ {
+ addr_t offset = lookup_addr.GetOffset();
+ if (offset > 0)
+ lookup_addr.SetOffset(offset - 1);
+ }
+
+
+ if (m_sc.module_sp)
+ {
+ // We have something in our stack frame symbol context, lets check
+ // if we haven't already tried to lookup one of those things. If we
+ // haven't then we will do the query.
+
+ uint32_t actual_resolve_scope = 0;
+
+ if (resolve_scope & eSymbolContextCompUnit)
+ {
+ if (m_flags.IsClear (eSymbolContextCompUnit))
+ {
+ if (m_sc.comp_unit)
+ resolved |= eSymbolContextCompUnit;
+ else
+ actual_resolve_scope |= eSymbolContextCompUnit;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextFunction)
+ {
+ if (m_flags.IsClear (eSymbolContextFunction))
+ {
+ if (m_sc.function)
+ resolved |= eSymbolContextFunction;
+ else
+ actual_resolve_scope |= eSymbolContextFunction;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextBlock)
+ {
+ if (m_flags.IsClear (eSymbolContextBlock))
+ {
+ if (m_sc.block)
+ resolved |= eSymbolContextBlock;
+ else
+ actual_resolve_scope |= eSymbolContextBlock;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextSymbol)
+ {
+ if (m_flags.IsClear (eSymbolContextSymbol))
+ {
+ if (m_sc.symbol)
+ resolved |= eSymbolContextSymbol;
+ else
+ actual_resolve_scope |= eSymbolContextSymbol;
+ }
+ }
+
+ if (resolve_scope & eSymbolContextLineEntry)
+ {
+ if (m_flags.IsClear (eSymbolContextLineEntry))
+ {
+ if (m_sc.line_entry.IsValid())
+ resolved |= eSymbolContextLineEntry;
+ else
+ actual_resolve_scope |= eSymbolContextLineEntry;
+ }
+ }
+
+ if (actual_resolve_scope)
+ {
+ // We might be resolving less information than what is already
+ // in our current symbol context so resolve into a temporary
+ // symbol context "sc" so we don't clear out data we have
+ // already found in "m_sc"
+ SymbolContext sc;
+ // Set flags that indicate what we have tried to resolve
+ resolved |= m_sc.module_sp->ResolveSymbolContextForAddress (lookup_addr, actual_resolve_scope, sc);
+ // Only replace what we didn't already have as we may have
+ // information for an inlined function scope that won't match
+ // what a standard lookup by address would match
+ if ((resolved & eSymbolContextCompUnit) && m_sc.comp_unit == NULL)
+ m_sc.comp_unit = sc.comp_unit;
+ if ((resolved & eSymbolContextFunction) && m_sc.function == NULL)
+ m_sc.function = sc.function;
+ if ((resolved & eSymbolContextBlock) && m_sc.block == NULL)
+ m_sc.block = sc.block;
+ if ((resolved & eSymbolContextSymbol) && m_sc.symbol == NULL)
+ m_sc.symbol = sc.symbol;
+ if ((resolved & eSymbolContextLineEntry) && !m_sc.line_entry.IsValid())
+ {
+ m_sc.line_entry = sc.line_entry;
+ if (m_sc.target_sp)
+ {
+ // Be sure to apply and file remappings to our file and line
+ // entries when handing out a line entry
+ FileSpec new_file_spec;
+ if (m_sc.target_sp->GetSourcePathMap().FindFile (m_sc.line_entry.file, new_file_spec))
+ m_sc.line_entry.file = new_file_spec;
+ }
+ }
+ }
+ }
+ else
+ {
+ // If we don't have a module, then we can't have the compile unit,
+ // function, block, line entry or symbol, so we can safely call
+ // ResolveSymbolContextForAddress with our symbol context member m_sc.
+ if (m_sc.target_sp)
+ {
+ resolved |= m_sc.target_sp->GetImages().ResolveSymbolContextForAddress (lookup_addr, resolve_scope, m_sc);
+ }
+ }
+
+ // Update our internal flags so we remember what we have tried to locate so
+ // we don't have to keep trying when more calls to this function are made.
+ // We might have dug up more information that was requested (for example
+ // if we were asked to only get the block, we will have gotten the
+ // compile unit, and function) so set any additional bits that we resolved
+ m_flags.Set (resolve_scope | resolved);
+ }
+
+ // Return the symbol context with everything that was possible to resolve
+ // resolved.
+ return m_sc;
+}
+
+
+VariableList *
+StackFrame::GetVariableList (bool get_file_globals)
+{
+ if (m_flags.IsClear(RESOLVED_VARIABLES))
+ {
+ m_flags.Set(RESOLVED_VARIABLES);
+
+ Block *frame_block = GetFrameBlock();
+
+ if (frame_block)
+ {
+ const bool get_child_variables = true;
+ const bool can_create = true;
+ const bool stop_if_child_block_is_inlined_function = true;
+ m_variable_list_sp.reset(new VariableList());
+ frame_block->AppendBlockVariables(can_create, get_child_variables, stop_if_child_block_is_inlined_function, m_variable_list_sp.get());
+ }
+ }
+
+ if (m_flags.IsClear(RESOLVED_GLOBAL_VARIABLES) &&
+ get_file_globals)
+ {
+ m_flags.Set(RESOLVED_GLOBAL_VARIABLES);
+
+ if (m_flags.IsClear (eSymbolContextCompUnit))
+ GetSymbolContext (eSymbolContextCompUnit);
+
+ if (m_sc.comp_unit)
+ {
+ VariableListSP global_variable_list_sp (m_sc.comp_unit->GetVariableList(true));
+ if (m_variable_list_sp)
+ m_variable_list_sp->AddVariables (global_variable_list_sp.get());
+ else
+ m_variable_list_sp = global_variable_list_sp;
+ }
+ }
+
+ return m_variable_list_sp.get();
+}
+
+VariableListSP
+StackFrame::GetInScopeVariableList (bool get_file_globals)
+{
+ VariableListSP var_list_sp(new VariableList);
+ GetSymbolContext (eSymbolContextCompUnit | eSymbolContextBlock);
+
+ if (m_sc.block)
+ {
+ const bool can_create = true;
+ const bool get_parent_variables = true;
+ const bool stop_if_block_is_inlined_function = true;
+ m_sc.block->AppendVariables (can_create,
+ get_parent_variables,
+ stop_if_block_is_inlined_function,
+ var_list_sp.get());
+ }
+
+ if (m_sc.comp_unit)
+ {
+ VariableListSP global_variable_list_sp (m_sc.comp_unit->GetVariableList(true));
+ if (global_variable_list_sp)
+ var_list_sp->AddVariables (global_variable_list_sp.get());
+ }
+
+ return var_list_sp;
+}
+
+
+ValueObjectSP
+StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
+ DynamicValueType use_dynamic,
+ uint32_t options,
+ VariableSP &var_sp,
+ Error &error)
+{
+
+ if (var_expr_cstr && var_expr_cstr[0])
+ {
+ const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0;
+ const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
+ const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
+ //const bool no_synth_array = (options & eExpressionPathOptionsNoSyntheticArrayRange) != 0;
+ error.Clear();
+ bool deref = false;
+ bool address_of = false;
+ ValueObjectSP valobj_sp;
+ const bool get_file_globals = true;
+ // When looking up a variable for an expression, we need only consider the
+ // variables that are in scope.
+ VariableListSP var_list_sp (GetInScopeVariableList (get_file_globals));
+ VariableList *variable_list = var_list_sp.get();
+
+ if (variable_list)
+ {
+ // If first character is a '*', then show pointer contents
+ const char *var_expr = var_expr_cstr;
+ if (var_expr[0] == '*')
+ {
+ deref = true;
+ var_expr++; // Skip the '*'
+ }
+ else if (var_expr[0] == '&')
+ {
+ address_of = true;
+ var_expr++; // Skip the '&'
+ }
+
+ std::string var_path (var_expr);
+ size_t separator_idx = var_path.find_first_of(".-[=+~|&^%#@!/?,<>{}");
+ StreamString var_expr_path_strm;
+
+ ConstString name_const_string;
+ if (separator_idx == std::string::npos)
+ name_const_string.SetCString (var_path.c_str());
+ else
+ name_const_string.SetCStringWithLength (var_path.c_str(), separator_idx);
+
+ var_sp = variable_list->FindVariable(name_const_string);
+
+ bool synthetically_added_instance_object = false;
+
+ if (var_sp)
+ {
+ var_path.erase (0, name_const_string.GetLength ());
+ }
+ else if (options & eExpressionPathOptionsAllowDirectIVarAccess)
+ {
+ // Check for direct ivars access which helps us with implicit
+ // access to ivars with the "this->" or "self->"
+ GetSymbolContext(eSymbolContextFunction|eSymbolContextBlock);
+ lldb::LanguageType method_language = eLanguageTypeUnknown;
+ bool is_instance_method = false;
+ ConstString method_object_name;
+ if (m_sc.GetFunctionMethodInfo (method_language, is_instance_method, method_object_name))
+ {
+ if (is_instance_method && method_object_name)
+ {
+ var_sp = variable_list->FindVariable(method_object_name);
+ if (var_sp)
+ {
+ separator_idx = 0;
+ var_path.insert(0, "->");
+ synthetically_added_instance_object = true;
+ }
+ }
+ }
+ }
+
+ if (var_sp)
+ {
+ valobj_sp = GetValueObjectForFrameVariable (var_sp, use_dynamic);
+ if (!valobj_sp)
+ return valobj_sp;
+
+ // We are dumping at least one child
+ while (separator_idx != std::string::npos)
+ {
+ // Calculate the next separator index ahead of time
+ ValueObjectSP child_valobj_sp;
+ const char separator_type = var_path[0];
+ switch (separator_type)
+ {
+
+ case '-':
+ if (var_path.size() >= 2 && var_path[1] != '>')
+ return ValueObjectSP();
+
+ if (no_fragile_ivar)
+ {
+ // Make sure we aren't trying to deref an objective
+ // C ivar if this is not allowed
+ const uint32_t pointer_type_flags = valobj_sp->GetClangType().GetTypeInfo (NULL);
+ if ((pointer_type_flags & ClangASTType::eTypeIsObjC) &&
+ (pointer_type_flags & ClangASTType::eTypeIsPointer))
+ {
+ // This was an objective C object pointer and
+ // it was requested we skip any fragile ivars
+ // so return nothing here
+ return ValueObjectSP();
+ }
+ }
+ var_path.erase (0, 1); // Remove the '-'
+ // Fall through
+ case '.':
+ {
+ const bool expr_is_ptr = var_path[0] == '>';
+
+ var_path.erase (0, 1); // Remove the '.' or '>'
+ separator_idx = var_path.find_first_of(".-[");
+ ConstString child_name;
+ if (separator_idx == std::string::npos)
+ child_name.SetCString (var_path.c_str());
+ else
+ child_name.SetCStringWithLength(var_path.c_str(), separator_idx);
+
+ if (check_ptr_vs_member)
+ {
+ // We either have a pointer type and need to verify
+ // valobj_sp is a pointer, or we have a member of a
+ // class/union/struct being accessed with the . syntax
+ // and need to verify we don't have a pointer.
+ const bool actual_is_ptr = valobj_sp->IsPointerType ();
+
+ if (actual_is_ptr != expr_is_ptr)
+ {
+ // Incorrect use of "." with a pointer, or "->" with
+ // a class/union/struct instance or reference.
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ if (actual_is_ptr)
+ error.SetErrorStringWithFormat ("\"%s\" is a pointer and . was used to attempt to access \"%s\". Did you mean \"%s->%s\"?",
+ var_expr_path_strm.GetString().c_str(),
+ child_name.GetCString(),
+ var_expr_path_strm.GetString().c_str(),
+ var_path.c_str());
+ else
+ error.SetErrorStringWithFormat ("\"%s\" is not a pointer and -> was used to attempt to access \"%s\". Did you mean \"%s.%s\"?",
+ var_expr_path_strm.GetString().c_str(),
+ child_name.GetCString(),
+ var_expr_path_strm.GetString().c_str(),
+ var_path.c_str());
+ return ValueObjectSP();
+ }
+ }
+ child_valobj_sp = valobj_sp->GetChildMemberWithName (child_name, true);
+ if (!child_valobj_sp)
+ {
+ if (no_synth_child == false)
+ {
+ child_valobj_sp = valobj_sp->GetSyntheticValue();
+ if (child_valobj_sp)
+ child_valobj_sp = child_valobj_sp->GetChildMemberWithName (child_name, true);
+ }
+
+ if (no_synth_child || !child_valobj_sp)
+ {
+ // No child member with name "child_name"
+ if (synthetically_added_instance_object)
+ {
+ // We added a "this->" or "self->" to the beginning of the expression
+ // and this is the first pointer ivar access, so just return the normal
+ // error
+ error.SetErrorStringWithFormat("no variable or instance variable named '%s' found in this frame",
+ name_const_string.GetCString());
+ }
+ else
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ if (child_name)
+ {
+ error.SetErrorStringWithFormat ("\"%s\" is not a member of \"(%s) %s\"",
+ child_name.GetCString(),
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("incomplete expression path after \"%s\" in \"%s\"",
+ var_expr_path_strm.GetString().c_str(),
+ var_expr_cstr);
+ }
+ }
+ return ValueObjectSP();
+ }
+ }
+ synthetically_added_instance_object = false;
+ // Remove the child name from the path
+ var_path.erase(0, child_name.GetLength());
+ if (use_dynamic != eNoDynamicValues)
+ {
+ ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ }
+ break;
+
+ case '[':
+ // Array member access, or treating pointer as an array
+ if (var_path.size() > 2) // Need at least two brackets and a number
+ {
+ char *end = NULL;
+ long child_index = ::strtol (&var_path[1], &end, 0);
+ if (end && *end == ']'
+ && *(end-1) != '[') // this code forces an error in the case of arr[]. as bitfield[] is not a good syntax we're good to go
+ {
+ if (valobj_sp->GetClangType().IsPointerToScalarType() && deref)
+ {
+ // what we have is *ptr[low]. the most similar C++ syntax is to deref ptr
+ // and extract bit low out of it. reading array item low
+ // would be done by saying ptr[low], without a deref * sign
+ Error error;
+ ValueObjectSP temp(valobj_sp->Dereference(error));
+ if (error.Fail())
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("could not dereference \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+ else if (valobj_sp->GetClangType().IsArrayOfScalarType() && deref)
+ {
+ // what we have is *arr[low]. the most similar C++ syntax is to get arr[0]
+ // (an operation that is equivalent to deref-ing arr)
+ // and extract bit low out of it. reading array item low
+ // would be done by saying arr[low], without a deref * sign
+ Error error;
+ ValueObjectSP temp(valobj_sp->GetChildAtIndex (0, true));
+ if (error.Fail())
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("could not get item 0 for \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+
+ bool is_incomplete_array = false;
+ if (valobj_sp->IsPointerType ())
+ {
+ bool is_objc_pointer = true;
+
+ if (valobj_sp->GetClangType().GetMinimumLanguage() != eLanguageTypeObjC)
+ is_objc_pointer = false;
+ else if (!valobj_sp->GetClangType().IsPointerType())
+ is_objc_pointer = false;
+
+ if (no_synth_child && is_objc_pointer)
+ {
+ error.SetErrorStringWithFormat("\"(%s) %s\" is an Objective-C pointer, and cannot be subscripted",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+
+ return ValueObjectSP();
+ }
+ else if (is_objc_pointer)
+ {
+ // dereferencing ObjC variables is not valid.. so let's try and recur to synthetic children
+ ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
+ if (synthetic.get() == NULL /* no synthetic */
+ || synthetic == valobj_sp) /* synthetic is the same as the original object */
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("\"(%s) %s\" is not an array type",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ else if (child_index >= synthetic->GetNumChildren() /* synthetic does not have that many values */)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ else
+ {
+ child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ }
+ else
+ {
+ child_valobj_sp = valobj_sp->GetSyntheticArrayMemberFromPointer (child_index, true);
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("failed to use pointer as array for index %ld for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ }
+ else if (valobj_sp->GetClangType().IsArrayType (NULL, NULL, &is_incomplete_array))
+ {
+ // Pass false to dynamic_value here so we can tell the difference between
+ // no dynamic value and no member of this type...
+ child_valobj_sp = valobj_sp->GetChildAtIndex (child_index, true);
+ if (!child_valobj_sp && (is_incomplete_array || no_synth_child == false))
+ child_valobj_sp = valobj_sp->GetSyntheticArrayMember (child_index, true);
+
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ else if (valobj_sp->GetClangType().IsScalarType())
+ {
+ // this is a bitfield asking to display just one bit
+ child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, child_index, true);
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("bitfield range %ld-%ld is not valid for \"(%s) %s\"",
+ child_index, child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ else
+ {
+ ValueObjectSP synthetic = valobj_sp->GetSyntheticValue();
+ if (no_synth_child /* synthetic is forbidden */ ||
+ synthetic.get() == NULL /* no synthetic */
+ || synthetic == valobj_sp) /* synthetic is the same as the original object */
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("\"(%s) %s\" is not an array type",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ else if (child_index >= synthetic->GetNumChildren() /* synthetic does not have that many values */)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ else
+ {
+ child_valobj_sp = synthetic->GetChildAtIndex(child_index, true);
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array index %ld is not valid for \"(%s) %s\"",
+ child_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ }
+
+ if (!child_valobj_sp)
+ {
+ // Invalid array index...
+ return ValueObjectSP();
+ }
+
+ // Erase the array member specification '[%i]' where
+ // %i is the array index
+ var_path.erase(0, (end - var_path.c_str()) + 1);
+ separator_idx = var_path.find_first_of(".-[");
+ if (use_dynamic != eNoDynamicValues)
+ {
+ ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ // Break out early from the switch since we were
+ // able to find the child member
+ break;
+ }
+ else if (end && *end == '-')
+ {
+ // this is most probably a BitField, let's take a look
+ char *real_end = NULL;
+ long final_index = ::strtol (end+1, &real_end, 0);
+ bool expand_bitfield = true;
+ if (real_end && *real_end == ']')
+ {
+ // if the format given is [high-low], swap range
+ if (child_index > final_index)
+ {
+ long temp = child_index;
+ child_index = final_index;
+ final_index = temp;
+ }
+
+ if (valobj_sp->GetClangType().IsPointerToScalarType() && deref)
+ {
+ // what we have is *ptr[low-high]. the most similar C++ syntax is to deref ptr
+ // and extract bits low thru high out of it. reading array items low thru high
+ // would be done by saying ptr[low-high], without a deref * sign
+ Error error;
+ ValueObjectSP temp(valobj_sp->Dereference(error));
+ if (error.Fail())
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("could not dereference \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+ else if (valobj_sp->GetClangType().IsArrayOfScalarType() && deref)
+ {
+ // what we have is *arr[low-high]. the most similar C++ syntax is to get arr[0]
+ // (an operation that is equivalent to deref-ing arr)
+ // and extract bits low thru high out of it. reading array items low thru high
+ // would be done by saying arr[low-high], without a deref * sign
+ Error error;
+ ValueObjectSP temp(valobj_sp->GetChildAtIndex (0, true));
+ if (error.Fail())
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("could not get item 0 for \"(%s) %s\"",
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ return ValueObjectSP();
+ }
+ valobj_sp = temp;
+ deref = false;
+ }
+ /*else if (valobj_sp->IsArrayType() || valobj_sp->IsPointerType())
+ {
+ child_valobj_sp = valobj_sp->GetSyntheticArrayRangeChild(child_index, final_index, true);
+ expand_bitfield = false;
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("array range %i-%i is not valid for \"(%s) %s\"",
+ child_index, final_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }*/
+
+ if (expand_bitfield)
+ {
+ child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
+ if (!child_valobj_sp)
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("bitfield range %ld-%ld is not valid for \"(%s) %s\"",
+ child_index, final_index,
+ valobj_sp->GetTypeName().AsCString("<invalid type>"),
+ var_expr_path_strm.GetString().c_str());
+ }
+ }
+ }
+
+ if (!child_valobj_sp)
+ {
+ // Invalid bitfield range...
+ return ValueObjectSP();
+ }
+
+ // Erase the bitfield member specification '[%i-%i]' where
+ // %i is the index
+ var_path.erase(0, (real_end - var_path.c_str()) + 1);
+ separator_idx = var_path.find_first_of(".-[");
+ if (use_dynamic != eNoDynamicValues)
+ {
+ ValueObjectSP dynamic_value_sp(child_valobj_sp->GetDynamicValue(use_dynamic));
+ if (dynamic_value_sp)
+ child_valobj_sp = dynamic_value_sp;
+ }
+ // Break out early from the switch since we were
+ // able to find the child member
+ break;
+
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("invalid square bracket encountered after \"%s\" in \"%s\"",
+ var_expr_path_strm.GetString().c_str(),
+ var_path.c_str());
+ }
+ return ValueObjectSP();
+
+ default:
+ // Failure...
+ {
+ valobj_sp->GetExpressionPath (var_expr_path_strm, false);
+ error.SetErrorStringWithFormat ("unexpected char '%c' encountered after \"%s\" in \"%s\"",
+ separator_type,
+ var_expr_path_strm.GetString().c_str(),
+ var_path.c_str());
+
+ return ValueObjectSP();
+ }
+ }
+
+ if (child_valobj_sp)
+ valobj_sp = child_valobj_sp;
+
+ if (var_path.empty())
+ break;
+
+ }
+ if (valobj_sp)
+ {
+ if (deref)
+ {
+ ValueObjectSP deref_valobj_sp (valobj_sp->Dereference(error));
+ valobj_sp = deref_valobj_sp;
+ }
+ else if (address_of)
+ {
+ ValueObjectSP address_of_valobj_sp (valobj_sp->AddressOf(error));
+ valobj_sp = address_of_valobj_sp;
+ }
+ }
+ return valobj_sp;
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("no variable named '%s' found in this frame",
+ name_const_string.GetCString());
+ }
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("invalid variable path '%s'", var_expr_cstr);
+ }
+ return ValueObjectSP();
+}
+
+bool
+StackFrame::GetFrameBaseValue (Scalar &frame_base, Error *error_ptr)
+{
+ if (m_flags.IsClear(GOT_FRAME_BASE))
+ {
+ if (m_sc.function)
+ {
+ m_frame_base.Clear();
+ m_frame_base_error.Clear();
+
+ m_flags.Set(GOT_FRAME_BASE);
+ ExecutionContext exe_ctx (shared_from_this());
+ Value expr_value;
+ addr_t loclist_base_addr = LLDB_INVALID_ADDRESS;
+ if (m_sc.function->GetFrameBaseExpression().IsLocationList())
+ loclist_base_addr = m_sc.function->GetAddressRange().GetBaseAddress().GetLoadAddress (exe_ctx.GetTargetPtr());
+
+ if (m_sc.function->GetFrameBaseExpression().Evaluate(&exe_ctx, NULL, NULL, NULL, loclist_base_addr, NULL, expr_value, &m_frame_base_error) == false)
+ {
+ // We should really have an error if evaluate returns, but in case
+ // we don't, lets set the error to something at least.
+ if (m_frame_base_error.Success())
+ m_frame_base_error.SetErrorString("Evaluation of the frame base expression failed.");
+ }
+ else
+ {
+ m_frame_base = expr_value.ResolveValue(&exe_ctx);
+ }
+ }
+ else
+ {
+ m_frame_base_error.SetErrorString ("No function in symbol context.");
+ }
+ }
+
+ if (m_frame_base_error.Success())
+ frame_base = m_frame_base;
+
+ if (error_ptr)
+ *error_ptr = m_frame_base_error;
+ return m_frame_base_error.Success();
+}
+
+RegisterContextSP
+StackFrame::GetRegisterContext ()
+{
+ if (!m_reg_context_sp)
+ {
+ ThreadSP thread_sp (GetThread());
+ if (thread_sp)
+ m_reg_context_sp = thread_sp->CreateRegisterContextForFrame (this);
+ }
+ return m_reg_context_sp;
+}
+
+bool
+StackFrame::HasDebugInformation ()
+{
+ GetSymbolContext (eSymbolContextLineEntry);
+ return m_sc.line_entry.IsValid();
+}
+
+
+ValueObjectSP
+StackFrame::GetValueObjectForFrameVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic)
+{
+ ValueObjectSP valobj_sp;
+ VariableList *var_list = GetVariableList (true);
+ if (var_list)
+ {
+ // Make sure the variable is a frame variable
+ const uint32_t var_idx = var_list->FindIndexForVariable (variable_sp.get());
+ const uint32_t num_variables = var_list->GetSize();
+ if (var_idx < num_variables)
+ {
+ valobj_sp = m_variable_list_value_objects.GetValueObjectAtIndex (var_idx);
+ if (valobj_sp.get() == NULL)
+ {
+ if (m_variable_list_value_objects.GetSize() < num_variables)
+ m_variable_list_value_objects.Resize(num_variables);
+ valobj_sp = ValueObjectVariable::Create (this, variable_sp);
+ m_variable_list_value_objects.SetValueObjectAtIndex (var_idx, valobj_sp);
+ }
+ }
+ }
+ if (use_dynamic != eNoDynamicValues && valobj_sp)
+ {
+ ValueObjectSP dynamic_sp = valobj_sp->GetDynamicValue (use_dynamic);
+ if (dynamic_sp)
+ return dynamic_sp;
+ }
+ return valobj_sp;
+}
+
+ValueObjectSP
+StackFrame::TrackGlobalVariable (const VariableSP &variable_sp, DynamicValueType use_dynamic)
+{
+ // Check to make sure we aren't already tracking this variable?
+ ValueObjectSP valobj_sp (GetValueObjectForFrameVariable (variable_sp, use_dynamic));
+ if (!valobj_sp)
+ {
+ // We aren't already tracking this global
+ VariableList *var_list = GetVariableList (true);
+ // If this frame has no variables, create a new list
+ if (var_list == NULL)
+ m_variable_list_sp.reset (new VariableList());
+
+ // Add the global/static variable to this frame
+ m_variable_list_sp->AddVariable (variable_sp);
+
+ // Now make a value object for it so we can track its changes
+ valobj_sp = GetValueObjectForFrameVariable (variable_sp, use_dynamic);
+ }
+ return valobj_sp;
+}
+
+bool
+StackFrame::IsInlined ()
+{
+ if (m_sc.block == NULL)
+ GetSymbolContext (eSymbolContextBlock);
+ if (m_sc.block)
+ return m_sc.block->GetContainingInlinedBlock() != NULL;
+ return false;
+}
+
+TargetSP
+StackFrame::CalculateTarget ()
+{
+ TargetSP target_sp;
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp)
+ {
+ ProcessSP process_sp (thread_sp->CalculateProcess());
+ if (process_sp)
+ target_sp = process_sp->CalculateTarget();
+ }
+ return target_sp;
+}
+
+ProcessSP
+StackFrame::CalculateProcess ()
+{
+ ProcessSP process_sp;
+ ThreadSP thread_sp(GetThread());
+ if (thread_sp)
+ process_sp = thread_sp->CalculateProcess();
+ return process_sp;
+}
+
+ThreadSP
+StackFrame::CalculateThread ()
+{
+ return GetThread();
+}
+
+StackFrameSP
+StackFrame::CalculateStackFrame ()
+{
+ return shared_from_this();
+}
+
+
+void
+StackFrame::CalculateExecutionContext (ExecutionContext &exe_ctx)
+{
+ exe_ctx.SetContext (shared_from_this());
+}
+
+void
+StackFrame::DumpUsingSettingsFormat (Stream *strm)
+{
+ if (strm == NULL)
+ return;
+
+ GetSymbolContext(eSymbolContextEverything);
+ ExecutionContext exe_ctx (shared_from_this());
+ StreamString s;
+ const char *frame_format = NULL;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target)
+ frame_format = target->GetDebugger().GetFrameFormat();
+ if (frame_format && Debugger::FormatPrompt (frame_format, &m_sc, &exe_ctx, NULL, s))
+ {
+ strm->Write(s.GetData(), s.GetSize());
+ }
+ else
+ {
+ Dump (strm, true, false);
+ strm->EOL();
+ }
+}
+
+void
+StackFrame::Dump (Stream *strm, bool show_frame_index, bool show_fullpaths)
+{
+ if (strm == NULL)
+ return;
+
+ if (show_frame_index)
+ strm->Printf("frame #%u: ", m_frame_index);
+ ExecutionContext exe_ctx (shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ strm->Printf("0x%0*" PRIx64 " ",
+ target ? (target->GetArchitecture().GetAddressByteSize() * 2) : 16,
+ GetFrameCodeAddress().GetLoadAddress(target));
+ GetSymbolContext(eSymbolContextEverything);
+ const bool show_module = true;
+ const bool show_inline = true;
+ m_sc.DumpStopContext (strm,
+ exe_ctx.GetBestExecutionContextScope(),
+ GetFrameCodeAddress(),
+ show_fullpaths,
+ show_module,
+ show_inline);
+}
+
+void
+StackFrame::UpdateCurrentFrameFromPreviousFrame (StackFrame &prev_frame)
+{
+ assert (GetStackID() == prev_frame.GetStackID()); // TODO: remove this after some testing
+ m_variable_list_sp = prev_frame.m_variable_list_sp;
+ m_variable_list_value_objects.Swap (prev_frame.m_variable_list_value_objects);
+ if (!m_disassembly.GetString().empty())
+ m_disassembly.GetString().swap (m_disassembly.GetString());
+}
+
+
+void
+StackFrame::UpdatePreviousFrameFromCurrentFrame (StackFrame &curr_frame)
+{
+ assert (GetStackID() == curr_frame.GetStackID()); // TODO: remove this after some testing
+ m_id.SetPC (curr_frame.m_id.GetPC()); // Update the Stack ID PC value
+ assert (GetThread() == curr_frame.GetThread());
+ m_frame_index = curr_frame.m_frame_index;
+ m_concrete_frame_index = curr_frame.m_concrete_frame_index;
+ m_reg_context_sp = curr_frame.m_reg_context_sp;
+ m_frame_code_addr = curr_frame.m_frame_code_addr;
+ assert (m_sc.target_sp.get() == NULL || curr_frame.m_sc.target_sp.get() == NULL || m_sc.target_sp.get() == curr_frame.m_sc.target_sp.get());
+ assert (m_sc.module_sp.get() == NULL || curr_frame.m_sc.module_sp.get() == NULL || m_sc.module_sp.get() == curr_frame.m_sc.module_sp.get());
+ assert (m_sc.comp_unit == NULL || curr_frame.m_sc.comp_unit == NULL || m_sc.comp_unit == curr_frame.m_sc.comp_unit);
+ assert (m_sc.function == NULL || curr_frame.m_sc.function == NULL || m_sc.function == curr_frame.m_sc.function);
+ m_sc = curr_frame.m_sc;
+ m_flags.Clear(GOT_FRAME_BASE | eSymbolContextEverything);
+ m_flags.Set (m_sc.GetResolvedMask());
+ m_frame_base.Clear();
+ m_frame_base_error.Clear();
+}
+
+
+bool
+StackFrame::HasCachedData () const
+{
+ if (m_variable_list_sp.get())
+ return true;
+ if (m_variable_list_value_objects.GetSize() > 0)
+ return true;
+ if (!m_disassembly.GetString().empty())
+ return true;
+ return false;
+}
+
+bool
+StackFrame::GetStatus (Stream& strm,
+ bool show_frame_info,
+ bool show_source)
+{
+
+ if (show_frame_info)
+ {
+ strm.Indent();
+ DumpUsingSettingsFormat (&strm);
+ }
+
+ if (show_source)
+ {
+ ExecutionContext exe_ctx (shared_from_this());
+ bool have_source = false;
+ Debugger::StopDisassemblyType disasm_display = Debugger::eStopDisassemblyTypeNever;
+ Target *target = exe_ctx.GetTargetPtr();
+ if (target)
+ {
+ Debugger &debugger = target->GetDebugger();
+ const uint32_t source_lines_before = debugger.GetStopSourceLineCount(true);
+ const uint32_t source_lines_after = debugger.GetStopSourceLineCount(false);
+ disasm_display = debugger.GetStopDisassemblyDisplay ();
+
+ if (source_lines_before > 0 || source_lines_after > 0)
+ {
+ GetSymbolContext(eSymbolContextCompUnit | eSymbolContextLineEntry);
+
+ if (m_sc.comp_unit && m_sc.line_entry.IsValid())
+ {
+ have_source = true;
+ target->GetSourceManager().DisplaySourceLinesWithLineNumbers (m_sc.line_entry.file,
+ m_sc.line_entry.line,
+ source_lines_before,
+ source_lines_after,
+ "->",
+ &strm);
+ }
+ }
+ switch (disasm_display)
+ {
+ case Debugger::eStopDisassemblyTypeNever:
+ break;
+
+ case Debugger::eStopDisassemblyTypeNoSource:
+ if (have_source)
+ break;
+ // Fall through to next case
+ case Debugger::eStopDisassemblyTypeAlways:
+ if (target)
+ {
+ const uint32_t disasm_lines = debugger.GetDisassemblyLineCount();
+ if (disasm_lines > 0)
+ {
+ const ArchSpec &target_arch = target->GetArchitecture();
+ AddressRange pc_range;
+ pc_range.GetBaseAddress() = GetFrameCodeAddress();
+ pc_range.SetByteSize(disasm_lines * target_arch.GetMaximumOpcodeByteSize());
+ const char *plugin_name = NULL;
+ const char *flavor = NULL;
+ Disassembler::Disassemble (target->GetDebugger(),
+ target_arch,
+ plugin_name,
+ flavor,
+ exe_ctx,
+ pc_range,
+ disasm_lines,
+ 0,
+ Disassembler::eOptionMarkPCAddress,
+ strm);
+ }
+ }
+ break;
+ }
+ }
+ }
+ return true;
+}
+
diff --git a/source/Target/StackFrameList.cpp b/source/Target/StackFrameList.cpp
new file mode 100644
index 000000000000..69309dfae7b6
--- /dev/null
+++ b/source/Target/StackFrameList.cpp
@@ -0,0 +1,899 @@
+//===-- StackFrameList.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/Target/StackFrameList.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/StreamFile.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Unwind.h"
+
+//#define DEBUG_STACK_FRAMES 1
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// StackFrameList constructor
+//----------------------------------------------------------------------
+StackFrameList::StackFrameList
+(
+ Thread &thread,
+ const lldb::StackFrameListSP &prev_frames_sp,
+ bool show_inline_frames
+) :
+ m_thread (thread),
+ m_prev_frames_sp (prev_frames_sp),
+ m_mutex (Mutex::eMutexTypeRecursive),
+ m_frames (),
+ m_selected_frame_idx (0),
+ m_concrete_frames_fetched (0),
+ m_current_inlined_depth (UINT32_MAX),
+ m_current_inlined_pc (LLDB_INVALID_ADDRESS),
+ m_show_inlined_frames (show_inline_frames)
+{
+ if (prev_frames_sp)
+ {
+ m_current_inlined_depth = prev_frames_sp->m_current_inlined_depth;
+ m_current_inlined_pc = prev_frames_sp->m_current_inlined_pc;
+ }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+StackFrameList::~StackFrameList()
+{
+ // Call clear since this takes a lock and clears the stack frame list
+ // in case another thread is currently using this stack frame list
+ Clear();
+}
+
+void
+StackFrameList::CalculateCurrentInlinedDepth()
+{
+ uint32_t cur_inlined_depth = GetCurrentInlinedDepth();
+ if (cur_inlined_depth == UINT32_MAX)
+ {
+ ResetCurrentInlinedDepth();
+ }
+}
+
+uint32_t
+StackFrameList::GetCurrentInlinedDepth ()
+{
+ if (m_show_inlined_frames && m_current_inlined_pc != LLDB_INVALID_ADDRESS)
+ {
+ lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
+ if (cur_pc != m_current_inlined_pc)
+ {
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ m_current_inlined_depth = UINT32_MAX;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("GetCurrentInlinedDepth: invalidating current inlined depth.\n");
+ }
+ return m_current_inlined_depth;
+ }
+ else
+ {
+ return UINT32_MAX;
+ }
+}
+
+void
+StackFrameList::ResetCurrentInlinedDepth ()
+{
+ if (m_show_inlined_frames)
+ {
+ GetFramesUpTo(0);
+ if (!m_frames[0]->IsInlined())
+ {
+ m_current_inlined_depth = UINT32_MAX;
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("ResetCurrentInlinedDepth: Invalidating current inlined depth.\n");
+ }
+ else
+ {
+ // We only need to do something special about inlined blocks when we
+ // are at the beginning of an inlined function:
+ // FIXME: We probably also have to do something special if the PC is at the END
+ // of an inlined function, which coincides with the end of either its containing
+ // function or another inlined function.
+
+ lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC();
+ Block *block_ptr = m_frames[0]->GetFrameBlock();
+ if (block_ptr)
+ {
+ Address pc_as_address;
+ pc_as_address.SetLoadAddress(curr_pc, &(m_thread.GetProcess()->GetTarget()));
+ AddressRange containing_range;
+ if (block_ptr->GetRangeContainingAddress(pc_as_address, containing_range))
+ {
+ if (pc_as_address == containing_range.GetBaseAddress())
+ {
+ // If we got here because of a breakpoint hit, then set the inlined depth depending on where
+ // the breakpoint was set.
+ // If we got here because of a crash, then set the inlined depth to the deepest most block.
+ // Otherwise, we stopped here naturally as the result of a step, so set ourselves in the
+ // containing frame of the whole set of nested inlines, so the user can then "virtually"
+ // step into the frames one by one, or next over the whole mess.
+ // Note: We don't have to handle being somewhere in the middle of the stack here, since
+ // ResetCurrentInlinedDepth doesn't get called if there is a valid inlined depth set.
+ StopInfoSP stop_info_sp = m_thread.GetStopInfo();
+ if (stop_info_sp)
+ {
+ switch (stop_info_sp->GetStopReason())
+ {
+ case eStopReasonWatchpoint:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonSignal:
+ // In all these cases we want to stop in the deepest most frame.
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = 0;
+ break;
+ case eStopReasonBreakpoint:
+ {
+ // FIXME: Figure out what this break point is doing, and set the inline depth
+ // appropriately. Be careful to take into account breakpoints that implement
+ // step over prologue, since that should do the default calculation.
+ // For now, if the breakpoints corresponding to this hit are all internal,
+ // I set the stop location to the top of the inlined stack, since that will make
+ // things like stepping over prologues work right. But if there are any non-internal
+ // breakpoints I do to the bottom of the stack, since that was the old behavior.
+ uint32_t bp_site_id = stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp(m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id));
+ bool all_internal = true;
+ if (bp_site_sp)
+ {
+ uint32_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (uint32_t i = 0; i < num_owners; i++)
+ {
+ Breakpoint &bp_ref = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (!bp_ref.IsInternal())
+ {
+ all_internal = false;
+ }
+ }
+ }
+ if (!all_internal)
+ {
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = 0;
+ break;
+ }
+ }
+ default:
+ {
+ // Otherwise, we should set ourselves at the container of the inlining, so that the
+ // user can descend into them.
+ // So first we check whether we have more than one inlined block sharing this PC:
+ int num_inlined_functions = 0;
+
+ for (Block *container_ptr = block_ptr->GetInlinedParent();
+ container_ptr != NULL;
+ container_ptr = container_ptr->GetInlinedParent())
+ {
+ if (!container_ptr->GetRangeContainingAddress(pc_as_address, containing_range))
+ break;
+ if (pc_as_address != containing_range.GetBaseAddress())
+ break;
+
+ num_inlined_functions++;
+ }
+ m_current_inlined_pc = curr_pc;
+ m_current_inlined_depth = num_inlined_functions + 1;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("ResetCurrentInlinedDepth: setting inlined depth: %d 0x%" PRIx64 ".\n", m_current_inlined_depth, curr_pc);
+
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+bool
+StackFrameList::DecrementCurrentInlinedDepth ()
+{
+ if (m_show_inlined_frames)
+ {
+ uint32_t current_inlined_depth = GetCurrentInlinedDepth();
+ if (current_inlined_depth != UINT32_MAX)
+ {
+ if (current_inlined_depth > 0)
+ {
+ m_current_inlined_depth--;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+StackFrameList::SetCurrentInlinedDepth (uint32_t new_depth)
+{
+ m_current_inlined_depth = new_depth;
+ if (new_depth == UINT32_MAX)
+ m_current_inlined_pc = LLDB_INVALID_ADDRESS;
+ else
+ m_current_inlined_pc = m_thread.GetRegisterContext()->GetPC();
+}
+
+void
+StackFrameList::GetFramesUpTo(uint32_t end_idx)
+{
+ // this makes sure we do not fetch frames for an invalid thread
+ if (m_thread.IsValid() == false)
+ return;
+
+ // We've already gotten more frames than asked for, or we've already finished unwinding, return.
+ if (m_frames.size() > end_idx || GetAllFramesFetched())
+ return;
+
+ Unwind *unwinder = m_thread.GetUnwinder ();
+
+ if (m_show_inlined_frames)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ StreamFile s(stdout, false);
+#endif
+ // If we are hiding some frames from the outside world, we need to add those onto the total count of
+ // frames to fetch. However, we don't need ot do that if end_idx is 0 since in that case we always
+ // get the first concrete frame and all the inlined frames below it... And of course, if end_idx is
+ // UINT32_MAX that means get all, so just do that...
+
+ uint32_t inlined_depth = 0;
+ if (end_idx > 0 && end_idx != UINT32_MAX)
+ {
+ inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ {
+ if (end_idx > 0)
+ end_idx += inlined_depth;
+ }
+ }
+
+ StackFrameSP unwind_frame_sp;
+ do
+ {
+ uint32_t idx = m_concrete_frames_fetched++;
+ lldb::addr_t pc;
+ lldb::addr_t cfa;
+ if (idx == 0)
+ {
+ // We might have already created frame zero, only create it
+ // if we need to
+ if (m_frames.empty())
+ {
+ RegisterContextSP reg_ctx_sp (m_thread.GetRegisterContext());
+
+ if (reg_ctx_sp)
+ {
+
+ const bool success = unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
+ // There shouldn't be any way not to get the frame info for frame 0.
+ // But if the unwinder can't make one, lets make one by hand with the
+ // SP as the CFA and see if that gets any further.
+ if (!success)
+ {
+ cfa = reg_ctx_sp->GetSP();
+ pc = reg_ctx_sp->GetPC();
+ }
+
+ unwind_frame_sp.reset (new StackFrame (m_thread.shared_from_this(),
+ m_frames.size(),
+ idx,
+ reg_ctx_sp,
+ cfa,
+ pc,
+ NULL));
+ m_frames.push_back (unwind_frame_sp);
+ }
+ }
+ else
+ {
+ unwind_frame_sp = m_frames.front();
+ cfa = unwind_frame_sp->m_id.GetCallFrameAddress();
+ }
+ }
+ else
+ {
+ const bool success = unwinder->GetFrameInfoAtIndex(idx, cfa, pc);
+ if (!success)
+ {
+ // We've gotten to the end of the stack.
+ SetAllFramesFetched();
+ break;
+ }
+ unwind_frame_sp.reset (new StackFrame (m_thread.shared_from_this(), m_frames.size(), idx, cfa, pc, NULL));
+ m_frames.push_back (unwind_frame_sp);
+ }
+
+ SymbolContext unwind_sc = unwind_frame_sp->GetSymbolContext (eSymbolContextBlock | eSymbolContextFunction);
+ Block *unwind_block = unwind_sc.block;
+ if (unwind_block)
+ {
+ Address curr_frame_address (unwind_frame_sp->GetFrameCodeAddress());
+ // Be sure to adjust the frame address to match the address
+ // that was used to lookup the symbol context above. If we are
+ // in the first concrete frame, then we lookup using the current
+ // address, else we decrement the address by one to get the correct
+ // location.
+ if (idx > 0)
+ curr_frame_address.Slide(-1);
+
+ SymbolContext next_frame_sc;
+ Address next_frame_address;
+
+ while (unwind_sc.GetParentOfInlinedScope(curr_frame_address, next_frame_sc, next_frame_address))
+ {
+ StackFrameSP frame_sp(new StackFrame (m_thread.shared_from_this(),
+ m_frames.size(),
+ idx,
+ unwind_frame_sp->GetRegisterContextSP (),
+ cfa,
+ next_frame_address,
+ &next_frame_sc));
+
+ m_frames.push_back (frame_sp);
+ unwind_sc = next_frame_sc;
+ curr_frame_address = next_frame_address;
+ }
+ }
+ } while (m_frames.size() - 1 < end_idx);
+
+ // Don't try to merge till you've calculated all the frames in this stack.
+ if (GetAllFramesFetched() && m_prev_frames_sp)
+ {
+ StackFrameList *prev_frames = m_prev_frames_sp.get();
+ StackFrameList *curr_frames = this;
+
+ //curr_frames->m_current_inlined_depth = prev_frames->m_current_inlined_depth;
+ //curr_frames->m_current_inlined_pc = prev_frames->m_current_inlined_pc;
+ //printf ("GetFramesUpTo: Copying current inlined depth: %d 0x%" PRIx64 ".\n", curr_frames->m_current_inlined_depth, curr_frames->m_current_inlined_pc);
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("\nprev_frames:\n");
+ prev_frames->Dump (&s);
+ s.PutCString("\ncurr_frames:\n");
+ curr_frames->Dump (&s);
+ s.EOL();
+#endif
+ size_t curr_frame_num, prev_frame_num;
+
+ for (curr_frame_num = curr_frames->m_frames.size(), prev_frame_num = prev_frames->m_frames.size();
+ curr_frame_num > 0 && prev_frame_num > 0;
+ --curr_frame_num, --prev_frame_num)
+ {
+ const size_t curr_frame_idx = curr_frame_num-1;
+ const size_t prev_frame_idx = prev_frame_num-1;
+ StackFrameSP curr_frame_sp (curr_frames->m_frames[curr_frame_idx]);
+ StackFrameSP prev_frame_sp (prev_frames->m_frames[prev_frame_idx]);
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.Printf("\n\nCurr frame #%u ", curr_frame_idx);
+ if (curr_frame_sp)
+ curr_frame_sp->Dump (&s, true, false);
+ else
+ s.PutCString("NULL");
+ s.Printf("\nPrev frame #%u ", prev_frame_idx);
+ if (prev_frame_sp)
+ prev_frame_sp->Dump (&s, true, false);
+ else
+ s.PutCString("NULL");
+#endif
+
+ StackFrame *curr_frame = curr_frame_sp.get();
+ StackFrame *prev_frame = prev_frame_sp.get();
+
+ if (curr_frame == NULL || prev_frame == NULL)
+ break;
+
+ // Check the stack ID to make sure they are equal
+ if (curr_frame->GetStackID() != prev_frame->GetStackID())
+ break;
+
+ prev_frame->UpdatePreviousFrameFromCurrentFrame (*curr_frame);
+ // Now copy the fixed up previous frame into the current frames
+ // so the pointer doesn't change
+ m_frames[curr_frame_idx] = prev_frame_sp;
+ //curr_frame->UpdateCurrentFrameFromPreviousFrame (*prev_frame);
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.Printf("\n Copying previous frame to current frame");
+#endif
+ }
+ // We are done with the old stack frame list, we can release it now
+ m_prev_frames_sp.reset();
+ }
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("\n\nNew frames:\n");
+ Dump (&s);
+ s.EOL();
+#endif
+ }
+ else
+ {
+ if (end_idx < m_concrete_frames_fetched)
+ return;
+
+ uint32_t num_frames = unwinder->GetFramesUpTo(end_idx);
+ if (num_frames <= end_idx + 1)
+ {
+ //Done unwinding.
+ m_concrete_frames_fetched = UINT32_MAX;
+ }
+ m_frames.resize(num_frames);
+ }
+}
+
+uint32_t
+StackFrameList::GetNumFrames (bool can_create)
+{
+ Mutex::Locker locker (m_mutex);
+
+ if (can_create)
+ GetFramesUpTo (UINT32_MAX);
+
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth == UINT32_MAX)
+ return m_frames.size();
+ else
+ return m_frames.size() - inlined_depth;
+}
+
+void
+StackFrameList::Dump (Stream *s)
+{
+ if (s == NULL)
+ return;
+ Mutex::Locker locker (m_mutex);
+
+ const_iterator pos, begin = m_frames.begin(), end = m_frames.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ StackFrame *frame = (*pos).get();
+ s->Printf("%p: ", frame);
+ if (frame)
+ {
+ frame->GetStackID().Dump (s);
+ frame->DumpUsingSettingsFormat (s);
+ }
+ else
+ s->Printf("frame #%u", (uint32_t)std::distance (begin, pos));
+ s->EOL();
+ }
+ s->EOL();
+}
+
+StackFrameSP
+StackFrameList::GetFrameAtIndex (uint32_t idx)
+{
+ StackFrameSP frame_sp;
+ Mutex::Locker locker (m_mutex);
+ uint32_t original_idx = idx;
+
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ idx += inlined_depth;
+
+ if (idx < m_frames.size())
+ frame_sp = m_frames[idx];
+
+ if (frame_sp)
+ return frame_sp;
+
+ // GetFramesUpTo will fill m_frames with as many frames as you asked for,
+ // if there are that many. If there weren't then you asked for too many
+ // frames.
+ GetFramesUpTo (idx);
+ if (idx < m_frames.size())
+ {
+ if (m_show_inlined_frames)
+ {
+ // When inline frames are enabled we actually create all the frames in GetFramesUpTo.
+ frame_sp = m_frames[idx];
+ }
+ else
+ {
+ Unwind *unwinder = m_thread.GetUnwinder ();
+ if (unwinder)
+ {
+ addr_t pc, cfa;
+ if (unwinder->GetFrameInfoAtIndex(idx, cfa, pc))
+ {
+ frame_sp.reset (new StackFrame (m_thread.shared_from_this(), idx, idx, cfa, pc, NULL));
+
+ Function *function = frame_sp->GetSymbolContext (eSymbolContextFunction).function;
+ if (function)
+ {
+ // When we aren't showing inline functions we always use
+ // the top most function block as the scope.
+ frame_sp->SetSymbolContextScope (&function->GetBlock(false));
+ }
+ else
+ {
+ // Set the symbol scope from the symbol regardless if it is NULL or valid.
+ frame_sp->SetSymbolContextScope (frame_sp->GetSymbolContext (eSymbolContextSymbol).symbol);
+ }
+ SetFrameAtIndex(idx, frame_sp);
+ }
+ }
+ }
+ }
+ else if (original_idx == 0)
+ {
+ // There should ALWAYS be a frame at index 0. If something went wrong with the CurrentInlinedDepth such that
+ // there weren't as many frames as we thought taking that into account, then reset the current inlined depth
+ // and return the real zeroth frame.
+ if (m_frames.size() > 0)
+ {
+ ResetCurrentInlinedDepth();
+ frame_sp = m_frames[original_idx];
+ }
+ else
+ {
+ // Why do we have a thread with zero frames, that should not ever happen...
+ if (m_thread.IsValid())
+ assert ("A valid thread has no frames.");
+
+ }
+ }
+
+ return frame_sp;
+}
+
+StackFrameSP
+StackFrameList::GetFrameWithConcreteFrameIndex (uint32_t unwind_idx)
+{
+ // First try assuming the unwind index is the same as the frame index. The
+ // unwind index is always greater than or equal to the frame index, so it
+ // is a good place to start. If we have inlined frames we might have 5
+ // concrete frames (frame unwind indexes go from 0-4), but we might have 15
+ // frames after we make all the inlined frames. Most of the time the unwind
+ // frame index (or the concrete frame index) is the same as the frame index.
+ uint32_t frame_idx = unwind_idx;
+ StackFrameSP frame_sp (GetFrameAtIndex (frame_idx));
+ while (frame_sp)
+ {
+ if (frame_sp->GetFrameIndex() == unwind_idx)
+ break;
+ frame_sp = GetFrameAtIndex (++frame_idx);
+ }
+ return frame_sp;
+}
+
+static bool
+CompareStackID (const StackFrameSP &stack_sp, const StackID &stack_id)
+{
+ return stack_sp->GetStackID() < stack_id;
+}
+
+StackFrameSP
+StackFrameList::GetFrameWithStackID (const StackID &stack_id)
+{
+ StackFrameSP frame_sp;
+
+ if (stack_id.IsValid())
+ {
+ Mutex::Locker locker (m_mutex);
+ uint32_t frame_idx = 0;
+ // Do a binary search in case the stack frame is already in our cache
+ collection::const_iterator begin = m_frames.begin();
+ collection::const_iterator end = m_frames.end();
+ if (begin != end)
+ {
+ collection::const_iterator pos = std::lower_bound (begin, end, stack_id, CompareStackID);
+ if (pos != end && (*pos)->GetStackID() == stack_id)
+ return *pos;
+
+ if (m_frames.back()->GetStackID() < stack_id)
+ frame_idx = m_frames.size();
+ }
+ do
+ {
+ frame_sp = GetFrameAtIndex (frame_idx);
+ if (frame_sp && frame_sp->GetStackID() == stack_id)
+ break;
+ frame_idx++;
+ }
+ while (frame_sp);
+ }
+ return frame_sp;
+}
+
+bool
+StackFrameList::SetFrameAtIndex (uint32_t idx, StackFrameSP &frame_sp)
+{
+ if (idx >= m_frames.size())
+ m_frames.resize(idx + 1);
+ // Make sure allocation succeeded by checking bounds again
+ if (idx < m_frames.size())
+ {
+ m_frames[idx] = frame_sp;
+ return true;
+ }
+ return false; // resize failed, out of memory?
+}
+
+uint32_t
+StackFrameList::GetSelectedFrameIndex () const
+{
+ Mutex::Locker locker (m_mutex);
+ return m_selected_frame_idx;
+}
+
+
+uint32_t
+StackFrameList::SetSelectedFrame (lldb_private::StackFrame *frame)
+{
+ Mutex::Locker locker (m_mutex);
+ const_iterator pos;
+ const_iterator begin = m_frames.begin();
+ const_iterator end = m_frames.end();
+ m_selected_frame_idx = 0;
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->get() == frame)
+ {
+ m_selected_frame_idx = std::distance (begin, pos);
+ uint32_t inlined_depth = GetCurrentInlinedDepth();
+ if (inlined_depth != UINT32_MAX)
+ m_selected_frame_idx -= inlined_depth;
+ break;
+ }
+ }
+ SetDefaultFileAndLineToSelectedFrame();
+ return m_selected_frame_idx;
+}
+
+// Mark a stack frame as the current frame using the frame index
+bool
+StackFrameList::SetSelectedFrameByIndex (uint32_t idx)
+{
+ Mutex::Locker locker (m_mutex);
+ StackFrameSP frame_sp (GetFrameAtIndex (idx));
+ if (frame_sp)
+ {
+ SetSelectedFrame(frame_sp.get());
+ return true;
+ }
+ else
+ return false;
+}
+
+void
+StackFrameList::SetDefaultFileAndLineToSelectedFrame()
+{
+ if (m_thread.GetID() == m_thread.GetProcess()->GetThreadList().GetSelectedThread()->GetID())
+ {
+ StackFrameSP frame_sp (GetFrameAtIndex (GetSelectedFrameIndex()));
+ if (frame_sp)
+ {
+ SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextLineEntry);
+ if (sc.line_entry.file)
+ m_thread.CalculateTarget()->GetSourceManager().SetDefaultFileAndLine (sc.line_entry.file,
+ sc.line_entry.line);
+ }
+ }
+}
+
+// The thread has been run, reset the number stack frames to zero so we can
+// determine how many frames we have lazily.
+void
+StackFrameList::Clear ()
+{
+ Mutex::Locker locker (m_mutex);
+ m_frames.clear();
+ m_concrete_frames_fetched = 0;
+}
+
+void
+StackFrameList::InvalidateFrames (uint32_t start_idx)
+{
+ Mutex::Locker locker (m_mutex);
+ if (m_show_inlined_frames)
+ {
+ Clear();
+ }
+ else
+ {
+ const size_t num_frames = m_frames.size();
+ while (start_idx < num_frames)
+ {
+ m_frames[start_idx].reset();
+ ++start_idx;
+ }
+ }
+}
+
+void
+StackFrameList::Merge (std::unique_ptr<StackFrameList>& curr_ap, lldb::StackFrameListSP& prev_sp)
+{
+ Mutex::Locker curr_locker (curr_ap.get() ? &curr_ap->m_mutex : NULL);
+ Mutex::Locker prev_locker (prev_sp.get() ? &prev_sp->m_mutex : NULL);
+
+#if defined (DEBUG_STACK_FRAMES)
+ StreamFile s(stdout, false);
+ s.PutCString("\n\nStackFrameList::Merge():\nPrev:\n");
+ if (prev_sp.get())
+ prev_sp->Dump (&s);
+ else
+ s.PutCString ("NULL");
+ s.PutCString("\nCurr:\n");
+ if (curr_ap.get())
+ curr_ap->Dump (&s);
+ else
+ s.PutCString ("NULL");
+ s.EOL();
+#endif
+
+ if (curr_ap.get() == NULL || curr_ap->GetNumFrames (false) == 0)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("No current frames, leave previous frames alone...\n");
+#endif
+ curr_ap.release();
+ return;
+ }
+
+ if (prev_sp.get() == NULL || prev_sp->GetNumFrames (false) == 0)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("No previous frames, so use current frames...\n");
+#endif
+ // We either don't have any previous frames, or since we have more than
+ // one current frames it means we have all the frames and can safely
+ // replace our previous frames.
+ prev_sp.reset (curr_ap.release());
+ return;
+ }
+
+ const uint32_t num_curr_frames = curr_ap->GetNumFrames (false);
+
+ if (num_curr_frames > 1)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("We have more than one current frame, so use current frames...\n");
+#endif
+ // We have more than one current frames it means we have all the frames
+ // and can safely replace our previous frames.
+ prev_sp.reset (curr_ap.release());
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("\nMerged:\n");
+ prev_sp->Dump (&s);
+#endif
+ return;
+ }
+
+ StackFrameSP prev_frame_zero_sp(prev_sp->GetFrameAtIndex (0));
+ StackFrameSP curr_frame_zero_sp(curr_ap->GetFrameAtIndex (0));
+ StackID curr_stack_id (curr_frame_zero_sp->GetStackID());
+ StackID prev_stack_id (prev_frame_zero_sp->GetStackID());
+
+#if defined (DEBUG_STACK_FRAMES)
+ const uint32_t num_prev_frames = prev_sp->GetNumFrames (false);
+ s.Printf("\n%u previous frames with one current frame\n", num_prev_frames);
+#endif
+
+ // We have only a single current frame
+ // Our previous stack frames only had a single frame as well...
+ if (curr_stack_id == prev_stack_id)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ s.Printf("\nPrevious frame #0 is same as current frame #0, merge the cached data\n");
+#endif
+
+ curr_frame_zero_sp->UpdateCurrentFrameFromPreviousFrame (*prev_frame_zero_sp);
+// prev_frame_zero_sp->UpdatePreviousFrameFromCurrentFrame (*curr_frame_zero_sp);
+// prev_sp->SetFrameAtIndex (0, prev_frame_zero_sp);
+ }
+ else if (curr_stack_id < prev_stack_id)
+ {
+#if defined (DEBUG_STACK_FRAMES)
+ s.Printf("\nCurrent frame #0 has a stack ID that is less than the previous frame #0, insert current frame zero in front of previous\n");
+#endif
+ prev_sp->m_frames.insert (prev_sp->m_frames.begin(), curr_frame_zero_sp);
+ }
+
+ curr_ap.release();
+
+#if defined (DEBUG_STACK_FRAMES)
+ s.PutCString("\nMerged:\n");
+ prev_sp->Dump (&s);
+#endif
+
+
+}
+
+lldb::StackFrameSP
+StackFrameList::GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr)
+{
+ const_iterator pos;
+ const_iterator begin = m_frames.begin();
+ const_iterator end = m_frames.end();
+ lldb::StackFrameSP ret_sp;
+
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->get() == stack_frame_ptr)
+ {
+ ret_sp = (*pos);
+ break;
+ }
+ }
+ return ret_sp;
+}
+
+size_t
+StackFrameList::GetStatus (Stream& strm,
+ uint32_t first_frame,
+ uint32_t num_frames,
+ bool show_frame_info,
+ uint32_t num_frames_with_source)
+{
+ size_t num_frames_displayed = 0;
+
+ if (num_frames == 0)
+ return 0;
+
+ StackFrameSP frame_sp;
+ uint32_t frame_idx = 0;
+ uint32_t last_frame;
+
+ // Don't let the last frame wrap around...
+ if (num_frames == UINT32_MAX)
+ last_frame = UINT32_MAX;
+ else
+ last_frame = first_frame + num_frames;
+
+ for (frame_idx = first_frame; frame_idx < last_frame; ++frame_idx)
+ {
+ frame_sp = GetFrameAtIndex(frame_idx);
+ if (frame_sp.get() == NULL)
+ break;
+
+ if (!frame_sp->GetStatus (strm,
+ show_frame_info,
+ num_frames_with_source > (first_frame - frame_idx)))
+ break;
+ ++num_frames_displayed;
+ }
+
+ strm.IndentLess();
+ return num_frames_displayed;
+}
+
diff --git a/source/Target/StackID.cpp b/source/Target/StackID.cpp
new file mode 100644
index 000000000000..9e8c315d0704
--- /dev/null
+++ b/source/Target/StackID.cpp
@@ -0,0 +1,110 @@
+//===-- StackID.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/Target/StackID.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/SymbolContext.h"
+
+using namespace lldb_private;
+
+
+void
+StackID::Dump (Stream *s)
+{
+ s->Printf("StackID (pc = 0x%16.16" PRIx64 ", cfa = 0x%16.16" PRIx64 ", symbol_scope = %p", (uint64_t)m_pc, (uint64_t)m_cfa, m_symbol_scope);
+ if (m_symbol_scope)
+ {
+ SymbolContext sc;
+
+ m_symbol_scope->CalculateSymbolContext (&sc);
+ if (sc.block)
+ s->Printf(" (Block {0x%8.8" PRIx64 "})", sc.block->GetID());
+ else if (sc.symbol)
+ s->Printf(" (Symbol{0x%8.8x})", sc.symbol->GetID());
+ }
+ s->PutCString(") ");
+}
+
+bool
+lldb_private::operator== (const StackID& lhs, const StackID& rhs)
+{
+ if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
+ return false;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ // Only compare the PC values if both symbol context scopes are NULL
+ if (lhs_scope == NULL && rhs_scope == NULL)
+ return lhs.GetPC() == rhs.GetPC();
+
+ return lhs_scope == rhs_scope;
+}
+
+bool
+lldb_private::operator!= (const StackID& lhs, const StackID& rhs)
+{
+ if (lhs.GetCallFrameAddress() != rhs.GetCallFrameAddress())
+ return true;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ if (lhs_scope == NULL && rhs_scope == NULL)
+ return lhs.GetPC() != rhs.GetPC();
+
+ return lhs_scope != rhs_scope;
+}
+
+bool
+lldb_private::operator< (const StackID& lhs, const StackID& rhs)
+{
+ const lldb::addr_t lhs_cfa = lhs.GetCallFrameAddress();
+ const lldb::addr_t rhs_cfa = rhs.GetCallFrameAddress();
+
+ // FIXME: We are assuming that the stacks grow downward in memory. That's not necessary, but true on
+ // all the machines we care about at present. If this changes, we'll have to deal with that. The ABI is the
+ // agent who knows this ordering, but the StackID has no access to the ABI. The most straightforward way
+ // to handle this is to add a "m_grows_downward" bool to the StackID, and set it in the constructor.
+ // But I'm not going to waste a bool per StackID on this till we need it.
+
+ if (lhs_cfa != rhs_cfa)
+ return lhs_cfa < rhs_cfa;
+
+ SymbolContextScope *lhs_scope = lhs.GetSymbolContextScope();
+ SymbolContextScope *rhs_scope = rhs.GetSymbolContextScope();
+
+ if (lhs_scope != NULL && rhs_scope != NULL)
+ {
+ // Same exact scope, lhs is not less than (younger than rhs)
+ if (lhs_scope == rhs_scope)
+ return false;
+
+ SymbolContext lhs_sc;
+ SymbolContext rhs_sc;
+ lhs_scope->CalculateSymbolContext (&lhs_sc);
+ rhs_scope->CalculateSymbolContext (&rhs_sc);
+
+ // Items with the same function can only be compared
+ if (lhs_sc.function == rhs_sc.function &&
+ lhs_sc.function != NULL && lhs_sc.block != NULL &&
+ rhs_sc.function != NULL && rhs_sc.block != NULL)
+ {
+ return rhs_sc.block->Contains (lhs_sc.block);
+ }
+ }
+ return false;
+}
diff --git a/source/Target/StopInfo.cpp b/source/Target/StopInfo.cpp
new file mode 100644
index 000000000000..81b9d866f711
--- /dev/null
+++ b/source/Target/StopInfo.cpp
@@ -0,0 +1,1143 @@
+//===-- StopInfo.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/lldb-python.h"
+
+#include "lldb/Target/StopInfo.h"
+
+// C Includes
+// C++ Includes
+#include <string>
+
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Log.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Expression/ClangUserExpression.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/UnixSignals.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+StopInfo::StopInfo (Thread &thread, uint64_t value) :
+ m_thread_wp (thread.shared_from_this()),
+ m_stop_id (thread.GetProcess()->GetStopID()),
+ m_resume_id (thread.GetProcess()->GetResumeID()),
+ m_value (value),
+ m_override_should_notify (eLazyBoolCalculate),
+ m_override_should_stop (eLazyBoolCalculate)
+{
+}
+
+bool
+StopInfo::IsValid () const
+{
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetStopID() == m_stop_id;
+ return false;
+}
+
+void
+StopInfo::MakeStopInfoValid ()
+{
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ m_stop_id = thread_sp->GetProcess()->GetStopID();
+ m_resume_id = thread_sp->GetProcess()->GetResumeID();
+ }
+}
+
+bool
+StopInfo::HasTargetRunSinceMe ()
+{
+ ThreadSP thread_sp (m_thread_wp.lock());
+
+ if (thread_sp)
+ {
+ lldb::StateType ret_type = thread_sp->GetProcess()->GetPrivateState();
+ if (ret_type == eStateRunning)
+ {
+ return true;
+ }
+ else if (ret_type == eStateStopped)
+ {
+ // This is a little tricky. We want to count "run and stopped again before you could
+ // ask this question as a "TRUE" answer to HasTargetRunSinceMe. But we don't want to
+ // include any running of the target done for expressions. So we track both resumes,
+ // and resumes caused by expressions, and check if there are any resumes NOT caused
+ // by expressions.
+
+ uint32_t curr_resume_id = thread_sp->GetProcess()->GetResumeID();
+ uint32_t last_user_expression_id = thread_sp->GetProcess()->GetLastUserExpressionResumeID ();
+ if (curr_resume_id == m_resume_id)
+ {
+ return false;
+ }
+ else if (curr_resume_id > last_user_expression_id)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------
+// StopInfoBreakpoint
+//----------------------------------------------------------------------
+
+namespace lldb_private
+{
+class StopInfoBreakpoint : public StopInfo
+{
+public:
+
+ StopInfoBreakpoint (Thread &thread, break_id_t break_id) :
+ StopInfo (thread, break_id),
+ m_description(),
+ m_should_stop (false),
+ m_should_stop_is_valid (false),
+ m_should_perform_action (true),
+ m_address (LLDB_INVALID_ADDRESS),
+ m_break_id(LLDB_INVALID_BREAK_ID),
+ m_was_one_shot (false)
+ {
+ StoreBPInfo();
+ }
+
+ StopInfoBreakpoint (Thread &thread, break_id_t break_id, bool should_stop) :
+ StopInfo (thread, break_id),
+ m_description(),
+ m_should_stop (should_stop),
+ m_should_stop_is_valid (true),
+ m_should_perform_action (true),
+ m_address (LLDB_INVALID_ADDRESS),
+ m_break_id(LLDB_INVALID_BREAK_ID),
+ m_was_one_shot (false)
+ {
+ StoreBPInfo();
+ }
+
+ void
+ StoreBPInfo ()
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value));
+ if (bp_site_sp)
+ {
+ if (bp_site_sp->GetNumberOfOwners() == 1)
+ {
+ BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(0);
+ if (bp_loc_sp)
+ {
+ m_break_id = bp_loc_sp->GetBreakpoint().GetID();
+ m_was_one_shot = bp_loc_sp->GetBreakpoint().IsOneShot();
+ }
+ }
+ m_address = bp_site_sp->GetLoadAddress();
+ }
+ }
+ }
+
+ virtual ~StopInfoBreakpoint ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonBreakpoint;
+ }
+
+ virtual bool
+ ShouldStopSynchronous (Event *event_ptr)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ if (!m_should_stop_is_valid)
+ {
+ // Only check once if we should stop at a breakpoint
+ BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value));
+ if (bp_site_sp)
+ {
+ ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0));
+ StoppointCallbackContext context (event_ptr, exe_ctx, true);
+ m_should_stop = bp_site_sp->ShouldStop (&context);
+ }
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value);
+
+ m_should_stop = true;
+ }
+ m_should_stop_is_valid = true;
+ }
+ return m_should_stop;
+ }
+ return false;
+ }
+
+ virtual bool
+ DoShouldNotify (Event *event_ptr)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value));
+ if (bp_site_sp)
+ {
+ bool all_internal = true;
+
+ for (uint32_t i = 0; i < bp_site_sp->GetNumberOfOwners(); i++)
+ {
+ if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal())
+ {
+ all_internal = false;
+ break;
+ }
+ }
+ return all_internal == false;
+ }
+ }
+ return true;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value));
+ if (bp_site_sp)
+ {
+ StreamString strm;
+ // If we have just hit an internal breakpoint, and it has a kind description, print that instead of the
+ // full breakpoint printing:
+ if (bp_site_sp->IsInternal())
+ {
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ for (size_t idx = 0; idx < num_owners; idx++)
+ {
+ const char *kind = bp_site_sp->GetOwnerAtIndex(idx)->GetBreakpoint().GetBreakpointKind();
+ if (kind != NULL)
+ {
+ m_description.assign (kind);
+ return kind;
+ }
+ }
+ }
+
+ strm.Printf("breakpoint ");
+ bp_site_sp->GetDescription(&strm, eDescriptionLevelBrief);
+ m_description.swap (strm.GetString());
+ }
+ else
+ {
+ StreamString strm;
+ if (m_break_id != LLDB_INVALID_BREAK_ID)
+ {
+ BreakpointSP break_sp = thread_sp->GetProcess()->GetTarget().GetBreakpointByID(m_break_id);
+ if (break_sp)
+ {
+ if (break_sp->IsInternal())
+ {
+ const char *kind = break_sp->GetBreakpointKind();
+ if (kind)
+ strm.Printf ("internal %s breakpoint(%d).", kind, m_break_id);
+ else
+ strm.Printf ("internal breakpoint(%d).", m_break_id);
+ }
+ else
+ {
+ strm.Printf ("breakpoint %d.", m_break_id);
+ }
+ }
+ else
+ {
+ if (m_was_one_shot)
+ strm.Printf ("one-shot breakpoint %d", m_break_id);
+ else
+ strm.Printf ("breakpoint %d which has been deleted.", m_break_id);
+ }
+ }
+ else if (m_address == LLDB_INVALID_ADDRESS)
+ strm.Printf("breakpoint site %" PRIi64 " which has been deleted - unknown address", m_value);
+ else
+ strm.Printf("breakpoint site %" PRIi64 " which has been deleted - was at 0x%" PRIx64, m_value, m_address);
+
+ m_description.swap (strm.GetString());
+ }
+ }
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ bool
+ ShouldStop (Event *event_ptr)
+ {
+ // This just reports the work done by PerformAction or the synchronous stop. It should
+ // only ever get called after they have had a chance to run.
+ assert (m_should_stop_is_valid);
+ return m_should_stop;
+ }
+
+ virtual void
+ PerformAction (Event *event_ptr)
+ {
+ if (!m_should_perform_action)
+ return;
+ m_should_perform_action = false;
+
+ ThreadSP thread_sp (m_thread_wp.lock());
+
+ if (thread_sp)
+ {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS);
+
+ if (!thread_sp->IsValid())
+ {
+ // This shouldn't ever happen, but just in case, don't do more harm.
+ if (log)
+ {
+ log->Printf ("PerformAction got called with an invalid thread.");
+ }
+ m_should_stop = true;
+ m_should_stop_is_valid = true;
+ return;
+ }
+
+ BreakpointSiteSP bp_site_sp (thread_sp->GetProcess()->GetBreakpointSiteList().FindByID (m_value));
+
+ if (bp_site_sp)
+ {
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+
+ if (num_owners == 0)
+ {
+ m_should_stop = true;
+ }
+ else
+ {
+ // We go through each location, and test first its condition. If the condition says to stop,
+ // then we run the callback for that location. If that callback says to stop as well, then
+ // we set m_should_stop to true; we are going to stop.
+ // But we still want to give all the breakpoints whose conditions say we are going to stop a
+ // chance to run their callbacks.
+ // Of course if any callback restarts the target by putting "continue" in the callback, then
+ // we're going to restart, without running the rest of the callbacks. And in this case we will
+ // end up not stopping even if another location said we should stop. But that's better than not
+ // running all the callbacks.
+
+ m_should_stop = false;
+
+ ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0));
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process->GetModIDRef().IsLastResumeForUserExpression())
+ {
+ // If we are in the middle of evaluating an expression, don't run asynchronous breakpoint commands or
+ // expressions. That could lead to infinite recursion if the command or condition re-calls the function
+ // with this breakpoint.
+ // TODO: We can keep a list of the breakpoints we've seen while running expressions in the nested
+ // PerformAction calls that can arise when the action runs a function that hits another breakpoint,
+ // and only stop running commands when we see the same breakpoint hit a second time.
+
+ m_should_stop_is_valid = true;
+ if (log)
+ log->Printf ("StopInfoBreakpoint::PerformAction - Hit a breakpoint while running an expression,"
+ " not running commands to avoid recursion.");
+ bool ignoring_breakpoints = process->GetIgnoreBreakpointsInExpressions();
+ if (ignoring_breakpoints)
+ {
+ m_should_stop = false;
+ // Internal breakpoints will always stop.
+ for (size_t j = 0; j < num_owners; j++)
+ {
+ lldb::BreakpointLocationSP bp_loc_sp = bp_site_sp->GetOwnerAtIndex(j);
+ if (bp_loc_sp->GetBreakpoint().IsInternal())
+ {
+ m_should_stop = true;
+ break;
+ }
+ }
+ }
+ else
+ {
+ m_should_stop = true;
+ }
+ if (log)
+ log->Printf ("StopInfoBreakpoint::PerformAction - in expression, continuing: %s.",
+ m_should_stop ? "true" : "false");
+ process->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf("Warning: hit breakpoint while "
+ "running function, skipping commands and conditions to prevent recursion.");
+ return;
+ }
+
+ StoppointCallbackContext context (event_ptr, exe_ctx, false);
+
+ // Let's copy the breakpoint locations out of the site and store them in a local list. That way if
+ // one of the breakpoint actions changes the site, then we won't be operating on a bad list.
+
+ BreakpointLocationCollection site_locations;
+ for (size_t j = 0; j < num_owners; j++)
+ site_locations.Add(bp_site_sp->GetOwnerAtIndex(j));
+
+ for (size_t j = 0; j < num_owners; j++)
+ {
+ lldb::BreakpointLocationSP bp_loc_sp = site_locations.GetByIndex(j);
+
+ // If another action disabled this breakpoint or its location, then don't run the actions.
+ if (!bp_loc_sp->IsEnabled() || !bp_loc_sp->GetBreakpoint().IsEnabled())
+ continue;
+
+ // The breakpoint site may have many locations associated with it, not all of them valid for
+ // this thread. Skip the ones that aren't:
+ if (!bp_loc_sp->ValidForThisThread(thread_sp.get()))
+ continue;
+
+ // First run the condition for the breakpoint. If that says we should stop, then we'll run
+ // the callback for the breakpoint. If the callback says we shouldn't stop that will win.
+
+ if (bp_loc_sp->GetConditionText() != NULL)
+ {
+ Error condition_error;
+ bool condition_says_stop = bp_loc_sp->ConditionSaysStop(exe_ctx, condition_error);
+
+ if (!condition_error.Success())
+ {
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP error_sp = debugger.GetAsyncErrorStream ();
+ error_sp->Printf ("Stopped due to an error evaluating condition of breakpoint ");
+ bp_loc_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief);
+ error_sp->Printf (": \"%s\"",
+ bp_loc_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str = condition_error.AsCString("<Unknown Error>");
+ if (log)
+ log->Printf("Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString (err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ // If the condition fails to be parsed or run, we should stop.
+ condition_says_stop = true;
+ }
+ else
+ {
+ if (!condition_says_stop)
+ continue;
+ }
+ }
+
+ bool callback_says_stop;
+
+ // FIXME: For now the callbacks have to run in async mode - the first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest breakpoint hits then this will change.
+
+ Debugger &debugger = thread_sp->CalculateTarget()->GetDebugger();
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution (true);
+
+ callback_says_stop = bp_loc_sp->InvokeCallback (&context);
+
+ debugger.SetAsyncExecution (old_async);
+
+ if (callback_says_stop)
+ m_should_stop = true;
+
+ // If we are going to stop for this breakpoint, then remove the breakpoint.
+ if (callback_says_stop && bp_loc_sp && bp_loc_sp->GetBreakpoint().IsOneShot())
+ {
+ thread_sp->GetProcess()->GetTarget().RemoveBreakpointByID (bp_loc_sp->GetBreakpoint().GetID());
+ }
+
+ // Also make sure that the callback hasn't continued the target.
+ // If it did, when we'll set m_should_start to false and get out of here.
+ if (HasTargetRunSinceMe ())
+ {
+ m_should_stop = false;
+ break;
+ }
+ }
+ }
+ // We've figured out what this stop wants to do, so mark it as valid so we don't compute it again.
+ m_should_stop_is_valid = true;
+
+ }
+ else
+ {
+ m_should_stop = true;
+ m_should_stop_is_valid = true;
+ Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log_process)
+ log_process->Printf ("Process::%s could not find breakpoint site id: %" PRId64 "...", __FUNCTION__, m_value);
+ }
+ if (log)
+ log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop);
+ }
+ }
+
+private:
+ std::string m_description;
+ bool m_should_stop;
+ bool m_should_stop_is_valid;
+ bool m_should_perform_action; // Since we are trying to preserve the "state" of the system even if we run functions
+ // etc. behind the users backs, we need to make sure we only REALLY perform the action once.
+ lldb::addr_t m_address; // We use this to capture the breakpoint site address when we create the StopInfo,
+ // in case somebody deletes it between the time the StopInfo is made and the
+ // description is asked for.
+ lldb::break_id_t m_break_id;
+ bool m_was_one_shot;
+};
+
+
+//----------------------------------------------------------------------
+// StopInfoWatchpoint
+//----------------------------------------------------------------------
+
+class StopInfoWatchpoint : public StopInfo
+{
+public:
+ // Make sure watchpoint is properly disabled and subsequently enabled while performing watchpoint actions.
+ class WatchpointSentry {
+ public:
+ WatchpointSentry(Process *p, Watchpoint *w):
+ process(p),
+ watchpoint(w)
+ {
+ if (process && watchpoint)
+ {
+ const bool notify = false;
+ watchpoint->TurnOnEphemeralMode();
+ process->DisableWatchpoint(watchpoint, notify);
+ }
+ }
+ ~WatchpointSentry()
+ {
+ if (process && watchpoint)
+ {
+ if (!watchpoint->IsDisabledDuringEphemeralMode())
+ {
+ const bool notify = false;
+ process->EnableWatchpoint(watchpoint, notify);
+ }
+ watchpoint->TurnOffEphemeralMode();
+ }
+ }
+ private:
+ Process *process;
+ Watchpoint *watchpoint;
+ };
+
+ StopInfoWatchpoint (Thread &thread, break_id_t watch_id) :
+ StopInfo(thread, watch_id),
+ m_description(),
+ m_should_stop(false),
+ m_should_stop_is_valid(false)
+ {
+ }
+
+ virtual ~StopInfoWatchpoint ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonWatchpoint;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ {
+ StreamString strm;
+ strm.Printf("watchpoint %" PRIi64, m_value);
+ m_description.swap (strm.GetString());
+ }
+ return m_description.c_str();
+ }
+
+protected:
+ virtual bool
+ ShouldStopSynchronous (Event *event_ptr)
+ {
+ // ShouldStop() method is idempotent and should not affect hit count.
+ // See Process::RunPrivateStateThread()->Process()->HandlePrivateEvent()
+ // -->Process()::ShouldBroadcastEvent()->ThreadList::ShouldStop()->
+ // Thread::ShouldStop()->ThreadPlanBase::ShouldStop()->
+ // StopInfoWatchpoint::ShouldStop() and
+ // Event::DoOnRemoval()->Process::ProcessEventData::DoOnRemoval()->
+ // StopInfoWatchpoint::PerformAction().
+ if (m_should_stop_is_valid)
+ return m_should_stop;
+
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue()));
+ if (wp_sp)
+ {
+ // Check if we should stop at a watchpoint.
+ ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0));
+ StoppointCallbackContext context (event_ptr, exe_ctx, true);
+ m_should_stop = wp_sp->ShouldStop (&context);
+ }
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log)
+ log->Printf ("Process::%s could not find watchpoint location id: %" PRId64 "...",
+ __FUNCTION__, GetValue());
+
+ m_should_stop = true;
+ }
+ }
+ m_should_stop_is_valid = true;
+ return m_should_stop;
+ }
+
+ bool
+ ShouldStop (Event *event_ptr)
+ {
+ // This just reports the work done by PerformAction or the synchronous stop. It should
+ // only ever get called after they have had a chance to run.
+ assert (m_should_stop_is_valid);
+ return m_should_stop;
+ }
+
+ virtual void
+ PerformAction (Event *event_ptr)
+ {
+ Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS);
+ // We're going to calculate if we should stop or not in some way during the course of
+ // this code. Also by default we're going to stop, so set that here.
+ m_should_stop = true;
+
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+
+ WatchpointSP wp_sp (thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue()));
+ if (wp_sp)
+ {
+ ExecutionContext exe_ctx (thread_sp->GetStackFrameAtIndex(0));
+ Process* process = exe_ctx.GetProcessPtr();
+
+ // This sentry object makes sure the current watchpoint is disabled while performing watchpoint actions,
+ // and it is then enabled after we are finished.
+ WatchpointSentry sentry(process, wp_sp.get());
+
+ {
+ // check if this process is running on an architecture where watchpoints trigger
+ // before the associated instruction runs. if so, disable the WP, single-step and then
+ // re-enable the watchpoint
+ if (process)
+ {
+ uint32_t num;
+ bool wp_triggers_after;
+ if (process->GetWatchpointSupportInfo(num, wp_triggers_after).Success())
+ {
+ if (!wp_triggers_after)
+ {
+ StopInfoSP stored_stop_info_sp = thread_sp->GetStopInfo();
+ assert (stored_stop_info_sp.get() == this);
+
+ ThreadPlanSP new_plan_sp(thread_sp->QueueThreadPlanForStepSingleInstruction(false, // step-over
+ false, // abort_other_plans
+ true)); // stop_other_threads
+ new_plan_sp->SetIsMasterPlan (true);
+ new_plan_sp->SetOkayToDiscard (false);
+ new_plan_sp->SetPrivate (true);
+ process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID());
+ process->Resume ();
+ process->WaitForProcessToStop (NULL);
+ process->GetThreadList().SetSelectedThreadByID (thread_sp->GetID());
+ thread_sp->SetStopInfo(stored_stop_info_sp);
+ }
+ }
+ }
+ }
+
+ if (m_should_stop && wp_sp->GetConditionText() != NULL)
+ {
+ // We need to make sure the user sees any parse errors in their condition, so we'll hook the
+ // constructor errors up to the debugger's Async I/O.
+ ExecutionResults result_code;
+ ValueObjectSP result_value_sp;
+ const bool unwind_on_error = true;
+ const bool ignore_breakpoints = true;
+ Error error;
+ result_code = ClangUserExpression::EvaluateWithError (exe_ctx,
+ eExecutionPolicyOnlyWhenNeeded,
+ lldb::eLanguageTypeUnknown,
+ ClangUserExpression::eResultTypeAny,
+ unwind_on_error,
+ ignore_breakpoints,
+ wp_sp->GetConditionText(),
+ NULL,
+ result_value_sp,
+ error,
+ true,
+ ClangUserExpression::kDefaultTimeout);
+ if (result_code == eExecutionCompleted)
+ {
+ if (result_value_sp)
+ {
+ Scalar scalar_value;
+ if (result_value_sp->ResolveValue (scalar_value))
+ {
+ if (scalar_value.ULongLong(1) == 0)
+ {
+ // We have been vetoed. This takes precedence over querying
+ // the watchpoint whether it should stop (aka ignore count and
+ // friends). See also StopInfoWatchpoint::ShouldStop() as well
+ // as Process::ProcessEventData::DoOnRemoval().
+ m_should_stop = false;
+ }
+ else
+ m_should_stop = true;
+ if (log)
+ log->Printf("Condition successfully evaluated, result is %s.\n",
+ m_should_stop ? "true" : "false");
+ }
+ else
+ {
+ m_should_stop = true;
+ if (log)
+ log->Printf("Failed to get an integer result from the expression.");
+ }
+ }
+ }
+ else
+ {
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP error_sp = debugger.GetAsyncErrorStream ();
+ error_sp->Printf ("Stopped due to an error evaluating condition of watchpoint ");
+ wp_sp->GetDescription (error_sp.get(), eDescriptionLevelBrief);
+ error_sp->Printf (": \"%s\"",
+ wp_sp->GetConditionText());
+ error_sp->EOL();
+ const char *err_str = error.AsCString("<Unknown Error>");
+ if (log)
+ log->Printf("Error evaluating condition: \"%s\"\n", err_str);
+
+ error_sp->PutCString (err_str);
+ error_sp->EOL();
+ error_sp->Flush();
+ // If the condition fails to be parsed or run, we should stop.
+ m_should_stop = true;
+ }
+ }
+
+ // If the condition says to stop, we run the callback to further decide whether to stop.
+ if (m_should_stop)
+ {
+ StoppointCallbackContext context (event_ptr, exe_ctx, false);
+ bool stop_requested = wp_sp->InvokeCallback (&context);
+ // Also make sure that the callback hasn't continued the target.
+ // If it did, when we'll set m_should_stop to false and get out of here.
+ if (HasTargetRunSinceMe ())
+ m_should_stop = false;
+
+ if (m_should_stop && !stop_requested)
+ {
+ // We have been vetoed by the callback mechanism.
+ m_should_stop = false;
+ }
+ }
+ // Finally, if we are going to stop, print out the new & old values:
+ if (m_should_stop)
+ {
+ wp_sp->CaptureWatchedValue(exe_ctx);
+
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP output_sp = debugger.GetAsyncOutputStream ();
+ wp_sp->DumpSnapshots(output_sp.get());
+ output_sp->EOL();
+ output_sp->Flush();
+ }
+
+ }
+ else
+ {
+ Log * log_process(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS));
+
+ if (log_process)
+ log_process->Printf ("Process::%s could not find watchpoint id: %" PRId64 "...", __FUNCTION__, m_value);
+ }
+ if (log)
+ log->Printf ("Process::%s returning from action with m_should_stop: %d.", __FUNCTION__, m_should_stop);
+
+ m_should_stop_is_valid = true;
+ }
+ }
+
+private:
+ std::string m_description;
+ bool m_should_stop;
+ bool m_should_stop_is_valid;
+};
+
+
+
+//----------------------------------------------------------------------
+// StopInfoUnixSignal
+//----------------------------------------------------------------------
+
+class StopInfoUnixSignal : public StopInfo
+{
+public:
+
+ StopInfoUnixSignal (Thread &thread, int signo) :
+ StopInfo (thread, signo)
+ {
+ }
+
+ virtual ~StopInfoUnixSignal ()
+ {
+ }
+
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonSignal;
+ }
+
+ virtual bool
+ ShouldStopSynchronous (Event *event_ptr)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetUnixSignals().GetShouldStop (m_value);
+ return false;
+ }
+
+ virtual bool
+ ShouldStop (Event *event_ptr)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ return thread_sp->GetProcess()->GetUnixSignals().GetShouldStop (m_value);
+ return false;
+ }
+
+
+ // If should stop returns false, check if we should notify of this event
+ virtual bool
+ DoShouldNotify (Event *event_ptr)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ bool should_notify = thread_sp->GetProcess()->GetUnixSignals().GetShouldNotify (m_value);
+ if (should_notify)
+ {
+ StreamString strm;
+ strm.Printf ("thread %d received signal: %s",
+ thread_sp->GetIndexID(),
+ thread_sp->GetProcess()->GetUnixSignals().GetSignalAsCString (m_value));
+ Process::ProcessEventData::AddRestartedReason(event_ptr, strm.GetData());
+ }
+ return should_notify;
+ }
+ return true;
+ }
+
+
+ virtual void
+ WillResume (lldb::StateType resume_state)
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ if (thread_sp->GetProcess()->GetUnixSignals().GetShouldSuppress(m_value) == false)
+ thread_sp->SetResumeSignal(m_value);
+ }
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ {
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ {
+ StreamString strm;
+ const char *signal_name = thread_sp->GetProcess()->GetUnixSignals().GetSignalAsCString (m_value);
+ if (signal_name)
+ strm.Printf("signal %s", signal_name);
+ else
+ strm.Printf("signal %" PRIi64, m_value);
+ m_description.swap (strm.GetString());
+ }
+ }
+ return m_description.c_str();
+ }
+};
+
+//----------------------------------------------------------------------
+// StopInfoTrace
+//----------------------------------------------------------------------
+
+class StopInfoTrace : public StopInfo
+{
+public:
+
+ StopInfoTrace (Thread &thread) :
+ StopInfo (thread, LLDB_INVALID_UID)
+ {
+ }
+
+ virtual ~StopInfoTrace ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonTrace;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ return "trace";
+ else
+ return m_description.c_str();
+ }
+};
+
+
+//----------------------------------------------------------------------
+// StopInfoException
+//----------------------------------------------------------------------
+
+class StopInfoException : public StopInfo
+{
+public:
+
+ StopInfoException (Thread &thread, const char *description) :
+ StopInfo (thread, LLDB_INVALID_UID)
+ {
+ if (description)
+ SetDescription (description);
+ }
+
+ virtual
+ ~StopInfoException ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonException;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ return "exception";
+ else
+ return m_description.c_str();
+ }
+};
+
+
+//----------------------------------------------------------------------
+// StopInfoThreadPlan
+//----------------------------------------------------------------------
+
+class StopInfoThreadPlan : public StopInfo
+{
+public:
+
+ StopInfoThreadPlan (ThreadPlanSP &plan_sp, ValueObjectSP &return_valobj_sp) :
+ StopInfo (plan_sp->GetThread(), LLDB_INVALID_UID),
+ m_plan_sp (plan_sp),
+ m_return_valobj_sp (return_valobj_sp)
+ {
+ }
+
+ virtual ~StopInfoThreadPlan ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonPlanComplete;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ if (m_description.empty())
+ {
+ StreamString strm;
+ m_plan_sp->GetDescription (&strm, eDescriptionLevelBrief);
+ m_description.swap (strm.GetString());
+ }
+ return m_description.c_str();
+ }
+
+ ValueObjectSP
+ GetReturnValueObject()
+ {
+ return m_return_valobj_sp;
+ }
+
+protected:
+ virtual bool
+ ShouldStop (Event *event_ptr)
+ {
+ if (m_plan_sp)
+ return m_plan_sp->ShouldStop(event_ptr);
+ else
+ return StopInfo::ShouldStop(event_ptr);
+ }
+
+private:
+ ThreadPlanSP m_plan_sp;
+ ValueObjectSP m_return_valobj_sp;
+};
+
+class StopInfoExec : public StopInfo
+{
+public:
+
+ StopInfoExec (Thread &thread) :
+ StopInfo (thread, LLDB_INVALID_UID),
+ m_performed_action (false)
+ {
+ }
+
+ virtual
+ ~StopInfoExec ()
+ {
+ }
+
+ virtual StopReason
+ GetStopReason () const
+ {
+ return eStopReasonExec;
+ }
+
+ virtual const char *
+ GetDescription ()
+ {
+ return "exec";
+ }
+protected:
+
+ virtual void
+ PerformAction (Event *event_ptr)
+ {
+ // Only perform the action once
+ if (m_performed_action)
+ return;
+ m_performed_action = true;
+ ThreadSP thread_sp (m_thread_wp.lock());
+ if (thread_sp)
+ thread_sp->GetProcess()->DidExec();
+ }
+
+ bool m_performed_action;
+};
+
+} // namespace lldb_private
+
+StopInfoSP
+StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id)
+{
+ return StopInfoSP (new StopInfoBreakpoint (thread, break_id));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithBreakpointSiteID (Thread &thread, break_id_t break_id, bool should_stop)
+{
+ return StopInfoSP (new StopInfoBreakpoint (thread, break_id, should_stop));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithWatchpointID (Thread &thread, break_id_t watch_id)
+{
+ return StopInfoSP (new StopInfoWatchpoint (thread, watch_id));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithSignal (Thread &thread, int signo)
+{
+ return StopInfoSP (new StopInfoUnixSignal (thread, signo));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonToTrace (Thread &thread)
+{
+ return StopInfoSP (new StopInfoTrace (thread));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithPlan (ThreadPlanSP &plan_sp, ValueObjectSP return_valobj_sp)
+{
+ return StopInfoSP (new StopInfoThreadPlan (plan_sp, return_valobj_sp));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithException (Thread &thread, const char *description)
+{
+ return StopInfoSP (new StopInfoException (thread, description));
+}
+
+StopInfoSP
+StopInfo::CreateStopReasonWithExec (Thread &thread)
+{
+ return StopInfoSP (new StopInfoExec (thread));
+}
+
+ValueObjectSP
+StopInfo::GetReturnValueObject(StopInfoSP &stop_info_sp)
+{
+ if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonPlanComplete)
+ {
+ StopInfoThreadPlan *plan_stop_info = static_cast<StopInfoThreadPlan *>(stop_info_sp.get());
+ return plan_stop_info->GetReturnValueObject();
+ }
+ else
+ return ValueObjectSP();
+}
diff --git a/source/Target/Target.cpp b/source/Target/Target.cpp
new file mode 100644
index 000000000000..5766b737c7d8
--- /dev/null
+++ b/source/Target/Target.cpp
@@ -0,0 +1,2866 @@
+//===-- Target.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/lldb-python.h"
+
+#include "lldb/Target/Target.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/BreakpointResolver.h"
+#include "lldb/Breakpoint/BreakpointResolverAddress.h"
+#include "lldb/Breakpoint/BreakpointResolverFileLine.h"
+#include "lldb/Breakpoint/BreakpointResolverFileRegex.h"
+#include "lldb/Breakpoint/BreakpointResolverName.h"
+#include "lldb/Breakpoint/Watchpoint.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Event.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Core/SourceManager.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Core/ValueObject.h"
+#include "lldb/Expression/ClangASTSource.h"
+#include "lldb/Expression/ClangUserExpression.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandReturnObject.h"
+#include "lldb/Interpreter/OptionGroupWatchpoint.h"
+#include "lldb/Interpreter/OptionValues.h"
+#include "lldb/Interpreter/Property.h"
+#include "lldb/lldb-private-log.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/StackFrame.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConstString &
+Target::GetStaticBroadcasterClass ()
+{
+ static ConstString class_name ("lldb.target");
+ return class_name;
+}
+
+//----------------------------------------------------------------------
+// Target constructor
+//----------------------------------------------------------------------
+Target::Target(Debugger &debugger, const ArchSpec &target_arch, const lldb::PlatformSP &platform_sp) :
+ TargetProperties (this),
+ Broadcaster (&debugger, Target::GetStaticBroadcasterClass().AsCString()),
+ ExecutionContextScope (),
+ m_debugger (debugger),
+ m_platform_sp (platform_sp),
+ m_mutex (Mutex::eMutexTypeRecursive),
+ m_arch (target_arch),
+ m_images (this),
+ m_section_load_list (),
+ m_breakpoint_list (false),
+ m_internal_breakpoint_list (true),
+ m_watchpoint_list (),
+ m_process_sp (),
+ m_valid (true),
+ m_search_filter_sp (),
+ m_image_search_paths (ImageSearchPathsChanged, this),
+ m_scratch_ast_context_ap (),
+ m_scratch_ast_source_ap (),
+ m_ast_importer_ap (),
+ m_persistent_variables (),
+ m_source_manager_ap(),
+ m_stop_hooks (),
+ m_stop_hook_next_id (0),
+ m_suppress_stop_hooks (false),
+ m_suppress_synthetic_value(false)
+{
+ SetEventName (eBroadcastBitBreakpointChanged, "breakpoint-changed");
+ SetEventName (eBroadcastBitModulesLoaded, "modules-loaded");
+ SetEventName (eBroadcastBitModulesUnloaded, "modules-unloaded");
+ SetEventName (eBroadcastBitWatchpointChanged, "watchpoint-changed");
+ SetEventName (eBroadcastBitSymbolsLoaded, "symbols-loaded");
+
+ CheckInWithManager();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Target::Target()", this);
+ if (m_arch.IsValid())
+ {
+ LogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET, "Target::Target created with architecture %s (%s)", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str());
+ }
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+Target::~Target()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Target::~Target()", this);
+ DeleteCurrentProcess ();
+}
+
+void
+Target::Dump (Stream *s, lldb::DescriptionLevel description_level)
+{
+// s->Printf("%.*p: ", (int)sizeof(void*) * 2, this);
+ if (description_level != lldb::eDescriptionLevelBrief)
+ {
+ s->Indent();
+ s->PutCString("Target\n");
+ s->IndentMore();
+ m_images.Dump(s);
+ m_breakpoint_list.Dump(s);
+ m_internal_breakpoint_list.Dump(s);
+ s->IndentLess();
+ }
+ else
+ {
+ Module *exe_module = GetExecutableModulePointer();
+ if (exe_module)
+ s->PutCString (exe_module->GetFileSpec().GetFilename().GetCString());
+ else
+ s->PutCString ("No executable module.");
+ }
+}
+
+void
+Target::CleanupProcess ()
+{
+ // Do any cleanup of the target we need to do between process instances.
+ // NB It is better to do this before destroying the process in case the
+ // clean up needs some help from the process.
+ m_breakpoint_list.ClearAllBreakpointSites();
+ m_internal_breakpoint_list.ClearAllBreakpointSites();
+ // Disable watchpoints just on the debugger side.
+ Mutex::Locker locker;
+ this->GetWatchpointList().GetListMutex(locker);
+ DisableAllWatchpoints(false);
+ ClearAllWatchpointHitCounts();
+}
+
+void
+Target::DeleteCurrentProcess ()
+{
+ if (m_process_sp.get())
+ {
+ m_section_load_list.Clear();
+ if (m_process_sp->IsAlive())
+ m_process_sp->Destroy();
+
+ m_process_sp->Finalize();
+
+ CleanupProcess ();
+
+ m_process_sp.reset();
+ }
+}
+
+const lldb::ProcessSP &
+Target::CreateProcess (Listener &listener, const char *plugin_name, const FileSpec *crash_file)
+{
+ DeleteCurrentProcess ();
+ m_process_sp = Process::FindPlugin(*this, plugin_name, listener, crash_file);
+ return m_process_sp;
+}
+
+const lldb::ProcessSP &
+Target::GetProcessSP () const
+{
+ return m_process_sp;
+}
+
+void
+Target::Destroy()
+{
+ Mutex::Locker locker (m_mutex);
+ m_valid = false;
+ DeleteCurrentProcess ();
+ m_platform_sp.reset();
+ m_arch.Clear();
+ m_images.Clear();
+ m_section_load_list.Clear();
+ const bool notify = false;
+ m_breakpoint_list.RemoveAll(notify);
+ m_internal_breakpoint_list.RemoveAll(notify);
+ m_last_created_breakpoint.reset();
+ m_last_created_watchpoint.reset();
+ m_search_filter_sp.reset();
+ m_image_search_paths.Clear(notify);
+ m_scratch_ast_context_ap.reset();
+ m_scratch_ast_source_ap.reset();
+ m_ast_importer_ap.reset();
+ m_persistent_variables.Clear();
+ m_stop_hooks.clear();
+ m_stop_hook_next_id = 0;
+ m_suppress_stop_hooks = false;
+ m_suppress_synthetic_value = false;
+}
+
+
+BreakpointList &
+Target::GetBreakpointList(bool internal)
+{
+ if (internal)
+ return m_internal_breakpoint_list;
+ else
+ return m_breakpoint_list;
+}
+
+const BreakpointList &
+Target::GetBreakpointList(bool internal) const
+{
+ if (internal)
+ return m_internal_breakpoint_list;
+ else
+ return m_breakpoint_list;
+}
+
+BreakpointSP
+Target::GetBreakpointByID (break_id_t break_id)
+{
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL (break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID (break_id);
+
+ return bp_sp;
+}
+
+BreakpointSP
+Target::CreateSourceRegexBreakpoint (const FileSpecList *containingModules,
+ const FileSpecList *source_file_spec_list,
+ RegularExpression &source_regex,
+ bool internal)
+{
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, source_file_spec_list));
+ BreakpointResolverSP resolver_sp(new BreakpointResolverFileRegex (NULL, source_regex));
+ return CreateBreakpoint (filter_sp, resolver_sp, internal);
+}
+
+
+BreakpointSP
+Target::CreateBreakpoint (const FileSpecList *containingModules,
+ const FileSpec &file,
+ uint32_t line_no,
+ LazyBool check_inlines,
+ LazyBool skip_prologue,
+ bool internal)
+{
+ if (check_inlines == eLazyBoolCalculate)
+ {
+ const InlineStrategy inline_strategy = GetInlineStrategy();
+ switch (inline_strategy)
+ {
+ case eInlineBreakpointsNever:
+ check_inlines = eLazyBoolNo;
+ break;
+
+ case eInlineBreakpointsHeaders:
+ if (file.IsSourceImplementationFile())
+ check_inlines = eLazyBoolNo;
+ else
+ check_inlines = eLazyBoolYes;
+ break;
+
+ case eInlineBreakpointsAlways:
+ check_inlines = eLazyBoolYes;
+ break;
+ }
+ }
+ SearchFilterSP filter_sp;
+ if (check_inlines == eLazyBoolNo)
+ {
+ // Not checking for inlines, we are looking only for matching compile units
+ FileSpecList compile_unit_list;
+ compile_unit_list.Append (file);
+ filter_sp = GetSearchFilterForModuleAndCUList (containingModules, &compile_unit_list);
+ }
+ else
+ {
+ filter_sp = GetSearchFilterForModuleList (containingModules);
+ }
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+
+ BreakpointResolverSP resolver_sp(new BreakpointResolverFileLine (NULL,
+ file,
+ line_no,
+ check_inlines,
+ skip_prologue));
+ return CreateBreakpoint (filter_sp, resolver_sp, internal);
+}
+
+
+BreakpointSP
+Target::CreateBreakpoint (lldb::addr_t addr, bool internal)
+{
+ Address so_addr;
+ // Attempt to resolve our load address if possible, though it is ok if
+ // it doesn't resolve to section/offset.
+
+ // Try and resolve as a load address if possible
+ m_section_load_list.ResolveLoadAddress(addr, so_addr);
+ if (!so_addr.IsValid())
+ {
+ // The address didn't resolve, so just set this as an absolute address
+ so_addr.SetOffset (addr);
+ }
+ BreakpointSP bp_sp (CreateBreakpoint(so_addr, internal));
+ return bp_sp;
+}
+
+BreakpointSP
+Target::CreateBreakpoint (Address &addr, bool internal)
+{
+ SearchFilterSP filter_sp(new SearchFilterForNonModuleSpecificSearches (shared_from_this()));
+ BreakpointResolverSP resolver_sp (new BreakpointResolverAddress (NULL, addr));
+ return CreateBreakpoint (filter_sp, resolver_sp, internal);
+}
+
+BreakpointSP
+Target::CreateBreakpoint (const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ const char *func_name,
+ uint32_t func_name_type_mask,
+ LazyBool skip_prologue,
+ bool internal)
+{
+ BreakpointSP bp_sp;
+ if (func_name)
+ {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+
+ BreakpointResolverSP resolver_sp (new BreakpointResolverName (NULL,
+ func_name,
+ func_name_type_mask,
+ Breakpoint::Exact,
+ skip_prologue));
+ bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal);
+ }
+ return bp_sp;
+}
+
+lldb::BreakpointSP
+Target::CreateBreakpoint (const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ const std::vector<std::string> &func_names,
+ uint32_t func_name_type_mask,
+ LazyBool skip_prologue,
+ bool internal)
+{
+ BreakpointSP bp_sp;
+ size_t num_names = func_names.size();
+ if (num_names > 0)
+ {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+
+ BreakpointResolverSP resolver_sp (new BreakpointResolverName (NULL,
+ func_names,
+ func_name_type_mask,
+ skip_prologue));
+ bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal);
+ }
+ return bp_sp;
+}
+
+BreakpointSP
+Target::CreateBreakpoint (const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ const char *func_names[],
+ size_t num_names,
+ uint32_t func_name_type_mask,
+ LazyBool skip_prologue,
+ bool internal)
+{
+ BreakpointSP bp_sp;
+ if (num_names > 0)
+ {
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles));
+
+ if (skip_prologue == eLazyBoolCalculate)
+ skip_prologue = GetSkipPrologue() ? eLazyBoolYes : eLazyBoolNo;
+
+ BreakpointResolverSP resolver_sp (new BreakpointResolverName (NULL,
+ func_names,
+ num_names,
+ func_name_type_mask,
+ skip_prologue));
+ bp_sp = CreateBreakpoint (filter_sp, resolver_sp, internal);
+ }
+ return bp_sp;
+}
+
+SearchFilterSP
+Target::GetSearchFilterForModule (const FileSpec *containingModule)
+{
+ SearchFilterSP filter_sp;
+ if (containingModule != NULL)
+ {
+ // TODO: We should look into sharing module based search filters
+ // across many breakpoints like we do for the simple target based one
+ filter_sp.reset (new SearchFilterByModule (shared_from_this(), *containingModule));
+ }
+ else
+ {
+ if (m_search_filter_sp.get() == NULL)
+ m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this()));
+ filter_sp = m_search_filter_sp;
+ }
+ return filter_sp;
+}
+
+SearchFilterSP
+Target::GetSearchFilterForModuleList (const FileSpecList *containingModules)
+{
+ SearchFilterSP filter_sp;
+ if (containingModules && containingModules->GetSize() != 0)
+ {
+ // TODO: We should look into sharing module based search filters
+ // across many breakpoints like we do for the simple target based one
+ filter_sp.reset (new SearchFilterByModuleList (shared_from_this(), *containingModules));
+ }
+ else
+ {
+ if (m_search_filter_sp.get() == NULL)
+ m_search_filter_sp.reset (new SearchFilterForNonModuleSpecificSearches (shared_from_this()));
+ filter_sp = m_search_filter_sp;
+ }
+ return filter_sp;
+}
+
+SearchFilterSP
+Target::GetSearchFilterForModuleAndCUList (const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles)
+{
+ if (containingSourceFiles == NULL || containingSourceFiles->GetSize() == 0)
+ return GetSearchFilterForModuleList(containingModules);
+
+ SearchFilterSP filter_sp;
+ if (containingModules == NULL)
+ {
+ // We could make a special "CU List only SearchFilter". Better yet was if these could be composable,
+ // but that will take a little reworking.
+
+ filter_sp.reset (new SearchFilterByModuleListAndCU (shared_from_this(), FileSpecList(), *containingSourceFiles));
+ }
+ else
+ {
+ filter_sp.reset (new SearchFilterByModuleListAndCU (shared_from_this(), *containingModules, *containingSourceFiles));
+ }
+ return filter_sp;
+}
+
+BreakpointSP
+Target::CreateFuncRegexBreakpoint (const FileSpecList *containingModules,
+ const FileSpecList *containingSourceFiles,
+ RegularExpression &func_regex,
+ LazyBool skip_prologue,
+ bool internal)
+{
+ SearchFilterSP filter_sp(GetSearchFilterForModuleAndCUList (containingModules, containingSourceFiles));
+ BreakpointResolverSP resolver_sp(new BreakpointResolverName (NULL,
+ func_regex,
+ skip_prologue == eLazyBoolCalculate ? GetSkipPrologue() : skip_prologue));
+
+ return CreateBreakpoint (filter_sp, resolver_sp, internal);
+}
+
+lldb::BreakpointSP
+Target::CreateExceptionBreakpoint (enum lldb::LanguageType language, bool catch_bp, bool throw_bp, bool internal)
+{
+ return LanguageRuntime::CreateExceptionBreakpoint (*this, language, catch_bp, throw_bp, internal);
+}
+
+BreakpointSP
+Target::CreateBreakpoint (SearchFilterSP &filter_sp, BreakpointResolverSP &resolver_sp, bool internal)
+{
+ BreakpointSP bp_sp;
+ if (filter_sp && resolver_sp)
+ {
+ bp_sp.reset(new Breakpoint (*this, filter_sp, resolver_sp));
+ resolver_sp->SetBreakpoint (bp_sp.get());
+
+ if (internal)
+ m_internal_breakpoint_list.Add (bp_sp, false);
+ else
+ m_breakpoint_list.Add (bp_sp, true);
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ {
+ StreamString s;
+ bp_sp->GetDescription(&s, lldb::eDescriptionLevelVerbose);
+ log->Printf ("Target::%s (internal = %s) => break_id = %s\n", __FUNCTION__, internal ? "yes" : "no", s.GetData());
+ }
+
+ bp_sp->ResolveBreakpoint();
+ }
+
+ if (!internal && bp_sp)
+ {
+ m_last_created_breakpoint = bp_sp;
+ }
+
+ return bp_sp;
+}
+
+bool
+Target::ProcessIsValid()
+{
+ return (m_process_sp && m_process_sp->IsAlive());
+}
+
+static bool
+CheckIfWatchpointsExhausted(Target *target, Error &error)
+{
+ uint32_t num_supported_hardware_watchpoints;
+ Error rc = target->GetProcessSP()->GetWatchpointSupportInfo(num_supported_hardware_watchpoints);
+ if (rc.Success())
+ {
+ uint32_t num_current_watchpoints = target->GetWatchpointList().GetSize();
+ if (num_current_watchpoints >= num_supported_hardware_watchpoints)
+ error.SetErrorStringWithFormat("number of supported hardware watchpoints (%u) has been reached",
+ num_supported_hardware_watchpoints);
+ }
+ return false;
+}
+
+// See also Watchpoint::SetWatchpointType(uint32_t type) and
+// the OptionGroupWatchpoint::WatchType enum type.
+WatchpointSP
+Target::CreateWatchpoint(lldb::addr_t addr, size_t size, const ClangASTType *type, uint32_t kind, Error &error)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf("Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64 " type = %u)\n",
+ __FUNCTION__, addr, (uint64_t)size, kind);
+
+ WatchpointSP wp_sp;
+ if (!ProcessIsValid())
+ {
+ error.SetErrorString("process is not alive");
+ return wp_sp;
+ }
+
+ if (addr == LLDB_INVALID_ADDRESS || size == 0)
+ {
+ if (size == 0)
+ error.SetErrorString("cannot set a watchpoint with watch_size of 0");
+ else
+ error.SetErrorStringWithFormat("invalid watch address: %" PRIu64, addr);
+ return wp_sp;
+ }
+
+ if (!LLDB_WATCH_TYPE_IS_VALID(kind))
+ {
+ error.SetErrorStringWithFormat ("invalid watchpoint type: %d", kind);
+ }
+
+ // Currently we only support one watchpoint per address, with total number
+ // of watchpoints limited by the hardware which the inferior is running on.
+
+ // Grab the list mutex while doing operations.
+ const bool notify = false; // Don't notify about all the state changes we do on creating the watchpoint.
+ Mutex::Locker locker;
+ this->GetWatchpointList().GetListMutex(locker);
+ WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr);
+ if (matched_sp)
+ {
+ size_t old_size = matched_sp->GetByteSize();
+ uint32_t old_type =
+ (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) |
+ (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0);
+ // Return the existing watchpoint if both size and type match.
+ if (size == old_size && kind == old_type)
+ {
+ wp_sp = matched_sp;
+ wp_sp->SetEnabled(false, notify);
+ }
+ else
+ {
+ // Nil the matched watchpoint; we will be creating a new one.
+ m_process_sp->DisableWatchpoint(matched_sp.get(), notify);
+ m_watchpoint_list.Remove(matched_sp->GetID(), true);
+ }
+ }
+
+ if (!wp_sp)
+ {
+ wp_sp.reset(new Watchpoint(*this, addr, size, type));
+ wp_sp->SetWatchpointType(kind, notify);
+ m_watchpoint_list.Add (wp_sp, true);
+ }
+
+ error = m_process_sp->EnableWatchpoint(wp_sp.get(), notify);
+ if (log)
+ log->Printf("Target::%s (creation of watchpoint %s with id = %u)\n",
+ __FUNCTION__,
+ error.Success() ? "succeeded" : "failed",
+ wp_sp->GetID());
+
+ if (error.Fail())
+ {
+ // Enabling the watchpoint on the device side failed.
+ // Remove the said watchpoint from the list maintained by the target instance.
+ m_watchpoint_list.Remove (wp_sp->GetID(), true);
+ // See if we could provide more helpful error message.
+ if (!CheckIfWatchpointsExhausted(this, error))
+ {
+ if (!OptionGroupWatchpoint::IsWatchSizeSupported(size))
+ error.SetErrorStringWithFormat("watch size of %lu is not supported", size);
+ }
+ wp_sp.reset();
+ }
+ else
+ m_last_created_watchpoint = wp_sp;
+ return wp_sp;
+}
+
+void
+Target::RemoveAllBreakpoints (bool internal_also)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no");
+
+ m_breakpoint_list.RemoveAll (true);
+ if (internal_also)
+ m_internal_breakpoint_list.RemoveAll (false);
+
+ m_last_created_breakpoint.reset();
+}
+
+void
+Target::DisableAllBreakpoints (bool internal_also)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no");
+
+ m_breakpoint_list.SetEnabledAll (false);
+ if (internal_also)
+ m_internal_breakpoint_list.SetEnabledAll (false);
+}
+
+void
+Target::EnableAllBreakpoints (bool internal_also)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (internal_also = %s)\n", __FUNCTION__, internal_also ? "yes" : "no");
+
+ m_breakpoint_list.SetEnabledAll (true);
+ if (internal_also)
+ m_internal_breakpoint_list.SetEnabledAll (true);
+}
+
+bool
+Target::RemoveBreakpointByID (break_id_t break_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no");
+
+ if (DisableBreakpointByID (break_id))
+ {
+ if (LLDB_BREAK_ID_IS_INTERNAL (break_id))
+ m_internal_breakpoint_list.Remove(break_id, false);
+ else
+ {
+ if (m_last_created_breakpoint)
+ {
+ if (m_last_created_breakpoint->GetID() == break_id)
+ m_last_created_breakpoint.reset();
+ }
+ m_breakpoint_list.Remove(break_id, true);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool
+Target::DisableBreakpointByID (break_id_t break_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (break_id = %i, internal = %s)\n", __FUNCTION__, break_id, LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no");
+
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL (break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID (break_id);
+ if (bp_sp)
+ {
+ bp_sp->SetEnabled (false);
+ return true;
+ }
+ return false;
+}
+
+bool
+Target::EnableBreakpointByID (break_id_t break_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS));
+ if (log)
+ log->Printf ("Target::%s (break_id = %i, internal = %s)\n",
+ __FUNCTION__,
+ break_id,
+ LLDB_BREAK_ID_IS_INTERNAL (break_id) ? "yes" : "no");
+
+ BreakpointSP bp_sp;
+
+ if (LLDB_BREAK_ID_IS_INTERNAL (break_id))
+ bp_sp = m_internal_breakpoint_list.FindBreakpointByID (break_id);
+ else
+ bp_sp = m_breakpoint_list.FindBreakpointByID (break_id);
+
+ if (bp_sp)
+ {
+ bp_sp->SetEnabled (true);
+ return true;
+ }
+ return false;
+}
+
+// The flag 'end_to_end', default to true, signifies that the operation is
+// performed end to end, for both the debugger and the debuggee.
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end
+// to end operations.
+bool
+Target::RemoveAllWatchpoints (bool end_to_end)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.RemoveAll(true);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i)
+ {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Error rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ m_watchpoint_list.RemoveAll (true);
+ m_last_created_watchpoint.reset();
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to
+// end operations.
+bool
+Target::DisableAllWatchpoints (bool end_to_end)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.SetEnabledAll(false);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i)
+ {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Error rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list for end to
+// end operations.
+bool
+Target::EnableAllWatchpoints (bool end_to_end)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s\n", __FUNCTION__);
+
+ if (!end_to_end) {
+ m_watchpoint_list.SetEnabledAll(true);
+ return true;
+ }
+
+ // Otherwise, it's an end to end operation.
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i)
+ {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ Error rc = m_process_sp->EnableWatchpoint(wp_sp.get());
+ if (rc.Fail())
+ return false;
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool
+Target::ClearAllWatchpointHitCounts ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s\n", __FUNCTION__);
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i)
+ {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ wp_sp->ResetHitCount();
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list
+// during these operations.
+bool
+Target::IgnoreAllWatchpoints (uint32_t ignore_count)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s\n", __FUNCTION__);
+
+ if (!ProcessIsValid())
+ return false;
+
+ size_t num_watchpoints = m_watchpoint_list.GetSize();
+ for (size_t i = 0; i < num_watchpoints; ++i)
+ {
+ WatchpointSP wp_sp = m_watchpoint_list.GetByIndex(i);
+ if (!wp_sp)
+ return false;
+
+ wp_sp->SetIgnoreCount(ignore_count);
+ }
+ return true; // Success!
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool
+Target::DisableWatchpointByID (lldb::watch_id_t watch_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id);
+ if (wp_sp)
+ {
+ Error rc = m_process_sp->DisableWatchpoint(wp_sp.get());
+ if (rc.Success())
+ return true;
+
+ // Else, fallthrough.
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool
+Target::EnableWatchpointByID (lldb::watch_id_t watch_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id);
+ if (wp_sp)
+ {
+ Error rc = m_process_sp->EnableWatchpoint(wp_sp.get());
+ if (rc.Success())
+ return true;
+
+ // Else, fallthrough.
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool
+Target::RemoveWatchpointByID (lldb::watch_id_t watch_id)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ WatchpointSP watch_to_remove_sp = m_watchpoint_list.FindByID(watch_id);
+ if (watch_to_remove_sp == m_last_created_watchpoint)
+ m_last_created_watchpoint.reset();
+
+ if (DisableWatchpointByID (watch_id))
+ {
+ m_watchpoint_list.Remove(watch_id, true);
+ return true;
+ }
+ return false;
+}
+
+// Assumption: Caller holds the list mutex lock for m_watchpoint_list.
+bool
+Target::IgnoreWatchpointByID (lldb::watch_id_t watch_id, uint32_t ignore_count)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_WATCHPOINTS));
+ if (log)
+ log->Printf ("Target::%s (watch_id = %i)\n", __FUNCTION__, watch_id);
+
+ if (!ProcessIsValid())
+ return false;
+
+ WatchpointSP wp_sp = m_watchpoint_list.FindByID (watch_id);
+ if (wp_sp)
+ {
+ wp_sp->SetIgnoreCount(ignore_count);
+ return true;
+ }
+ return false;
+}
+
+ModuleSP
+Target::GetExecutableModule ()
+{
+ return m_images.GetModuleAtIndex(0);
+}
+
+Module*
+Target::GetExecutableModulePointer ()
+{
+ return m_images.GetModulePointerAtIndex(0);
+}
+
+static void
+LoadScriptingResourceForModule (const ModuleSP &module_sp, Target *target)
+{
+ Error error;
+ StreamString feedback_stream;
+ if (module_sp && !module_sp->LoadScriptingResourceInTarget(target, error, &feedback_stream))
+ {
+ if (error.AsCString())
+ target->GetDebugger().GetErrorStream().Printf("unable to load scripting data for module %s - error reported was %s\n",
+ module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(),
+ error.AsCString());
+ if (feedback_stream.GetSize())
+ target->GetDebugger().GetOutputStream().Printf("%s\n",
+ feedback_stream.GetData());
+ }
+}
+
+void
+Target::SetExecutableModule (ModuleSP& executable_sp, bool get_dependent_files)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET));
+ m_images.Clear();
+ m_scratch_ast_context_ap.reset();
+ m_scratch_ast_source_ap.reset();
+ m_ast_importer_ap.reset();
+
+ if (executable_sp.get())
+ {
+ Timer scoped_timer (__PRETTY_FUNCTION__,
+ "Target::SetExecutableModule (executable = '%s')",
+ executable_sp->GetFileSpec().GetPath().c_str());
+
+ m_images.Append(executable_sp); // The first image is our exectuable file
+
+ // If we haven't set an architecture yet, reset our architecture based on what we found in the executable module.
+ if (!m_arch.IsValid())
+ {
+ m_arch = executable_sp->GetArchitecture();
+ if (log)
+ log->Printf ("Target::SetExecutableModule setting architecture to %s (%s) based on executable file", m_arch.GetArchitectureName(), m_arch.GetTriple().getTriple().c_str());
+ }
+
+ FileSpecList dependent_files;
+ ObjectFile *executable_objfile = executable_sp->GetObjectFile();
+
+ if (executable_objfile && get_dependent_files)
+ {
+ executable_objfile->GetDependentModules(dependent_files);
+ for (uint32_t i=0; i<dependent_files.GetSize(); i++)
+ {
+ FileSpec dependent_file_spec (dependent_files.GetFileSpecPointerAtIndex(i));
+ FileSpec platform_dependent_file_spec;
+ if (m_platform_sp)
+ m_platform_sp->GetFile (dependent_file_spec, NULL, platform_dependent_file_spec);
+ else
+ platform_dependent_file_spec = dependent_file_spec;
+
+ ModuleSpec module_spec (platform_dependent_file_spec, m_arch);
+ ModuleSP image_module_sp(GetSharedModule (module_spec));
+ if (image_module_sp.get())
+ {
+ ObjectFile *objfile = image_module_sp->GetObjectFile();
+ if (objfile)
+ objfile->GetDependentModules(dependent_files);
+ }
+ }
+ }
+ }
+}
+
+
+bool
+Target::SetArchitecture (const ArchSpec &arch_spec)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TARGET));
+ if (m_arch.IsCompatibleMatch(arch_spec) || !m_arch.IsValid())
+ {
+ // If we haven't got a valid arch spec, or the architectures are
+ // compatible, so just update the architecture. Architectures can be
+ // equal, yet the triple OS and vendor might change, so we need to do
+ // the assignment here just in case.
+ m_arch = arch_spec;
+ if (log)
+ log->Printf ("Target::SetArchitecture setting architecture to %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str());
+ return true;
+ }
+ else
+ {
+ // If we have an executable file, try to reset the executable to the desired architecture
+ if (log)
+ log->Printf ("Target::SetArchitecture changing architecture to %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str());
+ m_arch = arch_spec;
+ ModuleSP executable_sp = GetExecutableModule ();
+ m_images.Clear();
+ m_scratch_ast_context_ap.reset();
+ m_scratch_ast_source_ap.reset();
+ m_ast_importer_ap.reset();
+ // Need to do something about unsetting breakpoints.
+
+ if (executable_sp)
+ {
+ if (log)
+ log->Printf("Target::SetArchitecture Trying to select executable file architecture %s (%s)", arch_spec.GetArchitectureName(), arch_spec.GetTriple().getTriple().c_str());
+ ModuleSpec module_spec (executable_sp->GetFileSpec(), arch_spec);
+ Error error = ModuleList::GetSharedModule (module_spec,
+ executable_sp,
+ &GetExecutableSearchPaths(),
+ NULL,
+ NULL);
+
+ if (!error.Fail() && executable_sp)
+ {
+ SetExecutableModule (executable_sp, true);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void
+Target::WillClearList (const ModuleList& module_list)
+{
+}
+
+void
+Target::ModuleAdded (const ModuleList& module_list, const ModuleSP &module_sp)
+{
+ // A module is being added to this target for the first time
+ ModuleList my_module_list;
+ my_module_list.Append(module_sp);
+ LoadScriptingResourceForModule(module_sp, this);
+ ModulesDidLoad (my_module_list);
+}
+
+void
+Target::ModuleRemoved (const ModuleList& module_list, const ModuleSP &module_sp)
+{
+ // A module is being added to this target for the first time
+ ModuleList my_module_list;
+ my_module_list.Append(module_sp);
+ ModulesDidUnload (my_module_list);
+}
+
+void
+Target::ModuleUpdated (const ModuleList& module_list, const ModuleSP &old_module_sp, const ModuleSP &new_module_sp)
+{
+ // A module is replacing an already added module
+ m_breakpoint_list.UpdateBreakpointsWhenModuleIsReplaced(old_module_sp, new_module_sp);
+}
+
+void
+Target::ModulesDidLoad (ModuleList &module_list)
+{
+ if (module_list.GetSize())
+ {
+ m_breakpoint_list.UpdateBreakpoints (module_list, true);
+ // TODO: make event data that packages up the module_list
+ BroadcastEvent (eBroadcastBitModulesLoaded, NULL);
+ }
+}
+
+void
+Target::SymbolsDidLoad (ModuleList &module_list)
+{
+ if (module_list.GetSize())
+ {
+ if (m_process_sp)
+ {
+ LanguageRuntime* runtime = m_process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC);
+ if (runtime)
+ {
+ ObjCLanguageRuntime *objc_runtime = (ObjCLanguageRuntime*)runtime;
+ objc_runtime->SymbolsDidLoad(module_list);
+ }
+ }
+
+ m_breakpoint_list.UpdateBreakpoints (module_list, true);
+ BroadcastEvent(eBroadcastBitSymbolsLoaded, NULL);
+ }
+}
+
+void
+Target::ModulesDidUnload (ModuleList &module_list)
+{
+ if (module_list.GetSize())
+ {
+ m_breakpoint_list.UpdateBreakpoints (module_list, false);
+ // TODO: make event data that packages up the module_list
+ BroadcastEvent (eBroadcastBitModulesUnloaded, NULL);
+ }
+}
+
+bool
+Target::ModuleIsExcludedForNonModuleSpecificSearches (const FileSpec &module_file_spec)
+{
+ if (GetBreakpointsConsultPlatformAvoidList())
+ {
+ ModuleList matchingModules;
+ ModuleSpec module_spec (module_file_spec);
+ size_t num_modules = GetImages().FindModules(module_spec, matchingModules);
+
+ // If there is more than one module for this file spec, only return true if ALL the modules are on the
+ // black list.
+ if (num_modules > 0)
+ {
+ for (size_t i = 0; i < num_modules; i++)
+ {
+ if (!ModuleIsExcludedForNonModuleSpecificSearches (matchingModules.GetModuleAtIndex(i)))
+ return false;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Target::ModuleIsExcludedForNonModuleSpecificSearches (const lldb::ModuleSP &module_sp)
+{
+ if (GetBreakpointsConsultPlatformAvoidList())
+ {
+ if (m_platform_sp)
+ return m_platform_sp->ModuleIsExcludedForNonModuleSpecificSearches (*this, module_sp);
+ }
+ return false;
+}
+
+size_t
+Target::ReadMemoryFromFileCache (const Address& addr, void *dst, size_t dst_len, Error &error)
+{
+ SectionSP section_sp (addr.GetSection());
+ if (section_sp)
+ {
+ // If the contents of this section are encrypted, the on-disk file is unusuable. Read only from live memory.
+ if (section_sp->IsEncrypted())
+ {
+ error.SetErrorString("section is encrypted");
+ return 0;
+ }
+ ModuleSP module_sp (section_sp->GetModule());
+ if (module_sp)
+ {
+ ObjectFile *objfile = section_sp->GetModule()->GetObjectFile();
+ if (objfile)
+ {
+ size_t bytes_read = objfile->ReadSectionData (section_sp.get(),
+ addr.GetOffset(),
+ dst,
+ dst_len);
+ if (bytes_read > 0)
+ return bytes_read;
+ else
+ error.SetErrorStringWithFormat("error reading data from section %s", section_sp->GetName().GetCString());
+ }
+ else
+ error.SetErrorString("address isn't from a object file");
+ }
+ else
+ error.SetErrorString("address isn't in a module");
+ }
+ else
+ error.SetErrorString("address doesn't contain a section that points to a section in a object file");
+
+ return 0;
+}
+
+size_t
+Target::ReadMemory (const Address& addr,
+ bool prefer_file_cache,
+ void *dst,
+ size_t dst_len,
+ Error &error,
+ lldb::addr_t *load_addr_ptr)
+{
+ error.Clear();
+
+ // if we end up reading this from process memory, we will fill this
+ // with the actual load address
+ if (load_addr_ptr)
+ *load_addr_ptr = LLDB_INVALID_ADDRESS;
+
+ size_t bytes_read = 0;
+
+ addr_t load_addr = LLDB_INVALID_ADDRESS;
+ addr_t file_addr = LLDB_INVALID_ADDRESS;
+ Address resolved_addr;
+ if (!addr.IsSectionOffset())
+ {
+ if (m_section_load_list.IsEmpty())
+ {
+ // No sections are loaded, so we must assume we are not running
+ // yet and anything we are given is a file address.
+ file_addr = addr.GetOffset(); // "addr" doesn't have a section, so its offset is the file address
+ m_images.ResolveFileAddress (file_addr, resolved_addr);
+ }
+ else
+ {
+ // We have at least one section loaded. This can be becuase
+ // we have manually loaded some sections with "target modules load ..."
+ // or because we have have a live process that has sections loaded
+ // through the dynamic loader
+ load_addr = addr.GetOffset(); // "addr" doesn't have a section, so its offset is the load address
+ m_section_load_list.ResolveLoadAddress (load_addr, resolved_addr);
+ }
+ }
+ if (!resolved_addr.IsValid())
+ resolved_addr = addr;
+
+
+ if (prefer_file_cache)
+ {
+ bytes_read = ReadMemoryFromFileCache (resolved_addr, dst, dst_len, error);
+ if (bytes_read > 0)
+ return bytes_read;
+ }
+
+ if (ProcessIsValid())
+ {
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ load_addr = resolved_addr.GetLoadAddress (this);
+
+ if (load_addr == LLDB_INVALID_ADDRESS)
+ {
+ ModuleSP addr_module_sp (resolved_addr.GetModule());
+ if (addr_module_sp && addr_module_sp->GetFileSpec())
+ error.SetErrorStringWithFormat("%s[0x%" PRIx64 "] can't be resolved, %s in not currently loaded",
+ addr_module_sp->GetFileSpec().GetFilename().AsCString(),
+ resolved_addr.GetFileAddress(),
+ addr_module_sp->GetFileSpec().GetFilename().AsCString());
+ else
+ error.SetErrorStringWithFormat("0x%" PRIx64 " can't be resolved", resolved_addr.GetFileAddress());
+ }
+ else
+ {
+ bytes_read = m_process_sp->ReadMemory(load_addr, dst, dst_len, error);
+ if (bytes_read != dst_len)
+ {
+ if (error.Success())
+ {
+ if (bytes_read == 0)
+ error.SetErrorStringWithFormat("read memory from 0x%" PRIx64 " failed", load_addr);
+ else
+ error.SetErrorStringWithFormat("only %" PRIu64 " of %" PRIu64 " bytes were read from memory at 0x%" PRIx64, (uint64_t)bytes_read, (uint64_t)dst_len, load_addr);
+ }
+ }
+ if (bytes_read)
+ {
+ if (load_addr_ptr)
+ *load_addr_ptr = load_addr;
+ return bytes_read;
+ }
+ // If the address is not section offset we have an address that
+ // doesn't resolve to any address in any currently loaded shared
+ // libaries and we failed to read memory so there isn't anything
+ // more we can do. If it is section offset, we might be able to
+ // read cached memory from the object file.
+ if (!resolved_addr.IsSectionOffset())
+ return 0;
+ }
+ }
+
+ if (!prefer_file_cache && resolved_addr.IsSectionOffset())
+ {
+ // If we didn't already try and read from the object file cache, then
+ // try it after failing to read from the process.
+ return ReadMemoryFromFileCache (resolved_addr, dst, dst_len, error);
+ }
+ return 0;
+}
+
+size_t
+Target::ReadCStringFromMemory (const Address& addr, std::string &out_str, Error &error)
+{
+ char buf[256];
+ out_str.clear();
+ addr_t curr_addr = addr.GetLoadAddress(this);
+ Address address(addr);
+ while (1)
+ {
+ size_t length = ReadCStringFromMemory (address, buf, sizeof(buf), error);
+ if (length == 0)
+ break;
+ out_str.append(buf, length);
+ // If we got "length - 1" bytes, we didn't get the whole C string, we
+ // need to read some more characters
+ if (length == sizeof(buf) - 1)
+ curr_addr += length;
+ else
+ break;
+ address = Address(curr_addr);
+ }
+ return out_str.size();
+}
+
+
+size_t
+Target::ReadCStringFromMemory (const Address& addr, char *dst, size_t dst_max_len, Error &result_error)
+{
+ size_t total_cstr_len = 0;
+ if (dst && dst_max_len)
+ {
+ result_error.Clear();
+ // NULL out everything just to be safe
+ memset (dst, 0, dst_max_len);
+ Error error;
+ addr_t curr_addr = addr.GetLoadAddress(this);
+ Address address(addr);
+ const size_t cache_line_size = 512;
+ size_t bytes_left = dst_max_len - 1;
+ char *curr_dst = dst;
+
+ while (bytes_left > 0)
+ {
+ addr_t cache_line_bytes_left = cache_line_size - (curr_addr % cache_line_size);
+ addr_t bytes_to_read = std::min<addr_t>(bytes_left, cache_line_bytes_left);
+ size_t bytes_read = ReadMemory (address, false, curr_dst, bytes_to_read, error);
+
+ if (bytes_read == 0)
+ {
+ result_error = error;
+ dst[total_cstr_len] = '\0';
+ break;
+ }
+ const size_t len = strlen(curr_dst);
+
+ total_cstr_len += len;
+
+ if (len < bytes_to_read)
+ break;
+
+ curr_dst += bytes_read;
+ curr_addr += bytes_read;
+ bytes_left -= bytes_read;
+ address = Address(curr_addr);
+ }
+ }
+ else
+ {
+ if (dst == NULL)
+ result_error.SetErrorString("invalid arguments");
+ else
+ result_error.Clear();
+ }
+ return total_cstr_len;
+}
+
+size_t
+Target::ReadScalarIntegerFromMemory (const Address& addr,
+ bool prefer_file_cache,
+ uint32_t byte_size,
+ bool is_signed,
+ Scalar &scalar,
+ Error &error)
+{
+ uint64_t uval;
+
+ if (byte_size <= sizeof(uval))
+ {
+ size_t bytes_read = ReadMemory (addr, prefer_file_cache, &uval, byte_size, error);
+ if (bytes_read == byte_size)
+ {
+ DataExtractor data (&uval, sizeof(uval), m_arch.GetByteOrder(), m_arch.GetAddressByteSize());
+ lldb::offset_t offset = 0;
+ if (byte_size <= 4)
+ scalar = data.GetMaxU32 (&offset, byte_size);
+ else
+ scalar = data.GetMaxU64 (&offset, byte_size);
+
+ if (is_signed)
+ scalar.SignExtend(byte_size * 8);
+ return bytes_read;
+ }
+ }
+ else
+ {
+ error.SetErrorStringWithFormat ("byte size of %u is too large for integer scalar type", byte_size);
+ }
+ return 0;
+}
+
+uint64_t
+Target::ReadUnsignedIntegerFromMemory (const Address& addr,
+ bool prefer_file_cache,
+ size_t integer_byte_size,
+ uint64_t fail_value,
+ Error &error)
+{
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory (addr,
+ prefer_file_cache,
+ integer_byte_size,
+ false,
+ scalar,
+ error))
+ return scalar.ULongLong(fail_value);
+ return fail_value;
+}
+
+bool
+Target::ReadPointerFromMemory (const Address& addr,
+ bool prefer_file_cache,
+ Error &error,
+ Address &pointer_addr)
+{
+ Scalar scalar;
+ if (ReadScalarIntegerFromMemory (addr,
+ prefer_file_cache,
+ m_arch.GetAddressByteSize(),
+ false,
+ scalar,
+ error))
+ {
+ addr_t pointer_vm_addr = scalar.ULongLong(LLDB_INVALID_ADDRESS);
+ if (pointer_vm_addr != LLDB_INVALID_ADDRESS)
+ {
+ if (m_section_load_list.IsEmpty())
+ {
+ // No sections are loaded, so we must assume we are not running
+ // yet and anything we are given is a file address.
+ m_images.ResolveFileAddress (pointer_vm_addr, pointer_addr);
+ }
+ else
+ {
+ // We have at least one section loaded. This can be becuase
+ // we have manually loaded some sections with "target modules load ..."
+ // or because we have have a live process that has sections loaded
+ // through the dynamic loader
+ m_section_load_list.ResolveLoadAddress (pointer_vm_addr, pointer_addr);
+ }
+ // We weren't able to resolve the pointer value, so just return
+ // an address with no section
+ if (!pointer_addr.IsValid())
+ pointer_addr.SetOffset (pointer_vm_addr);
+ return true;
+
+ }
+ }
+ return false;
+}
+
+ModuleSP
+Target::GetSharedModule (const ModuleSpec &module_spec, Error *error_ptr)
+{
+ ModuleSP module_sp;
+
+ Error error;
+
+ // First see if we already have this module in our module list. If we do, then we're done, we don't need
+ // to consult the shared modules list. But only do this if we are passed a UUID.
+
+ if (module_spec.GetUUID().IsValid())
+ module_sp = m_images.FindFirstModule(module_spec);
+
+ if (!module_sp)
+ {
+ ModuleSP old_module_sp; // This will get filled in if we have a new version of the library
+ bool did_create_module = false;
+
+ // If there are image search path entries, try to use them first to acquire a suitable image.
+ if (m_image_search_paths.GetSize())
+ {
+ ModuleSpec transformed_spec (module_spec);
+ if (m_image_search_paths.RemapPath (module_spec.GetFileSpec().GetDirectory(), transformed_spec.GetFileSpec().GetDirectory()))
+ {
+ transformed_spec.GetFileSpec().GetFilename() = module_spec.GetFileSpec().GetFilename();
+ error = ModuleList::GetSharedModule (transformed_spec,
+ module_sp,
+ &GetExecutableSearchPaths(),
+ &old_module_sp,
+ &did_create_module);
+ }
+ }
+
+ if (!module_sp)
+ {
+ // If we have a UUID, we can check our global shared module list in case
+ // we already have it. If we don't have a valid UUID, then we can't since
+ // the path in "module_spec" will be a platform path, and we will need to
+ // let the platform find that file. For example, we could be asking for
+ // "/usr/lib/dyld" and if we do not have a UUID, we don't want to pick
+ // the local copy of "/usr/lib/dyld" since our platform could be a remote
+ // platform that has its own "/usr/lib/dyld" in an SDK or in a local file
+ // cache.
+ if (module_spec.GetUUID().IsValid())
+ {
+ // We have a UUID, it is OK to check the global module list...
+ error = ModuleList::GetSharedModule (module_spec,
+ module_sp,
+ &GetExecutableSearchPaths(),
+ &old_module_sp,
+ &did_create_module);
+ }
+
+ if (!module_sp)
+ {
+ // The platform is responsible for finding and caching an appropriate
+ // module in the shared module cache.
+ if (m_platform_sp)
+ {
+ FileSpec platform_file_spec;
+ error = m_platform_sp->GetSharedModule (module_spec,
+ module_sp,
+ &GetExecutableSearchPaths(),
+ &old_module_sp,
+ &did_create_module);
+ }
+ else
+ {
+ error.SetErrorString("no platform is currently set");
+ }
+ }
+ }
+
+ // We found a module that wasn't in our target list. Let's make sure that there wasn't an equivalent
+ // module in the list already, and if there was, let's remove it.
+ if (module_sp)
+ {
+ ObjectFile *objfile = module_sp->GetObjectFile();
+ if (objfile)
+ {
+ switch (objfile->GetType())
+ {
+ case ObjectFile::eTypeCoreFile: /// A core file that has a checkpoint of a program's execution state
+ case ObjectFile::eTypeExecutable: /// A normal executable
+ case ObjectFile::eTypeDynamicLinker: /// The platform's dynamic linker executable
+ case ObjectFile::eTypeObjectFile: /// An intermediate object file
+ case ObjectFile::eTypeSharedLibrary: /// A shared library that can be used during execution
+ break;
+ case ObjectFile::eTypeDebugInfo: /// An object file that contains only debug information
+ if (error_ptr)
+ error_ptr->SetErrorString("debug info files aren't valid target modules, please specify an executable");
+ return ModuleSP();
+ case ObjectFile::eTypeStubLibrary: /// A library that can be linked against but not used for execution
+ if (error_ptr)
+ error_ptr->SetErrorString("stub libraries aren't valid target modules, please specify an executable");
+ return ModuleSP();
+ default:
+ if (error_ptr)
+ error_ptr->SetErrorString("unsupported file type, please specify an executable");
+ return ModuleSP();
+ }
+ // GetSharedModule is not guaranteed to find the old shared module, for instance
+ // in the common case where you pass in the UUID, it is only going to find the one
+ // module matching the UUID. In fact, it has no good way to know what the "old module"
+ // relevant to this target is, since there might be many copies of a module with this file spec
+ // in various running debug sessions, but only one of them will belong to this target.
+ // So let's remove the UUID from the module list, and look in the target's module list.
+ // Only do this if there is SOMETHING else in the module spec...
+ if (!old_module_sp)
+ {
+ if (module_spec.GetUUID().IsValid() && !module_spec.GetFileSpec().GetFilename().IsEmpty() && !module_spec.GetFileSpec().GetDirectory().IsEmpty())
+ {
+ ModuleSpec module_spec_copy(module_spec.GetFileSpec());
+ module_spec_copy.GetUUID().Clear();
+
+ ModuleList found_modules;
+ size_t num_found = m_images.FindModules (module_spec_copy, found_modules);
+ if (num_found == 1)
+ {
+ old_module_sp = found_modules.GetModuleAtIndex(0);
+ }
+ }
+ }
+
+ if (old_module_sp && m_images.GetIndexForModule (old_module_sp.get()) != LLDB_INVALID_INDEX32)
+ {
+ m_images.ReplaceModule(old_module_sp, module_sp);
+ Module *old_module_ptr = old_module_sp.get();
+ old_module_sp.reset();
+ ModuleList::RemoveSharedModuleIfOrphaned (old_module_ptr);
+ }
+ else
+ m_images.Append(module_sp);
+ }
+ }
+ }
+ if (error_ptr)
+ *error_ptr = error;
+ return module_sp;
+}
+
+
+TargetSP
+Target::CalculateTarget ()
+{
+ return shared_from_this();
+}
+
+ProcessSP
+Target::CalculateProcess ()
+{
+ return ProcessSP();
+}
+
+ThreadSP
+Target::CalculateThread ()
+{
+ return ThreadSP();
+}
+
+StackFrameSP
+Target::CalculateStackFrame ()
+{
+ return StackFrameSP();
+}
+
+void
+Target::CalculateExecutionContext (ExecutionContext &exe_ctx)
+{
+ exe_ctx.Clear();
+ exe_ctx.SetTargetPtr(this);
+}
+
+PathMappingList &
+Target::GetImageSearchPathList ()
+{
+ return m_image_search_paths;
+}
+
+void
+Target::ImageSearchPathsChanged
+(
+ const PathMappingList &path_list,
+ void *baton
+)
+{
+ Target *target = (Target *)baton;
+ ModuleSP exe_module_sp (target->GetExecutableModule());
+ if (exe_module_sp)
+ {
+ target->m_images.Clear();
+ target->SetExecutableModule (exe_module_sp, true);
+ }
+}
+
+ClangASTContext *
+Target::GetScratchClangASTContext(bool create_on_demand)
+{
+ // Now see if we know the target triple, and if so, create our scratch AST context:
+ if (m_scratch_ast_context_ap.get() == NULL && m_arch.IsValid() && create_on_demand)
+ {
+ m_scratch_ast_context_ap.reset (new ClangASTContext(m_arch.GetTriple().str().c_str()));
+ m_scratch_ast_source_ap.reset (new ClangASTSource(shared_from_this()));
+ m_scratch_ast_source_ap->InstallASTContext(m_scratch_ast_context_ap->getASTContext());
+ llvm::OwningPtr<clang::ExternalASTSource> proxy_ast_source(m_scratch_ast_source_ap->CreateProxy());
+ m_scratch_ast_context_ap->SetExternalSource(proxy_ast_source);
+ }
+ return m_scratch_ast_context_ap.get();
+}
+
+ClangASTImporter *
+Target::GetClangASTImporter()
+{
+ ClangASTImporter *ast_importer = m_ast_importer_ap.get();
+
+ if (!ast_importer)
+ {
+ ast_importer = new ClangASTImporter();
+ m_ast_importer_ap.reset(ast_importer);
+ }
+
+ return ast_importer;
+}
+
+void
+Target::SettingsInitialize ()
+{
+ Process::SettingsInitialize ();
+}
+
+void
+Target::SettingsTerminate ()
+{
+ Process::SettingsTerminate ();
+}
+
+FileSpecList
+Target::GetDefaultExecutableSearchPaths ()
+{
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetExecutableSearchPaths();
+ return FileSpecList();
+}
+
+FileSpecList
+Target::GetDefaultDebugFileSearchPaths ()
+{
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetDebugFileSearchPaths();
+ return FileSpecList();
+}
+
+ArchSpec
+Target::GetDefaultArchitecture ()
+{
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ return properties_sp->GetDefaultArchitecture();
+ return ArchSpec();
+}
+
+void
+Target::SetDefaultArchitecture (const ArchSpec &arch)
+{
+ TargetPropertiesSP properties_sp(Target::GetGlobalProperties());
+ if (properties_sp)
+ {
+ LogIfAnyCategoriesSet(LIBLLDB_LOG_TARGET, "Target::SetDefaultArchitecture setting target's default architecture to %s (%s)", arch.GetArchitectureName(), arch.GetTriple().getTriple().c_str());
+ return properties_sp->SetDefaultArchitecture(arch);
+ }
+}
+
+Target *
+Target::GetTargetFromContexts (const ExecutionContext *exe_ctx_ptr, const SymbolContext *sc_ptr)
+{
+ // The target can either exist in the "process" of ExecutionContext, or in
+ // the "target_sp" member of SymbolContext. This accessor helper function
+ // will get the target from one of these locations.
+
+ Target *target = NULL;
+ if (sc_ptr != NULL)
+ target = sc_ptr->target_sp.get();
+ if (target == NULL && exe_ctx_ptr)
+ target = exe_ctx_ptr->GetTargetPtr();
+ return target;
+}
+
+ExecutionResults
+Target::EvaluateExpression
+(
+ const char *expr_cstr,
+ StackFrame *frame,
+ lldb::ValueObjectSP &result_valobj_sp,
+ const EvaluateExpressionOptions& options
+)
+{
+ result_valobj_sp.reset();
+
+ ExecutionResults execution_results = eExecutionSetupError;
+
+ if (expr_cstr == NULL || expr_cstr[0] == '\0')
+ return execution_results;
+
+ // We shouldn't run stop hooks in expressions.
+ // Be sure to reset this if you return anywhere within this function.
+ bool old_suppress_value = m_suppress_stop_hooks;
+ m_suppress_stop_hooks = true;
+
+ ExecutionContext exe_ctx;
+
+ if (frame)
+ {
+ frame->CalculateExecutionContext(exe_ctx);
+ }
+ else if (m_process_sp)
+ {
+ m_process_sp->CalculateExecutionContext(exe_ctx);
+ }
+ else
+ {
+ CalculateExecutionContext(exe_ctx);
+ }
+
+ // Make sure we aren't just trying to see the value of a persistent
+ // variable (something like "$0")
+ lldb::ClangExpressionVariableSP persistent_var_sp;
+ // Only check for persistent variables the expression starts with a '$'
+ if (expr_cstr[0] == '$')
+ persistent_var_sp = m_persistent_variables.GetVariable (expr_cstr);
+
+ if (persistent_var_sp)
+ {
+ result_valobj_sp = persistent_var_sp->GetValueObject ();
+ execution_results = eExecutionCompleted;
+ }
+ else
+ {
+ const char *prefix = GetExpressionPrefixContentsAsCString();
+
+ execution_results = ClangUserExpression::Evaluate (exe_ctx,
+ options.GetExecutionPolicy(),
+ lldb::eLanguageTypeUnknown,
+ options.DoesCoerceToId() ? ClangUserExpression::eResultTypeId : ClangUserExpression::eResultTypeAny,
+ options.DoesUnwindOnError(),
+ options.DoesIgnoreBreakpoints(),
+ expr_cstr,
+ prefix,
+ result_valobj_sp,
+ options.GetRunOthers(),
+ options.GetTimeoutUsec());
+ }
+
+ m_suppress_stop_hooks = old_suppress_value;
+
+ return execution_results;
+}
+
+lldb::addr_t
+Target::GetCallableLoadAddress (lldb::addr_t load_addr, AddressClass addr_class) const
+{
+ addr_t code_addr = load_addr;
+ switch (m_arch.GetMachine())
+ {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (addr_class)
+ {
+ case eAddressClassData:
+ case eAddressClassDebug:
+ return LLDB_INVALID_ADDRESS;
+
+ case eAddressClassUnknown:
+ case eAddressClassInvalid:
+ case eAddressClassCode:
+ case eAddressClassCodeAlternateISA:
+ case eAddressClassRuntime:
+ // Check if bit zero it no set?
+ if ((code_addr & 1ull) == 0)
+ {
+ // Bit zero isn't set, check if the address is a multiple of 2?
+ if (code_addr & 2ull)
+ {
+ // The address is a multiple of 2 so it must be thumb, set bit zero
+ code_addr |= 1ull;
+ }
+ else if (addr_class == eAddressClassCodeAlternateISA)
+ {
+ // We checked the address and the address claims to be the alternate ISA
+ // which means thumb, so set bit zero.
+ code_addr |= 1ull;
+ }
+ }
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return code_addr;
+}
+
+lldb::addr_t
+Target::GetOpcodeLoadAddress (lldb::addr_t load_addr, AddressClass addr_class) const
+{
+ addr_t opcode_addr = load_addr;
+ switch (m_arch.GetMachine())
+ {
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ switch (addr_class)
+ {
+ case eAddressClassData:
+ case eAddressClassDebug:
+ return LLDB_INVALID_ADDRESS;
+
+ case eAddressClassInvalid:
+ case eAddressClassUnknown:
+ case eAddressClassCode:
+ case eAddressClassCodeAlternateISA:
+ case eAddressClassRuntime:
+ opcode_addr &= ~(1ull);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return opcode_addr;
+}
+
+SourceManager &
+Target::GetSourceManager ()
+{
+ if (m_source_manager_ap.get() == NULL)
+ m_source_manager_ap.reset (new SourceManager(shared_from_this()));
+ return *m_source_manager_ap;
+}
+
+
+lldb::user_id_t
+Target::AddStopHook (Target::StopHookSP &new_hook_sp)
+{
+ lldb::user_id_t new_uid = ++m_stop_hook_next_id;
+ new_hook_sp.reset (new StopHook(shared_from_this(), new_uid));
+ m_stop_hooks[new_uid] = new_hook_sp;
+ return new_uid;
+}
+
+bool
+Target::RemoveStopHookByID (lldb::user_id_t user_id)
+{
+ size_t num_removed;
+ num_removed = m_stop_hooks.erase (user_id);
+ if (num_removed == 0)
+ return false;
+ else
+ return true;
+}
+
+void
+Target::RemoveAllStopHooks ()
+{
+ m_stop_hooks.clear();
+}
+
+Target::StopHookSP
+Target::GetStopHookByID (lldb::user_id_t user_id)
+{
+ StopHookSP found_hook;
+
+ StopHookCollection::iterator specified_hook_iter;
+ specified_hook_iter = m_stop_hooks.find (user_id);
+ if (specified_hook_iter != m_stop_hooks.end())
+ found_hook = (*specified_hook_iter).second;
+ return found_hook;
+}
+
+bool
+Target::SetStopHookActiveStateByID (lldb::user_id_t user_id, bool active_state)
+{
+ StopHookCollection::iterator specified_hook_iter;
+ specified_hook_iter = m_stop_hooks.find (user_id);
+ if (specified_hook_iter == m_stop_hooks.end())
+ return false;
+
+ (*specified_hook_iter).second->SetIsActive (active_state);
+ return true;
+}
+
+void
+Target::SetAllStopHooksActiveState (bool active_state)
+{
+ StopHookCollection::iterator pos, end = m_stop_hooks.end();
+ for (pos = m_stop_hooks.begin(); pos != end; pos++)
+ {
+ (*pos).second->SetIsActive (active_state);
+ }
+}
+
+void
+Target::RunStopHooks ()
+{
+ if (m_suppress_stop_hooks)
+ return;
+
+ if (!m_process_sp)
+ return;
+
+ // <rdar://problem/12027563> make sure we check that we are not stopped because of us running a user expression
+ // since in that case we do not want to run the stop-hooks
+ if (m_process_sp->GetModIDRef().IsLastResumeForUserExpression())
+ return;
+
+ if (m_stop_hooks.empty())
+ return;
+
+ StopHookCollection::iterator pos, end = m_stop_hooks.end();
+
+ // If there aren't any active stop hooks, don't bother either:
+ bool any_active_hooks = false;
+ for (pos = m_stop_hooks.begin(); pos != end; pos++)
+ {
+ if ((*pos).second->IsActive())
+ {
+ any_active_hooks = true;
+ break;
+ }
+ }
+ if (!any_active_hooks)
+ return;
+
+ CommandReturnObject result;
+
+ std::vector<ExecutionContext> exc_ctx_with_reasons;
+ std::vector<SymbolContext> sym_ctx_with_reasons;
+
+ ThreadList &cur_threadlist = m_process_sp->GetThreadList();
+ size_t num_threads = cur_threadlist.GetSize();
+ for (size_t i = 0; i < num_threads; i++)
+ {
+ lldb::ThreadSP cur_thread_sp = cur_threadlist.GetThreadAtIndex (i);
+ if (cur_thread_sp->ThreadStoppedForAReason())
+ {
+ lldb::StackFrameSP cur_frame_sp = cur_thread_sp->GetStackFrameAtIndex(0);
+ exc_ctx_with_reasons.push_back(ExecutionContext(m_process_sp.get(), cur_thread_sp.get(), cur_frame_sp.get()));
+ sym_ctx_with_reasons.push_back(cur_frame_sp->GetSymbolContext(eSymbolContextEverything));
+ }
+ }
+
+ // If no threads stopped for a reason, don't run the stop-hooks.
+ size_t num_exe_ctx = exc_ctx_with_reasons.size();
+ if (num_exe_ctx == 0)
+ return;
+
+ result.SetImmediateOutputStream (m_debugger.GetAsyncOutputStream());
+ result.SetImmediateErrorStream (m_debugger.GetAsyncErrorStream());
+
+ bool keep_going = true;
+ bool hooks_ran = false;
+ bool print_hook_header;
+ bool print_thread_header;
+
+ if (num_exe_ctx == 1)
+ print_thread_header = false;
+ else
+ print_thread_header = true;
+
+ if (m_stop_hooks.size() == 1)
+ print_hook_header = false;
+ else
+ print_hook_header = true;
+
+ for (pos = m_stop_hooks.begin(); keep_going && pos != end; pos++)
+ {
+ // result.Clear();
+ StopHookSP cur_hook_sp = (*pos).second;
+ if (!cur_hook_sp->IsActive())
+ continue;
+
+ bool any_thread_matched = false;
+ for (size_t i = 0; keep_going && i < num_exe_ctx; i++)
+ {
+ if ((cur_hook_sp->GetSpecifier () == NULL
+ || cur_hook_sp->GetSpecifier()->SymbolContextMatches(sym_ctx_with_reasons[i]))
+ && (cur_hook_sp->GetThreadSpecifier() == NULL
+ || cur_hook_sp->GetThreadSpecifier()->ThreadPassesBasicTests(exc_ctx_with_reasons[i].GetThreadRef())))
+ {
+ if (!hooks_ran)
+ {
+ hooks_ran = true;
+ }
+ if (print_hook_header && !any_thread_matched)
+ {
+ const char *cmd = (cur_hook_sp->GetCommands().GetSize() == 1 ?
+ cur_hook_sp->GetCommands().GetStringAtIndex(0) :
+ NULL);
+ if (cmd)
+ result.AppendMessageWithFormat("\n- Hook %" PRIu64 " (%s)\n", cur_hook_sp->GetID(), cmd);
+ else
+ result.AppendMessageWithFormat("\n- Hook %" PRIu64 "\n", cur_hook_sp->GetID());
+ any_thread_matched = true;
+ }
+
+ if (print_thread_header)
+ result.AppendMessageWithFormat("-- Thread %d\n", exc_ctx_with_reasons[i].GetThreadPtr()->GetIndexID());
+
+ bool stop_on_continue = true;
+ bool stop_on_error = true;
+ bool echo_commands = false;
+ bool print_results = true;
+ GetDebugger().GetCommandInterpreter().HandleCommands (cur_hook_sp->GetCommands(),
+ &exc_ctx_with_reasons[i],
+ stop_on_continue,
+ stop_on_error,
+ echo_commands,
+ print_results,
+ eLazyBoolNo,
+ result);
+
+ // If the command started the target going again, we should bag out of
+ // running the stop hooks.
+ if ((result.GetStatus() == eReturnStatusSuccessContinuingNoResult) ||
+ (result.GetStatus() == eReturnStatusSuccessContinuingResult))
+ {
+ result.AppendMessageWithFormat ("Aborting stop hooks, hook %" PRIu64 " set the program running.", cur_hook_sp->GetID());
+ keep_going = false;
+ }
+ }
+ }
+ }
+
+ result.GetImmediateOutputStream()->Flush();
+ result.GetImmediateErrorStream()->Flush();
+}
+
+
+//--------------------------------------------------------------
+// class Target::StopHook
+//--------------------------------------------------------------
+
+
+Target::StopHook::StopHook (lldb::TargetSP target_sp, lldb::user_id_t uid) :
+ UserID (uid),
+ m_target_sp (target_sp),
+ m_commands (),
+ m_specifier_sp (),
+ m_thread_spec_ap(),
+ m_active (true)
+{
+}
+
+Target::StopHook::StopHook (const StopHook &rhs) :
+ UserID (rhs.GetID()),
+ m_target_sp (rhs.m_target_sp),
+ m_commands (rhs.m_commands),
+ m_specifier_sp (rhs.m_specifier_sp),
+ m_thread_spec_ap (),
+ m_active (rhs.m_active)
+{
+ if (rhs.m_thread_spec_ap.get() != NULL)
+ m_thread_spec_ap.reset (new ThreadSpec(*rhs.m_thread_spec_ap.get()));
+}
+
+
+Target::StopHook::~StopHook ()
+{
+}
+
+void
+Target::StopHook::SetThreadSpecifier (ThreadSpec *specifier)
+{
+ m_thread_spec_ap.reset (specifier);
+}
+
+
+void
+Target::StopHook::GetDescription (Stream *s, lldb::DescriptionLevel level) const
+{
+ int indent_level = s->GetIndentLevel();
+
+ s->SetIndentLevel(indent_level + 2);
+
+ s->Printf ("Hook: %" PRIu64 "\n", GetID());
+ if (m_active)
+ s->Indent ("State: enabled\n");
+ else
+ s->Indent ("State: disabled\n");
+
+ if (m_specifier_sp)
+ {
+ s->Indent();
+ s->PutCString ("Specifier:\n");
+ s->SetIndentLevel (indent_level + 4);
+ m_specifier_sp->GetDescription (s, level);
+ s->SetIndentLevel (indent_level + 2);
+ }
+
+ if (m_thread_spec_ap.get() != NULL)
+ {
+ StreamString tmp;
+ s->Indent("Thread:\n");
+ m_thread_spec_ap->GetDescription (&tmp, level);
+ s->SetIndentLevel (indent_level + 4);
+ s->Indent (tmp.GetData());
+ s->PutCString ("\n");
+ s->SetIndentLevel (indent_level + 2);
+ }
+
+ s->Indent ("Commands: \n");
+ s->SetIndentLevel (indent_level + 4);
+ uint32_t num_commands = m_commands.GetSize();
+ for (uint32_t i = 0; i < num_commands; i++)
+ {
+ s->Indent(m_commands.GetStringAtIndex(i));
+ s->PutCString ("\n");
+ }
+ s->SetIndentLevel (indent_level);
+}
+
+//--------------------------------------------------------------
+// class TargetProperties
+//--------------------------------------------------------------
+
+OptionEnumValueElement
+lldb_private::g_dynamic_value_types[] =
+{
+ { eNoDynamicValues, "no-dynamic-values", "Don't calculate the dynamic type of values"},
+ { eDynamicCanRunTarget, "run-target", "Calculate the dynamic type of values even if you have to run the target."},
+ { eDynamicDontRunTarget, "no-run-target", "Calculate the dynamic type of values, but don't run the target."},
+ { 0, NULL, NULL }
+};
+
+static OptionEnumValueElement
+g_inline_breakpoint_enums[] =
+{
+ { eInlineBreakpointsNever, "never", "Never look for inline breakpoint locations (fastest). This setting should only be used if you know that no inlining occurs in your programs."},
+ { eInlineBreakpointsHeaders, "headers", "Only check for inline breakpoint locations when setting breakpoints in header files, but not when setting breakpoint in implementation source files (default)."},
+ { eInlineBreakpointsAlways, "always", "Always look for inline breakpoint locations when setting file and line breakpoints (slower but most accurate)."},
+ { 0, NULL, NULL }
+};
+
+typedef enum x86DisassemblyFlavor
+{
+ eX86DisFlavorDefault,
+ eX86DisFlavorIntel,
+ eX86DisFlavorATT
+} x86DisassemblyFlavor;
+
+static OptionEnumValueElement
+g_x86_dis_flavor_value_types[] =
+{
+ { eX86DisFlavorDefault, "default", "Disassembler default (currently att)."},
+ { eX86DisFlavorIntel, "intel", "Intel disassembler flavor."},
+ { eX86DisFlavorATT, "att", "AT&T disassembler flavor."},
+ { 0, NULL, NULL }
+};
+
+static OptionEnumValueElement
+g_hex_immediate_style_values[] =
+{
+ { Disassembler::eHexStyleC, "c", "C-style (0xffff)."},
+ { Disassembler::eHexStyleAsm, "asm", "Asm-style (0ffffh)."},
+ { 0, NULL, NULL }
+};
+
+static OptionEnumValueElement
+g_load_script_from_sym_file_values[] =
+{
+ { eLoadScriptFromSymFileTrue, "true", "Load debug scripts inside symbol files"},
+ { eLoadScriptFromSymFileFalse, "false", "Do not load debug scripts inside symbol files."},
+ { eLoadScriptFromSymFileWarn, "warn", "Warn about debug scripts inside symbol files but do not load them."},
+ { 0, NULL, NULL }
+};
+
+
+static OptionEnumValueElement
+g_memory_module_load_level_values[] =
+{
+ { eMemoryModuleLoadLevelMinimal, "minimal" , "Load minimal information when loading modules from memory. Currently this setting loads sections only."},
+ { eMemoryModuleLoadLevelPartial, "partial" , "Load partial information when loading modules from memory. Currently this setting loads sections and function bounds."},
+ { eMemoryModuleLoadLevelComplete, "complete", "Load complete information when loading modules from memory. Currently this setting loads sections and all symbols."},
+ { 0, NULL, NULL }
+};
+
+static PropertyDefinition
+g_properties[] =
+{
+ { "default-arch" , OptionValue::eTypeArch , true , 0 , NULL, NULL, "Default architecture to choose, when there's a choice." },
+ { "expr-prefix" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "Path to a file containing expressions to be prepended to all expressions." },
+ { "prefer-dynamic-value" , OptionValue::eTypeEnum , false, eNoDynamicValues , NULL, g_dynamic_value_types, "Should printed values be shown as their dynamic value." },
+ { "enable-synthetic-value" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Should synthetic values be used by default whenever available." },
+ { "skip-prologue" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Skip function prologues when setting breakpoints by name." },
+ { "source-map" , OptionValue::eTypePathMap , false, 0 , NULL, NULL, "Source path remappings used to track the change of location between a source file when built, and "
+ "where it exists on the current system. It consists of an array of duples, the first element of each duple is "
+ "some part (starting at the root) of the path to the file when it was built, "
+ "and the second is where the remainder of the original build hierarchy is rooted on the local system. "
+ "Each element of the array is checked in order and the first one that results in a match wins." },
+ { "exec-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , NULL, NULL, "Executable search paths to use when locating executable files whose paths don't match the local file system." },
+ { "debug-file-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , NULL, NULL, "List of directories to be searched when locating debug symbol files." },
+ { "max-children-count" , OptionValue::eTypeSInt64 , false, 256 , NULL, NULL, "Maximum number of children to expand in any level of depth." },
+ { "max-string-summary-length" , OptionValue::eTypeSInt64 , false, 1024 , NULL, NULL, "Maximum number of characters to show when using %s in summary strings." },
+ { "max-memory-read-size" , OptionValue::eTypeSInt64 , false, 1024 , NULL, NULL, "Maximum number of bytes that 'memory read' will fetch before --force must be specified." },
+ { "breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean , false, true , NULL, NULL, "Consult the platform module avoid list when setting non-module specific breakpoints." },
+ { "arg0" , OptionValue::eTypeString , false, 0 , NULL, NULL, "The first argument passed to the program in the argument array which can be different from the executable itself." },
+ { "run-args" , OptionValue::eTypeArgs , false, 0 , NULL, NULL, "A list containing all the arguments to be passed to the executable when it is run. Note that this does NOT include the argv[0] which is in target.arg0." },
+ { "env-vars" , OptionValue::eTypeDictionary, false, OptionValue::eTypeString , NULL, NULL, "A list of all the environment variables to be passed to the executable's environment, and their values." },
+ { "inherit-env" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Inherit the environment from the process that is running LLDB." },
+ { "input-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for reading its standard input." },
+ { "output-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard output." },
+ { "error-path" , OptionValue::eTypeFileSpec , false, 0 , NULL, NULL, "The file/path to be used by the executable program for writing its standard error." },
+ { "disable-aslr" , OptionValue::eTypeBoolean , false, true , NULL, NULL, "Disable Address Space Layout Randomization (ASLR)" },
+ { "disable-stdio" , OptionValue::eTypeBoolean , false, false , NULL, NULL, "Disable stdin/stdout for process (e.g. for a GUI application)" },
+ { "inline-breakpoint-strategy" , OptionValue::eTypeEnum , false, eInlineBreakpointsHeaders , NULL, g_inline_breakpoint_enums, "The strategy to use when settings breakpoints by file and line. "
+ "Breakpoint locations can end up being inlined by the compiler, so that a compile unit 'a.c' might contain an inlined function from another source file. "
+ "Usually this is limitted to breakpoint locations from inlined functions from header or other include files, or more accurately non-implementation source files. "
+ "Sometimes code might #include implementation files and cause inlined breakpoint locations in inlined implementation files. "
+ "Always checking for inlined breakpoint locations can be expensive (memory and time), so we try to minimize the "
+ "times we look for inlined locations. This setting allows you to control exactly which strategy is used when settings "
+ "file and line breakpoints." },
+ // FIXME: This is the wrong way to do per-architecture settings, but we don't have a general per architecture settings system in place yet.
+ { "x86-disassembly-flavor" , OptionValue::eTypeEnum , false, eX86DisFlavorDefault, NULL, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets." },
+ { "use-hex-immediates" , OptionValue::eTypeBoolean , false, true, NULL, NULL, "Show immediates in disassembly as hexadecimal." },
+ { "hex-immediate-style" , OptionValue::eTypeEnum , false, Disassembler::eHexStyleC, NULL, g_hex_immediate_style_values, "Which style to use for printing hexadecimal disassembly values." },
+ { "use-fast-stepping" , OptionValue::eTypeBoolean , false, true, NULL, NULL, "Use a fast stepping algorithm based on running from branch to branch rather than instruction single-stepping." },
+ { "load-script-from-symbol-file" , OptionValue::eTypeEnum , false, eLoadScriptFromSymFileWarn, NULL, g_load_script_from_sym_file_values, "Allow LLDB to load scripting resources embedded in symbol files when available." },
+ { "memory-module-load-level" , OptionValue::eTypeEnum , false, eMemoryModuleLoadLevelComplete, NULL, g_memory_module_load_level_values,
+ "Loading modules from memory can be slow as reading the symbol tables and other data can take a long time depending on your connection to the debug target. "
+ "This setting helps users control how much information gets loaded when loading modules from memory."
+ "'complete' is the default value for this setting which will load all sections and symbols by reading them from memory (slowest, most accurate). "
+ "'partial' will load sections and attempt to find function bounds without downloading the symbol table (faster, still accurate, missing symbol names). "
+ "'minimal' is the fastest setting and will load section data with no symbols, but should rarely be used as stack frames in these memory regions will be inaccurate and not provide any context (fastest). " },
+ { NULL , OptionValue::eTypeInvalid , false, 0 , NULL, NULL, NULL }
+};
+enum
+{
+ ePropertyDefaultArch,
+ ePropertyExprPrefix,
+ ePropertyPreferDynamic,
+ ePropertyEnableSynthetic,
+ ePropertySkipPrologue,
+ ePropertySourceMap,
+ ePropertyExecutableSearchPaths,
+ ePropertyDebugFileSearchPaths,
+ ePropertyMaxChildrenCount,
+ ePropertyMaxSummaryLength,
+ ePropertyMaxMemReadSize,
+ ePropertyBreakpointUseAvoidList,
+ ePropertyArg0,
+ ePropertyRunArgs,
+ ePropertyEnvVars,
+ ePropertyInheritEnv,
+ ePropertyInputPath,
+ ePropertyOutputPath,
+ ePropertyErrorPath,
+ ePropertyDisableASLR,
+ ePropertyDisableSTDIO,
+ ePropertyInlineStrategy,
+ ePropertyDisassemblyFlavor,
+ ePropertyUseHexImmediates,
+ ePropertyHexImmediateStyle,
+ ePropertyUseFastStepping,
+ ePropertyLoadScriptFromSymbolFile,
+ ePropertyMemoryModuleLoadLevel
+};
+
+
+class TargetOptionValueProperties : public OptionValueProperties
+{
+public:
+ TargetOptionValueProperties (const ConstString &name) :
+ OptionValueProperties (name),
+ m_target (NULL),
+ m_got_host_env (false)
+ {
+ }
+
+ // This constructor is used when creating TargetOptionValueProperties when it
+ // is part of a new lldb_private::Target instance. It will copy all current
+ // global property values as needed
+ TargetOptionValueProperties (Target *target, const TargetPropertiesSP &target_properties_sp) :
+ OptionValueProperties(*target_properties_sp->GetValueProperties()),
+ m_target (target),
+ m_got_host_env (false)
+ {
+ }
+
+ virtual const Property *
+ GetPropertyAtIndex (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const
+ {
+ // When gettings the value for a key from the target options, we will always
+ // try and grab the setting from the current target if there is one. Else we just
+ // use the one from this instance.
+ if (idx == ePropertyEnvVars)
+ GetHostEnvironmentIfNeeded ();
+
+ if (exe_ctx)
+ {
+ Target *target = exe_ctx->GetTargetPtr();
+ if (target)
+ {
+ TargetOptionValueProperties *target_properties = static_cast<TargetOptionValueProperties *>(target->GetValueProperties().get());
+ if (this != target_properties)
+ return target_properties->ProtectedGetPropertyAtIndex (idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex (idx);
+ }
+
+ lldb::TargetSP
+ GetTargetSP ()
+ {
+ return m_target->shared_from_this();
+ }
+
+protected:
+
+ void
+ GetHostEnvironmentIfNeeded () const
+ {
+ if (!m_got_host_env)
+ {
+ if (m_target)
+ {
+ m_got_host_env = true;
+ const uint32_t idx = ePropertyInheritEnv;
+ if (GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0))
+ {
+ PlatformSP platform_sp (m_target->GetPlatform());
+ if (platform_sp)
+ {
+ StringList env;
+ if (platform_sp->GetEnvironment(env))
+ {
+ OptionValueDictionary *env_dict = GetPropertyAtIndexAsOptionValueDictionary (NULL, ePropertyEnvVars);
+ if (env_dict)
+ {
+ const bool can_replace = false;
+ const size_t envc = env.GetSize();
+ for (size_t idx=0; idx<envc; idx++)
+ {
+ const char *env_entry = env.GetStringAtIndex (idx);
+ if (env_entry)
+ {
+ const char *equal_pos = ::strchr(env_entry, '=');
+ ConstString key;
+ // It is ok to have environment variables with no values
+ const char *value = NULL;
+ if (equal_pos)
+ {
+ key.SetCStringWithLength(env_entry, equal_pos - env_entry);
+ if (equal_pos[1])
+ value = equal_pos + 1;
+ }
+ else
+ {
+ key.SetCString(env_entry);
+ }
+ // Don't allow existing keys to be replaced with ones we get from the platform environment
+ env_dict->SetValueForKey(key, OptionValueSP(new OptionValueString(value)), can_replace);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ Target *m_target;
+ mutable bool m_got_host_env;
+};
+
+TargetProperties::TargetProperties (Target *target) :
+ Properties ()
+{
+ if (target)
+ {
+ m_collection_sp.reset (new TargetOptionValueProperties(target, Target::GetGlobalProperties()));
+ }
+ else
+ {
+ m_collection_sp.reset (new TargetOptionValueProperties(ConstString("target")));
+ m_collection_sp->Initialize(g_properties);
+ m_collection_sp->AppendProperty(ConstString("process"),
+ ConstString("Settings specify to processes."),
+ true,
+ Process::GetGlobalProperties()->GetValueProperties());
+ }
+}
+
+TargetProperties::~TargetProperties ()
+{
+}
+ArchSpec
+TargetProperties::GetDefaultArchitecture () const
+{
+ OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch (NULL, ePropertyDefaultArch);
+ if (value)
+ return value->GetCurrentValue();
+ return ArchSpec();
+}
+
+void
+TargetProperties::SetDefaultArchitecture (const ArchSpec& arch)
+{
+ OptionValueArch *value = m_collection_sp->GetPropertyAtIndexAsOptionValueArch (NULL, ePropertyDefaultArch);
+ if (value)
+ return value->SetCurrentValue(arch, true);
+}
+
+lldb::DynamicValueType
+TargetProperties::GetPreferDynamicValue() const
+{
+ const uint32_t idx = ePropertyPreferDynamic;
+ return (lldb::DynamicValueType)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value);
+}
+
+bool
+TargetProperties::GetDisableASLR () const
+{
+ const uint32_t idx = ePropertyDisableASLR;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+TargetProperties::SetDisableASLR (bool b)
+{
+ const uint32_t idx = ePropertyDisableASLR;
+ m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
+}
+
+bool
+TargetProperties::GetDisableSTDIO () const
+{
+ const uint32_t idx = ePropertyDisableSTDIO;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+void
+TargetProperties::SetDisableSTDIO (bool b)
+{
+ const uint32_t idx = ePropertyDisableSTDIO;
+ m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b);
+}
+
+const char *
+TargetProperties::GetDisassemblyFlavor () const
+{
+ const uint32_t idx = ePropertyDisassemblyFlavor;
+ const char *return_value;
+
+ x86DisassemblyFlavor flavor_value = (x86DisassemblyFlavor) m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value);
+ return_value = g_x86_dis_flavor_value_types[flavor_value].string_value;
+ return return_value;
+}
+
+InlineStrategy
+TargetProperties::GetInlineStrategy () const
+{
+ const uint32_t idx = ePropertyInlineStrategy;
+ return (InlineStrategy)m_collection_sp->GetPropertyAtIndexAsEnumeration (NULL, idx, g_properties[idx].default_uint_value);
+}
+
+const char *
+TargetProperties::GetArg0 () const
+{
+ const uint32_t idx = ePropertyArg0;
+ return m_collection_sp->GetPropertyAtIndexAsString (NULL, idx, NULL);
+}
+
+void
+TargetProperties::SetArg0 (const char *arg)
+{
+ const uint32_t idx = ePropertyArg0;
+ m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, arg);
+}
+
+bool
+TargetProperties::GetRunArguments (Args &args) const
+{
+ const uint32_t idx = ePropertyRunArgs;
+ return m_collection_sp->GetPropertyAtIndexAsArgs (NULL, idx, args);
+}
+
+void
+TargetProperties::SetRunArguments (const Args &args)
+{
+ const uint32_t idx = ePropertyRunArgs;
+ m_collection_sp->SetPropertyAtIndexFromArgs (NULL, idx, args);
+}
+
+size_t
+TargetProperties::GetEnvironmentAsArgs (Args &env) const
+{
+ const uint32_t idx = ePropertyEnvVars;
+ return m_collection_sp->GetPropertyAtIndexAsArgs (NULL, idx, env);
+}
+
+bool
+TargetProperties::GetSkipPrologue() const
+{
+ const uint32_t idx = ePropertySkipPrologue;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+PathMappingList &
+TargetProperties::GetSourcePathMap () const
+{
+ const uint32_t idx = ePropertySourceMap;
+ OptionValuePathMappings *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValuePathMappings (NULL, false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+FileSpecList &
+TargetProperties::GetExecutableSearchPaths ()
+{
+ const uint32_t idx = ePropertyExecutableSearchPaths;
+ OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList (NULL, false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+FileSpecList &
+TargetProperties::GetDebugFileSearchPaths ()
+{
+ const uint32_t idx = ePropertyDebugFileSearchPaths;
+ OptionValueFileSpecList *option_value = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpecList (NULL, false, idx);
+ assert(option_value);
+ return option_value->GetCurrentValue();
+}
+
+bool
+TargetProperties::GetEnableSyntheticValue () const
+{
+ const uint32_t idx = ePropertyEnableSynthetic;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+uint32_t
+TargetProperties::GetMaximumNumberOfChildrenToDisplay() const
+{
+ const uint32_t idx = ePropertyMaxChildrenCount;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value);
+}
+
+uint32_t
+TargetProperties::GetMaximumSizeOfStringSummary() const
+{
+ const uint32_t idx = ePropertyMaxSummaryLength;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value);
+}
+
+uint32_t
+TargetProperties::GetMaximumMemReadSize () const
+{
+ const uint32_t idx = ePropertyMaxMemReadSize;
+ return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value);
+}
+
+FileSpec
+TargetProperties::GetStandardInputPath () const
+{
+ const uint32_t idx = ePropertyInputPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx);
+}
+
+void
+TargetProperties::SetStandardInputPath (const char *p)
+{
+ const uint32_t idx = ePropertyInputPath;
+ m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p);
+}
+
+FileSpec
+TargetProperties::GetStandardOutputPath () const
+{
+ const uint32_t idx = ePropertyOutputPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec (NULL, idx);
+}
+
+void
+TargetProperties::SetStandardOutputPath (const char *p)
+{
+ const uint32_t idx = ePropertyOutputPath;
+ m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p);
+}
+
+FileSpec
+TargetProperties::GetStandardErrorPath () const
+{
+ const uint32_t idx = ePropertyErrorPath;
+ return m_collection_sp->GetPropertyAtIndexAsFileSpec(NULL, idx);
+}
+
+const char *
+TargetProperties::GetExpressionPrefixContentsAsCString ()
+{
+ const uint32_t idx = ePropertyExprPrefix;
+ OptionValueFileSpec *file = m_collection_sp->GetPropertyAtIndexAsOptionValueFileSpec (NULL, false, idx);
+ if (file)
+ {
+ const bool null_terminate = true;
+ DataBufferSP data_sp(file->GetFileContents(null_terminate));
+ if (data_sp)
+ return (const char *) data_sp->GetBytes();
+ }
+ return NULL;
+}
+
+void
+TargetProperties::SetStandardErrorPath (const char *p)
+{
+ const uint32_t idx = ePropertyErrorPath;
+ m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p);
+}
+
+bool
+TargetProperties::GetBreakpointsConsultPlatformAvoidList ()
+{
+ const uint32_t idx = ePropertyBreakpointUseAvoidList;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool
+TargetProperties::GetUseHexImmediates () const
+{
+ const uint32_t idx = ePropertyUseHexImmediates;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+bool
+TargetProperties::GetUseFastStepping () const
+{
+ const uint32_t idx = ePropertyUseFastStepping;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+LoadScriptFromSymFile
+TargetProperties::GetLoadScriptFromSymbolFile () const
+{
+ const uint32_t idx = ePropertyLoadScriptFromSymbolFile;
+ return (LoadScriptFromSymFile)m_collection_sp->GetPropertyAtIndexAsEnumeration(NULL, idx, g_properties[idx].default_uint_value);
+}
+
+Disassembler::HexImmediateStyle
+TargetProperties::GetHexImmediateStyle () const
+{
+ const uint32_t idx = ePropertyHexImmediateStyle;
+ return (Disassembler::HexImmediateStyle)m_collection_sp->GetPropertyAtIndexAsEnumeration(NULL, idx, g_properties[idx].default_uint_value);
+}
+
+MemoryModuleLoadLevel
+TargetProperties::GetMemoryModuleLoadLevel() const
+{
+ const uint32_t idx = ePropertyMemoryModuleLoadLevel;
+ return (MemoryModuleLoadLevel)m_collection_sp->GetPropertyAtIndexAsEnumeration(NULL, idx, g_properties[idx].default_uint_value);
+}
+
+
+const TargetPropertiesSP &
+Target::GetGlobalProperties()
+{
+ static TargetPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ {
+ g_settings_sp.reset (new TargetProperties (NULL));
+ }
+ return g_settings_sp;
+}
+
+const ConstString &
+Target::TargetEventData::GetFlavorString ()
+{
+ static ConstString g_flavor ("Target::TargetEventData");
+ return g_flavor;
+}
+
+const ConstString &
+Target::TargetEventData::GetFlavor () const
+{
+ return TargetEventData::GetFlavorString ();
+}
+
+Target::TargetEventData::TargetEventData (const lldb::TargetSP &new_target_sp) :
+ EventData(),
+ m_target_sp (new_target_sp)
+{
+}
+
+Target::TargetEventData::~TargetEventData()
+{
+
+}
+
+void
+Target::TargetEventData::Dump (Stream *s) const
+{
+
+}
+
+const TargetSP
+Target::TargetEventData::GetTargetFromEvent (const lldb::EventSP &event_sp)
+{
+ TargetSP target_sp;
+
+ const TargetEventData *data = GetEventDataFromEvent (event_sp.get());
+ if (data)
+ target_sp = data->m_target_sp;
+
+ return target_sp;
+}
+
+const Target::TargetEventData *
+Target::TargetEventData::GetEventDataFromEvent (const Event *event_ptr)
+{
+ if (event_ptr)
+ {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data && event_data->GetFlavor() == TargetEventData::GetFlavorString())
+ return static_cast <const TargetEventData *> (event_ptr->GetData());
+ }
+ return NULL;
+}
+
diff --git a/source/Target/TargetList.cpp b/source/Target/TargetList.cpp
new file mode 100644
index 000000000000..8d907f432697
--- /dev/null
+++ b/source/Target/TargetList.cpp
@@ -0,0 +1,576 @@
+//===-- TargetList.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/lldb-python.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Broadcaster.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Event.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/Timer.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/OptionGroupPlatform.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Platform.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/TargetList.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ConstString &
+TargetList::GetStaticBroadcasterClass ()
+{
+ static ConstString class_name ("lldb.targetList");
+ return class_name;
+}
+
+//----------------------------------------------------------------------
+// TargetList constructor
+//----------------------------------------------------------------------
+TargetList::TargetList(Debugger &debugger) :
+ Broadcaster(&debugger, TargetList::GetStaticBroadcasterClass().AsCString()),
+ m_target_list(),
+ m_target_list_mutex (Mutex::eMutexTypeRecursive),
+ m_selected_target_idx (0)
+{
+ CheckInWithManager();
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+TargetList::~TargetList()
+{
+ Mutex::Locker locker(m_target_list_mutex);
+ m_target_list.clear();
+}
+
+Error
+TargetList::CreateTarget (Debugger &debugger,
+ const char *user_exe_path,
+ const char *triple_cstr,
+ bool get_dependent_files,
+ const OptionGroupPlatform *platform_options,
+ TargetSP &target_sp)
+{
+ Error error;
+ PlatformSP platform_sp;
+
+ // This is purposely left empty unless it is specified by triple_cstr.
+ // If not initialized via triple_cstr, then the currently selected platform
+ // will set the architecture correctly.
+ const ArchSpec arch(triple_cstr);
+ if (triple_cstr && triple_cstr[0])
+ {
+ if (!arch.IsValid())
+ {
+ error.SetErrorStringWithFormat("invalid triple '%s'", triple_cstr);
+ return error;
+ }
+ }
+
+ ArchSpec platform_arch(arch);
+
+
+ if (user_exe_path && user_exe_path[0])
+ {
+ ModuleSpecList module_specs;
+ ModuleSpec module_spec;
+ module_spec.GetFileSpec().SetFile(user_exe_path, true);
+ lldb::offset_t file_offset = 0;
+ lldb::offset_t file_size = 0;
+ const size_t num_specs = ObjectFile::GetModuleSpecifications (module_spec.GetFileSpec(), file_offset, file_size, module_specs);
+ if (num_specs > 0)
+ {
+ ModuleSpec matching_module_spec;
+
+ if (num_specs == 1)
+ {
+ if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec))
+ {
+ if (platform_arch.IsValid())
+ {
+ if (!platform_arch.IsCompatibleMatch(matching_module_spec.GetArchitecture()))
+ {
+ error.SetErrorStringWithFormat("the specified architecture '%s' is not compatible with '%s' in '%s'",
+ platform_arch.GetTriple().str().c_str(),
+ matching_module_spec.GetArchitecture().GetTriple().str().c_str(),
+ module_spec.GetFileSpec().GetPath().c_str());
+ return error;
+ }
+ }
+ else
+ {
+ // Only one arch and none was specified
+ platform_arch = matching_module_spec.GetArchitecture();
+ }
+ }
+ }
+ else
+ {
+ if (arch.IsValid())
+ {
+ module_spec.GetArchitecture() = arch;
+ if (module_specs.FindMatchingModuleSpec(module_spec, matching_module_spec))
+ {
+ platform_arch = matching_module_spec.GetArchitecture();
+ }
+ }
+ // Don't just select the first architecture, we want to let the platform select
+ // the best architecture first when there are multiple archs.
+// else
+// {
+// // No arch specified, select the first arch
+// if (module_specs.GetModuleSpecAtIndex(0, matching_module_spec))
+// {
+// platform_arch = matching_module_spec.GetArchitecture();
+// }
+// }
+ }
+ }
+ }
+
+ CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
+ if (platform_options)
+ {
+ if (platform_options->PlatformWasSpecified ())
+ {
+ const bool select_platform = true;
+ platform_sp = platform_options->CreatePlatformWithOptions (interpreter,
+ arch,
+ select_platform,
+ error,
+ platform_arch);
+ if (!platform_sp)
+ return error;
+ }
+ }
+
+ if (!platform_sp)
+ {
+ // Get the current platform and make sure it is compatible with the
+ // current architecture if we have a valid architecture.
+ platform_sp = debugger.GetPlatformList().GetSelectedPlatform ();
+
+ if (arch.IsValid() && !platform_sp->IsCompatibleArchitecture(arch, false, &platform_arch))
+ {
+ platform_sp = Platform::GetPlatformForArchitecture(arch, &platform_arch);
+ }
+ }
+
+ if (!platform_arch.IsValid())
+ platform_arch = arch;
+
+ error = TargetList::CreateTarget (debugger,
+ user_exe_path,
+ platform_arch,
+ get_dependent_files,
+ platform_sp,
+ target_sp);
+ return error;
+}
+
+Error
+TargetList::CreateTarget (Debugger &debugger,
+ const char *user_exe_path,
+ const ArchSpec& specified_arch,
+ bool get_dependent_files,
+ PlatformSP &platform_sp,
+ TargetSP &target_sp)
+{
+ Timer scoped_timer (__PRETTY_FUNCTION__,
+ "TargetList::CreateTarget (file = '%s', arch = '%s')",
+ user_exe_path,
+ specified_arch.GetArchitectureName());
+ Error error;
+
+ ArchSpec arch(specified_arch);
+
+ if (platform_sp)
+ {
+ if (arch.IsValid())
+ {
+ if (!platform_sp->IsCompatibleArchitecture(arch, false, NULL))
+ platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch);
+ }
+ }
+ else if (arch.IsValid())
+ {
+ platform_sp = Platform::GetPlatformForArchitecture(specified_arch, &arch);
+ }
+
+ if (!platform_sp)
+ platform_sp = debugger.GetPlatformList().GetSelectedPlatform();
+
+ if (!arch.IsValid())
+ arch = specified_arch;
+
+ FileSpec file (user_exe_path, false);
+ if (!file.Exists() && user_exe_path && user_exe_path[0] == '~')
+ {
+ // we want to expand the tilde but we don't want to resolve any symbolic links
+ // so we can't use the FileSpec constructor's resolve flag
+ char unglobbed_path[PATH_MAX];
+ unglobbed_path[0] = '\0';
+
+ size_t return_count = FileSpec::ResolveUsername(user_exe_path, unglobbed_path, sizeof(unglobbed_path));
+
+ if (return_count == 0 || return_count >= sizeof(unglobbed_path))
+ ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", user_exe_path);
+
+ file = FileSpec(unglobbed_path, false);
+ }
+
+ bool user_exe_path_is_bundle = false;
+ char resolved_bundle_exe_path[PATH_MAX];
+ resolved_bundle_exe_path[0] = '\0';
+ if (file)
+ {
+ if (file.GetFileType() == FileSpec::eFileTypeDirectory)
+ user_exe_path_is_bundle = true;
+
+ if (file.IsRelativeToCurrentWorkingDirectory())
+ {
+ // Ignore paths that start with "./" and "../"
+ if (!((user_exe_path[0] == '.' && user_exe_path[1] == '/') ||
+ (user_exe_path[0] == '.' && user_exe_path[1] == '.' && user_exe_path[2] == '/')))
+ {
+ char cwd[PATH_MAX];
+ if (getcwd (cwd, sizeof(cwd)))
+ {
+ std::string cwd_user_exe_path (cwd);
+ cwd_user_exe_path += '/';
+ cwd_user_exe_path += user_exe_path;
+ FileSpec cwd_file (cwd_user_exe_path.c_str(), false);
+ if (cwd_file.Exists())
+ file = cwd_file;
+ }
+ }
+ }
+
+ ModuleSP exe_module_sp;
+ if (platform_sp)
+ {
+ FileSpecList executable_search_paths (Target::GetDefaultExecutableSearchPaths());
+ error = platform_sp->ResolveExecutable (file,
+ arch,
+ exe_module_sp,
+ executable_search_paths.GetSize() ? &executable_search_paths : NULL);
+ }
+
+ if (error.Success() && exe_module_sp)
+ {
+ if (exe_module_sp->GetObjectFile() == NULL)
+ {
+ if (arch.IsValid())
+ {
+ error.SetErrorStringWithFormat("\"%s\" doesn't contain architecture %s",
+ file.GetPath().c_str(),
+ arch.GetArchitectureName());
+ }
+ else
+ {
+ error.SetErrorStringWithFormat("unsupported file type \"%s\"",
+ file.GetPath().c_str());
+ }
+ return error;
+ }
+ target_sp.reset(new Target(debugger, arch, platform_sp));
+ target_sp->SetExecutableModule (exe_module_sp, get_dependent_files);
+ if (user_exe_path_is_bundle)
+ exe_module_sp->GetFileSpec().GetPath(resolved_bundle_exe_path, sizeof(resolved_bundle_exe_path));
+ }
+ }
+ else
+ {
+ // No file was specified, just create an empty target with any arch
+ // if a valid arch was specified
+ target_sp.reset(new Target(debugger, arch, platform_sp));
+ }
+
+ if (target_sp)
+ {
+ // Set argv0 with what the user typed, unless the user specified a
+ // directory. If the user specified a directory, then it is probably a
+ // bundle that was resolved and we need to use the resolved bundle path
+ if (user_exe_path)
+ {
+ // Use exactly what the user typed as the first argument when we exec or posix_spawn
+ if (user_exe_path_is_bundle && resolved_bundle_exe_path[0])
+ {
+ target_sp->SetArg0 (resolved_bundle_exe_path);
+ }
+ else
+ {
+ // Use resolved path
+ target_sp->SetArg0 (file.GetPath().c_str());
+ }
+ }
+ if (file.GetDirectory())
+ {
+ FileSpec file_dir;
+ file_dir.GetDirectory() = file.GetDirectory();
+ target_sp->GetExecutableSearchPaths ().Append (file_dir);
+ }
+ Mutex::Locker locker(m_target_list_mutex);
+ m_selected_target_idx = m_target_list.size();
+ m_target_list.push_back(target_sp);
+
+
+ }
+
+ return error;
+}
+
+bool
+TargetList::DeleteTarget (TargetSP &target_sp)
+{
+ Mutex::Locker locker(m_target_list_mutex);
+ collection::iterator pos, end = m_target_list.end();
+
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ if (pos->get() == target_sp.get())
+ {
+ m_target_list.erase(pos);
+ return true;
+ }
+ }
+ return false;
+}
+
+
+TargetSP
+TargetList::FindTargetWithExecutableAndArchitecture
+(
+ const FileSpec &exe_file_spec,
+ const ArchSpec *exe_arch_ptr
+) const
+{
+ Mutex::Locker locker (m_target_list_mutex);
+ TargetSP target_sp;
+ bool full_match = exe_file_spec.GetDirectory();
+
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ Module *exe_module = (*pos)->GetExecutableModulePointer();
+
+ if (exe_module)
+ {
+ if (FileSpec::Equal (exe_file_spec, exe_module->GetFileSpec(), full_match))
+ {
+ if (exe_arch_ptr)
+ {
+ if (!exe_arch_ptr->IsCompatibleMatch(exe_module->GetArchitecture()))
+ continue;
+ }
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+TargetSP
+TargetList::FindTargetWithProcessID (lldb::pid_t pid) const
+{
+ Mutex::Locker locker(m_target_list_mutex);
+ TargetSP target_sp;
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ Process* process = (*pos)->GetProcessSP().get();
+ if (process && process->GetID() == pid)
+ {
+ target_sp = *pos;
+ break;
+ }
+ }
+ return target_sp;
+}
+
+
+TargetSP
+TargetList::FindTargetWithProcess (Process *process) const
+{
+ TargetSP target_sp;
+ if (process)
+ {
+ Mutex::Locker locker(m_target_list_mutex);
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ if (process == (*pos)->GetProcessSP().get())
+ {
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+TargetSP
+TargetList::GetTargetSP (Target *target) const
+{
+ TargetSP target_sp;
+ if (target)
+ {
+ Mutex::Locker locker(m_target_list_mutex);
+ collection::const_iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ if (target == (*pos).get())
+ {
+ target_sp = *pos;
+ break;
+ }
+ }
+ }
+ return target_sp;
+}
+
+uint32_t
+TargetList::SendAsyncInterrupt (lldb::pid_t pid)
+{
+ uint32_t num_async_interrupts_sent = 0;
+
+ if (pid != LLDB_INVALID_PROCESS_ID)
+ {
+ TargetSP target_sp(FindTargetWithProcessID (pid));
+ if (target_sp.get())
+ {
+ Process* process = target_sp->GetProcessSP().get();
+ if (process)
+ {
+ process->SendAsyncInterrupt();
+ ++num_async_interrupts_sent;
+ }
+ }
+ }
+ else
+ {
+ // We don't have a valid pid to broadcast to, so broadcast to the target
+ // list's async broadcaster...
+ BroadcastEvent (Process::eBroadcastBitInterrupt, NULL);
+ }
+
+ return num_async_interrupts_sent;
+}
+
+uint32_t
+TargetList::SignalIfRunning (lldb::pid_t pid, int signo)
+{
+ uint32_t num_signals_sent = 0;
+ Process *process = NULL;
+ if (pid == LLDB_INVALID_PROCESS_ID)
+ {
+ // Signal all processes with signal
+ Mutex::Locker locker(m_target_list_mutex);
+ collection::iterator pos, end = m_target_list.end();
+ for (pos = m_target_list.begin(); pos != end; ++pos)
+ {
+ process = (*pos)->GetProcessSP().get();
+ if (process)
+ {
+ if (process->IsAlive())
+ {
+ ++num_signals_sent;
+ process->Signal (signo);
+ }
+ }
+ }
+ }
+ else
+ {
+ // Signal a specific process with signal
+ TargetSP target_sp(FindTargetWithProcessID (pid));
+ if (target_sp.get())
+ {
+ process = target_sp->GetProcessSP().get();
+ if (process)
+ {
+ if (process->IsAlive())
+ {
+ ++num_signals_sent;
+ process->Signal (signo);
+ }
+ }
+ }
+ }
+ return num_signals_sent;
+}
+
+int
+TargetList::GetNumTargets () const
+{
+ Mutex::Locker locker (m_target_list_mutex);
+ return m_target_list.size();
+}
+
+lldb::TargetSP
+TargetList::GetTargetAtIndex (uint32_t idx) const
+{
+ TargetSP target_sp;
+ Mutex::Locker locker (m_target_list_mutex);
+ if (idx < m_target_list.size())
+ target_sp = m_target_list[idx];
+ return target_sp;
+}
+
+uint32_t
+TargetList::GetIndexOfTarget (lldb::TargetSP target_sp) const
+{
+ Mutex::Locker locker (m_target_list_mutex);
+ size_t num_targets = m_target_list.size();
+ for (size_t idx = 0; idx < num_targets; idx++)
+ {
+ if (target_sp == m_target_list[idx])
+ return idx;
+ }
+ return UINT32_MAX;
+}
+
+uint32_t
+TargetList::SetSelectedTarget (Target* target)
+{
+ Mutex::Locker locker (m_target_list_mutex);
+ collection::const_iterator pos,
+ begin = m_target_list.begin(),
+ end = m_target_list.end();
+ for (pos = begin; pos != end; ++pos)
+ {
+ if (pos->get() == target)
+ {
+ m_selected_target_idx = std::distance (begin, pos);
+ return m_selected_target_idx;
+ }
+ }
+ m_selected_target_idx = 0;
+ return m_selected_target_idx;
+}
+
+lldb::TargetSP
+TargetList::GetSelectedTarget ()
+{
+ Mutex::Locker locker (m_target_list_mutex);
+ if (m_selected_target_idx >= m_target_list.size())
+ m_selected_target_idx = 0;
+ return GetTargetAtIndex (m_selected_target_idx);
+}
diff --git a/source/Target/Thread.cpp b/source/Target/Thread.cpp
new file mode 100644
index 000000000000..b65434bf9482
--- /dev/null
+++ b/source/Target/Thread.cpp
@@ -0,0 +1,1988 @@
+//===-- Thread.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/lldb-python.h"
+
+#include "lldb/lldb-private-log.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Core/StreamString.h"
+#include "lldb/Core/RegularExpression.h"
+#include "lldb/Host/Host.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+#include "lldb/Target/ThreadPlanBase.h"
+#include "lldb/Target/ThreadPlanStepInstruction.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepOverBreakpoint.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Target/ThreadPlanStepInRange.h"
+#include "lldb/Target/ThreadPlanStepOverRange.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+#include "lldb/Target/ThreadPlanStepUntil.h"
+#include "lldb/Target/ThreadSpec.h"
+#include "lldb/Target/Unwind.h"
+#include "Plugins/Process/Utility/UnwindLLDB.h"
+#include "UnwindMacOSXFrameBackchain.h"
+
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+const ThreadPropertiesSP &
+Thread::GetGlobalProperties()
+{
+ static ThreadPropertiesSP g_settings_sp;
+ if (!g_settings_sp)
+ g_settings_sp.reset (new ThreadProperties (true));
+ return g_settings_sp;
+}
+
+static PropertyDefinition
+g_properties[] =
+{
+ { "step-avoid-regexp", OptionValue::eTypeRegex , true , REG_EXTENDED, "^std::", NULL, "A regular expression defining functions step-in won't stop in." },
+ { "trace-thread", OptionValue::eTypeBoolean, false, false, NULL, NULL, "If true, this thread will single-step and log execution." },
+ { NULL , OptionValue::eTypeInvalid, false, 0 , NULL, NULL, NULL }
+};
+
+enum {
+ ePropertyStepAvoidRegex,
+ ePropertyEnableThreadTrace
+};
+
+
+class ThreadOptionValueProperties : public OptionValueProperties
+{
+public:
+ ThreadOptionValueProperties (const ConstString &name) :
+ OptionValueProperties (name)
+ {
+ }
+
+ // This constructor is used when creating ThreadOptionValueProperties when it
+ // is part of a new lldb_private::Thread instance. It will copy all current
+ // global property values as needed
+ ThreadOptionValueProperties (ThreadProperties *global_properties) :
+ OptionValueProperties(*global_properties->GetValueProperties())
+ {
+ }
+
+ virtual const Property *
+ GetPropertyAtIndex (const ExecutionContext *exe_ctx, bool will_modify, uint32_t idx) const
+ {
+ // When gettings the value for a key from the thread options, we will always
+ // try and grab the setting from the current thread if there is one. Else we just
+ // use the one from this instance.
+ if (exe_ctx)
+ {
+ Thread *thread = exe_ctx->GetThreadPtr();
+ if (thread)
+ {
+ ThreadOptionValueProperties *instance_properties = static_cast<ThreadOptionValueProperties *>(thread->GetValueProperties().get());
+ if (this != instance_properties)
+ return instance_properties->ProtectedGetPropertyAtIndex (idx);
+ }
+ }
+ return ProtectedGetPropertyAtIndex (idx);
+ }
+};
+
+
+
+ThreadProperties::ThreadProperties (bool is_global) :
+ Properties ()
+{
+ if (is_global)
+ {
+ m_collection_sp.reset (new ThreadOptionValueProperties(ConstString("thread")));
+ m_collection_sp->Initialize(g_properties);
+ }
+ else
+ m_collection_sp.reset (new ThreadOptionValueProperties(Thread::GetGlobalProperties().get()));
+}
+
+ThreadProperties::~ThreadProperties()
+{
+}
+
+const RegularExpression *
+ThreadProperties::GetSymbolsToAvoidRegexp()
+{
+ const uint32_t idx = ePropertyStepAvoidRegex;
+ return m_collection_sp->GetPropertyAtIndexAsOptionValueRegex (NULL, idx);
+}
+
+bool
+ThreadProperties::GetTraceEnabledState() const
+{
+ const uint32_t idx = ePropertyEnableThreadTrace;
+ return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0);
+}
+
+//------------------------------------------------------------------
+// Thread Event Data
+//------------------------------------------------------------------
+
+
+const ConstString &
+Thread::ThreadEventData::GetFlavorString ()
+{
+ static ConstString g_flavor ("Thread::ThreadEventData");
+ return g_flavor;
+}
+
+Thread::ThreadEventData::ThreadEventData (const lldb::ThreadSP thread_sp) :
+ m_thread_sp (thread_sp),
+ m_stack_id ()
+{
+}
+
+Thread::ThreadEventData::ThreadEventData (const lldb::ThreadSP thread_sp, const StackID &stack_id) :
+ m_thread_sp (thread_sp),
+ m_stack_id (stack_id)
+{
+}
+
+Thread::ThreadEventData::ThreadEventData () :
+ m_thread_sp (),
+ m_stack_id ()
+{
+}
+
+Thread::ThreadEventData::~ThreadEventData ()
+{
+}
+
+void
+Thread::ThreadEventData::Dump (Stream *s) const
+{
+
+}
+
+const Thread::ThreadEventData *
+Thread::ThreadEventData::GetEventDataFromEvent (const Event *event_ptr)
+{
+ if (event_ptr)
+ {
+ const EventData *event_data = event_ptr->GetData();
+ if (event_data && event_data->GetFlavor() == ThreadEventData::GetFlavorString())
+ return static_cast <const ThreadEventData *> (event_ptr->GetData());
+ }
+ return NULL;
+}
+
+ThreadSP
+Thread::ThreadEventData::GetThreadFromEvent (const Event *event_ptr)
+{
+ ThreadSP thread_sp;
+ const ThreadEventData *event_data = GetEventDataFromEvent (event_ptr);
+ if (event_data)
+ thread_sp = event_data->GetThread();
+ return thread_sp;
+}
+
+StackID
+Thread::ThreadEventData::GetStackIDFromEvent (const Event *event_ptr)
+{
+ StackID stack_id;
+ const ThreadEventData *event_data = GetEventDataFromEvent (event_ptr);
+ if (event_data)
+ stack_id = event_data->GetStackID();
+ return stack_id;
+}
+
+StackFrameSP
+Thread::ThreadEventData::GetStackFrameFromEvent (const Event *event_ptr)
+{
+ const ThreadEventData *event_data = GetEventDataFromEvent (event_ptr);
+ StackFrameSP frame_sp;
+ if (event_data)
+ {
+ ThreadSP thread_sp = event_data->GetThread();
+ if (thread_sp)
+ {
+ frame_sp = thread_sp->GetStackFrameList()->GetFrameWithStackID (event_data->GetStackID());
+ }
+ }
+ return frame_sp;
+}
+
+//------------------------------------------------------------------
+// Thread class
+//------------------------------------------------------------------
+
+ConstString &
+Thread::GetStaticBroadcasterClass ()
+{
+ static ConstString class_name ("lldb.thread");
+ return class_name;
+}
+
+Thread::Thread (Process &process, lldb::tid_t tid) :
+ ThreadProperties (false),
+ UserID (tid),
+ Broadcaster(&process.GetTarget().GetDebugger(), Thread::GetStaticBroadcasterClass().AsCString()),
+ m_process_wp (process.shared_from_this()),
+ m_stop_info_sp (),
+ m_stop_info_stop_id (0),
+ m_index_id (process.GetNextThreadIndexID(tid)),
+ m_reg_context_sp (),
+ m_state (eStateUnloaded),
+ m_state_mutex (Mutex::eMutexTypeRecursive),
+ m_plan_stack (),
+ m_completed_plan_stack(),
+ m_frame_mutex (Mutex::eMutexTypeRecursive),
+ m_curr_frames_sp (),
+ m_prev_frames_sp (),
+ m_resume_signal (LLDB_INVALID_SIGNAL_NUMBER),
+ m_resume_state (eStateRunning),
+ m_temporary_resume_state (eStateRunning),
+ m_unwinder_ap (),
+ m_destroy_called (false),
+ m_override_should_notify (eLazyBoolCalculate)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Thread::Thread(tid = 0x%4.4" PRIx64 ")", this, GetID());
+
+ CheckInWithManager();
+ QueueFundamentalPlan(true);
+}
+
+
+Thread::~Thread()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OBJECT));
+ if (log)
+ log->Printf ("%p Thread::~Thread(tid = 0x%4.4" PRIx64 ")", this, GetID());
+ /// If you hit this assert, it means your derived class forgot to call DoDestroy in its destructor.
+ assert (m_destroy_called);
+}
+
+void
+Thread::DestroyThread ()
+{
+ // Tell any plans on the plan stack that the thread is being destroyed since
+ // any active plans that have a thread go away in the middle of might need
+ // to do cleanup.
+ for (auto plan : m_plan_stack)
+ plan->ThreadDestroyed();
+
+ m_destroy_called = true;
+ m_plan_stack.clear();
+ m_discarded_plan_stack.clear();
+ m_completed_plan_stack.clear();
+
+ // Push a ThreadPlanNull on the plan stack. That way we can continue assuming that the
+ // plan stack is never empty, but if somebody errantly asks questions of a destroyed thread
+ // without checking first whether it is destroyed, they won't crash.
+ ThreadPlanSP null_plan_sp(new ThreadPlanNull (*this));
+ m_plan_stack.push_back (null_plan_sp);
+
+ m_stop_info_sp.reset();
+ m_reg_context_sp.reset();
+ m_unwinder_ap.reset();
+ Mutex::Locker locker(m_frame_mutex);
+ m_curr_frames_sp.reset();
+ m_prev_frames_sp.reset();
+}
+
+void
+Thread::BroadcastSelectedFrameChange(StackID &new_frame_id)
+{
+ if (EventTypeHasListeners(eBroadcastBitSelectedFrameChanged))
+ BroadcastEvent(eBroadcastBitSelectedFrameChanged, new ThreadEventData (this->shared_from_this(), new_frame_id));
+}
+
+uint32_t
+Thread::SetSelectedFrame (lldb_private::StackFrame *frame, bool broadcast)
+{
+ uint32_t ret_value = GetStackFrameList()->SetSelectedFrame(frame);
+ if (broadcast)
+ BroadcastSelectedFrameChange(frame->GetStackID());
+ return ret_value;
+}
+
+bool
+Thread::SetSelectedFrameByIndex (uint32_t frame_idx, bool broadcast)
+{
+ StackFrameSP frame_sp(GetStackFrameList()->GetFrameAtIndex (frame_idx));
+ if (frame_sp)
+ {
+ GetStackFrameList()->SetSelectedFrame(frame_sp.get());
+ if (broadcast)
+ BroadcastSelectedFrameChange(frame_sp->GetStackID());
+ return true;
+ }
+ else
+ return false;
+}
+
+bool
+Thread::SetSelectedFrameByIndexNoisily (uint32_t frame_idx, Stream &output_stream)
+{
+ const bool broadcast = true;
+ bool success = SetSelectedFrameByIndex (frame_idx, broadcast);
+ if (success)
+ {
+ StackFrameSP frame_sp = GetSelectedFrame();
+ if (frame_sp)
+ {
+ bool already_shown = false;
+ SymbolContext frame_sc(frame_sp->GetSymbolContext(eSymbolContextLineEntry));
+ if (GetProcess()->GetTarget().GetDebugger().GetUseExternalEditor() && frame_sc.line_entry.file && frame_sc.line_entry.line != 0)
+ {
+ already_shown = Host::OpenFileInExternalEditor (frame_sc.line_entry.file, frame_sc.line_entry.line);
+ }
+
+ bool show_frame_info = true;
+ bool show_source = !already_shown;
+ return frame_sp->GetStatus (output_stream, show_frame_info, show_source);
+ }
+ return false;
+ }
+ else
+ return false;
+}
+
+
+lldb::StopInfoSP
+Thread::GetStopInfo ()
+{
+ if (m_destroy_called)
+ return m_stop_info_sp;
+
+ ThreadPlanSP plan_sp (GetCompletedPlan());
+ ProcessSP process_sp (GetProcess());
+ const uint32_t stop_id = process_sp ? process_sp->GetStopID() : UINT32_MAX;
+ if (plan_sp && plan_sp->PlanSucceeded())
+ {
+ return StopInfo::CreateStopReasonWithPlan (plan_sp, GetReturnValueObject());
+ }
+ else
+ {
+ if ((m_stop_info_stop_id == stop_id) || // Stop info is valid, just return what we have (even if empty)
+ (m_stop_info_sp && m_stop_info_sp->IsValid())) // Stop info is valid, just return what we have
+ {
+ return m_stop_info_sp;
+ }
+ else
+ {
+ GetPrivateStopInfo ();
+ return m_stop_info_sp;
+ }
+ }
+}
+
+lldb::StopInfoSP
+Thread::GetPrivateStopInfo ()
+{
+ if (m_destroy_called)
+ return m_stop_info_sp;
+
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ {
+ const uint32_t process_stop_id = process_sp->GetStopID();
+ if (m_stop_info_stop_id != process_stop_id)
+ {
+ if (m_stop_info_sp)
+ {
+ if (m_stop_info_sp->IsValid()
+ || IsStillAtLastBreakpointHit()
+ || GetCurrentPlan()->IsVirtualStep())
+ SetStopInfo (m_stop_info_sp);
+ else
+ m_stop_info_sp.reset();
+ }
+
+ if (!m_stop_info_sp)
+ {
+ if (CalculateStopInfo() == false)
+ SetStopInfo (StopInfoSP());
+ }
+ }
+ }
+ return m_stop_info_sp;
+}
+
+
+lldb::StopReason
+Thread::GetStopReason()
+{
+ lldb::StopInfoSP stop_info_sp (GetStopInfo ());
+ if (stop_info_sp)
+ return stop_info_sp->GetStopReason();
+ return eStopReasonNone;
+}
+
+
+
+void
+Thread::SetStopInfo (const lldb::StopInfoSP &stop_info_sp)
+{
+ m_stop_info_sp = stop_info_sp;
+ if (m_stop_info_sp)
+ {
+ m_stop_info_sp->MakeStopInfoValid();
+ // If we are overriding the ShouldReportStop, do that here:
+ if (m_override_should_notify != eLazyBoolCalculate)
+ m_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
+ }
+
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ m_stop_info_stop_id = process_sp->GetStopID();
+ else
+ m_stop_info_stop_id = UINT32_MAX;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Printf("%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)\n", this, GetID(), stop_info_sp ? stop_info_sp->GetDescription() : "<NULL>", m_stop_info_stop_id);
+}
+
+void
+Thread::SetShouldReportStop (Vote vote)
+{
+ if (vote == eVoteNoOpinion)
+ return;
+ else
+ {
+ m_override_should_notify = (vote == eVoteYes ? eLazyBoolYes : eLazyBoolNo);
+ if (m_stop_info_sp)
+ m_stop_info_sp->OverrideShouldNotify (m_override_should_notify == eLazyBoolYes);
+ }
+}
+
+void
+Thread::SetStopInfoToNothing()
+{
+ // Note, we can't just NULL out the private reason, or the native thread implementation will try to
+ // go calculate it again. For now, just set it to a Unix Signal with an invalid signal number.
+ SetStopInfo (StopInfo::CreateStopReasonWithSignal (*this, LLDB_INVALID_SIGNAL_NUMBER));
+}
+
+bool
+Thread::ThreadStoppedForAReason (void)
+{
+ return (bool) GetPrivateStopInfo ();
+}
+
+bool
+Thread::CheckpointThreadState (ThreadStateCheckpoint &saved_state)
+{
+ if (!SaveFrameZeroState(saved_state.register_backup))
+ return false;
+
+ saved_state.stop_info_sp = GetStopInfo();
+ ProcessSP process_sp (GetProcess());
+ if (process_sp)
+ saved_state.orig_stop_id = process_sp->GetStopID();
+ saved_state.current_inlined_depth = GetCurrentInlinedDepth();
+
+ return true;
+}
+
+bool
+Thread::RestoreRegisterStateFromCheckpoint (ThreadStateCheckpoint &saved_state)
+{
+ RestoreSaveFrameZero(saved_state.register_backup);
+ return true;
+}
+
+bool
+Thread::RestoreThreadStateFromCheckpoint (ThreadStateCheckpoint &saved_state)
+{
+ if (saved_state.stop_info_sp)
+ saved_state.stop_info_sp->MakeStopInfoValid();
+ SetStopInfo(saved_state.stop_info_sp);
+ GetStackFrameList()->SetCurrentInlinedDepth (saved_state.current_inlined_depth);
+ return true;
+}
+
+StateType
+Thread::GetState() const
+{
+ // If any other threads access this we will need a mutex for it
+ Mutex::Locker locker(m_state_mutex);
+ return m_state;
+}
+
+void
+Thread::SetState(StateType state)
+{
+ Mutex::Locker locker(m_state_mutex);
+ m_state = state;
+}
+
+void
+Thread::WillStop()
+{
+ ThreadPlan *current_plan = GetCurrentPlan();
+
+ // FIXME: I may decide to disallow threads with no plans. In which
+ // case this should go to an assert.
+
+ if (!current_plan)
+ return;
+
+ current_plan->WillStop();
+}
+
+void
+Thread::SetupForResume ()
+{
+ if (GetResumeState() != eStateSuspended)
+ {
+
+ // If we're at a breakpoint push the step-over breakpoint plan. Do this before
+ // telling the current plan it will resume, since we might change what the current
+ // plan is.
+
+// StopReason stop_reason = lldb::eStopReasonInvalid;
+// StopInfoSP stop_info_sp = GetStopInfo();
+// if (stop_info_sp.get())
+// stop_reason = stop_info_sp->GetStopReason();
+// if (stop_reason == lldb::eStopReasonBreakpoint)
+ lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext());
+ if (reg_ctx_sp)
+ {
+ BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(reg_ctx_sp->GetPC());
+ if (bp_site_sp)
+ {
+ // Note, don't assume there's a ThreadPlanStepOverBreakpoint, the target may not require anything
+ // special to step over a breakpoint.
+
+ ThreadPlan *cur_plan = GetCurrentPlan();
+
+ if (cur_plan->GetKind() != ThreadPlan::eKindStepOverBreakpoint)
+ {
+ ThreadPlanStepOverBreakpoint *step_bp_plan = new ThreadPlanStepOverBreakpoint (*this);
+ if (step_bp_plan)
+ {
+ ThreadPlanSP step_bp_plan_sp;
+ step_bp_plan->SetPrivate (true);
+
+ if (GetCurrentPlan()->RunState() != eStateStepping)
+ {
+ step_bp_plan->SetAutoContinue(true);
+ }
+ step_bp_plan_sp.reset (step_bp_plan);
+ QueueThreadPlan (step_bp_plan_sp, false);
+ }
+ }
+ }
+ }
+ }
+}
+
+bool
+Thread::ShouldResume (StateType resume_state)
+{
+ // At this point clear the completed plan stack.
+ m_completed_plan_stack.clear();
+ m_discarded_plan_stack.clear();
+ m_override_should_notify = eLazyBoolCalculate;
+
+ m_temporary_resume_state = resume_state;
+
+ lldb::ThreadSP backing_thread_sp (GetBackingThread ());
+ if (backing_thread_sp)
+ backing_thread_sp->m_temporary_resume_state = resume_state;
+
+ // Make sure m_stop_info_sp is valid
+ GetPrivateStopInfo();
+
+ // This is a little dubious, but we are trying to limit how often we actually fetch stop info from
+ // the target, 'cause that slows down single stepping. So assume that if we got to the point where
+ // we're about to resume, and we haven't yet had to fetch the stop reason, then it doesn't need to know
+ // about the fact that we are resuming...
+ const uint32_t process_stop_id = GetProcess()->GetStopID();
+ if (m_stop_info_stop_id == process_stop_id &&
+ (m_stop_info_sp && m_stop_info_sp->IsValid()))
+ {
+ StopInfo *stop_info = GetPrivateStopInfo().get();
+ if (stop_info)
+ stop_info->WillResume (resume_state);
+ }
+
+ // Tell all the plans that we are about to resume in case they need to clear any state.
+ // We distinguish between the plan on the top of the stack and the lower
+ // plans in case a plan needs to do any special business before it runs.
+
+ bool need_to_resume = false;
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+ if (plan_ptr)
+ {
+ need_to_resume = plan_ptr->WillResume(resume_state, true);
+
+ while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL)
+ {
+ plan_ptr->WillResume (resume_state, false);
+ }
+
+ // If the WillResume for the plan says we are faking a resume, then it will have set an appropriate stop info.
+ // In that case, don't reset it here.
+
+ if (need_to_resume && resume_state != eStateSuspended)
+ {
+ m_stop_info_sp.reset();
+ }
+ }
+
+ if (need_to_resume)
+ {
+ ClearStackFrames();
+ // Let Thread subclasses do any special work they need to prior to resuming
+ WillResume (resume_state);
+ }
+
+ return need_to_resume;
+}
+
+void
+Thread::DidResume ()
+{
+ SetResumeSignal (LLDB_INVALID_SIGNAL_NUMBER);
+}
+
+void
+Thread::DidStop ()
+{
+ SetState (eStateStopped);
+}
+
+bool
+Thread::ShouldStop (Event* event_ptr)
+{
+ ThreadPlan *current_plan = GetCurrentPlan();
+
+ bool should_stop = true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (GetResumeState () == eStateSuspended)
+ {
+ if (log)
+ log->Printf ("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)",
+ __FUNCTION__,
+ GetID (),
+ GetProtocolID());
+ return false;
+ }
+
+ if (GetTemporaryResumeState () == eStateSuspended)
+ {
+ if (log)
+ log->Printf ("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", should_stop = 0 (ignore since thread was suspended)",
+ __FUNCTION__,
+ GetID (),
+ GetProtocolID());
+ return false;
+ }
+
+ // Based on the current thread plan and process stop info, check if this
+ // thread caused the process to stop. NOTE: this must take place before
+ // the plan is moved from the current plan stack to the completed plan
+ // stack.
+ if (ThreadStoppedForAReason() == false)
+ {
+ if (log)
+ log->Printf ("Thread::%s for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64 ", should_stop = 0 (ignore since no stop reason)",
+ __FUNCTION__,
+ GetID (),
+ GetProtocolID(),
+ GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS);
+ return false;
+ }
+
+ if (log)
+ {
+ log->Printf ("Thread::%s(%p) for tid = 0x%4.4" PRIx64 " 0x%4.4" PRIx64 ", pc = 0x%16.16" PRIx64,
+ __FUNCTION__,
+ this,
+ GetID (),
+ GetProtocolID (),
+ GetRegisterContext() ? GetRegisterContext()->GetPC() : LLDB_INVALID_ADDRESS);
+ log->Printf ("^^^^^^^^ Thread::ShouldStop Begin ^^^^^^^^");
+ StreamString s;
+ s.IndentMore();
+ DumpThreadPlans(&s);
+ log->Printf ("Plan stack initial state:\n%s", s.GetData());
+ }
+
+ // The top most plan always gets to do the trace log...
+ current_plan->DoTraceLog ();
+
+ // First query the stop info's ShouldStopSynchronous. This handles "synchronous" stop reasons, for example the breakpoint
+ // command on internal breakpoints. If a synchronous stop reason says we should not stop, then we don't have to
+ // do any more work on this stop.
+ StopInfoSP private_stop_info (GetPrivateStopInfo());
+ if (private_stop_info && private_stop_info->ShouldStopSynchronous(event_ptr) == false)
+ {
+ if (log)
+ log->Printf ("StopInfo::ShouldStop async callback says we should not stop, returning ShouldStop of false.");
+ return false;
+ }
+
+ // If we've already been restarted, don't query the plans since the state they would examine is not current.
+ if (Process::ProcessEventData::GetRestartedFromEvent(event_ptr))
+ return false;
+
+ // Before the plans see the state of the world, calculate the current inlined depth.
+ GetStackFrameList()->CalculateCurrentInlinedDepth();
+
+ // If the base plan doesn't understand why we stopped, then we have to find a plan that does.
+ // If that plan is still working, then we don't need to do any more work. If the plan that explains
+ // the stop is done, then we should pop all the plans below it, and pop it, and then let the plans above it decide
+ // whether they still need to do more work.
+
+ bool done_processing_current_plan = false;
+
+ if (!current_plan->PlanExplainsStop(event_ptr))
+ {
+ if (current_plan->TracerExplainsStop())
+ {
+ done_processing_current_plan = true;
+ should_stop = false;
+ }
+ else
+ {
+ // If the current plan doesn't explain the stop, then find one that
+ // does and let it handle the situation.
+ ThreadPlan *plan_ptr = current_plan;
+ while ((plan_ptr = GetPreviousPlan(plan_ptr)) != NULL)
+ {
+ if (plan_ptr->PlanExplainsStop(event_ptr))
+ {
+ should_stop = plan_ptr->ShouldStop (event_ptr);
+
+ // plan_ptr explains the stop, next check whether plan_ptr is done, if so, then we should take it
+ // and all the plans below it off the stack.
+
+ if (plan_ptr->MischiefManaged())
+ {
+ // We're going to pop the plans up to and including the plan that explains the stop.
+ ThreadPlan *prev_plan_ptr = GetPreviousPlan (plan_ptr);
+
+ do
+ {
+ if (should_stop)
+ current_plan->WillStop();
+ PopPlan();
+ }
+ while ((current_plan = GetCurrentPlan()) != prev_plan_ptr);
+ // Now, if the responsible plan was not "Okay to discard" then we're done,
+ // otherwise we forward this to the next plan in the stack below.
+ if (plan_ptr->IsMasterPlan() && !plan_ptr->OkayToDiscard())
+ done_processing_current_plan = true;
+ else
+ done_processing_current_plan = false;
+ }
+ else
+ done_processing_current_plan = true;
+
+ break;
+ }
+
+ }
+ }
+ }
+
+ if (!done_processing_current_plan)
+ {
+ bool over_ride_stop = current_plan->ShouldAutoContinue(event_ptr);
+
+ if (log)
+ log->Printf("Plan %s explains stop, auto-continue %i.", current_plan->GetName(), over_ride_stop);
+
+ // We're starting from the base plan, so just let it decide;
+ if (PlanIsBasePlan(current_plan))
+ {
+ should_stop = current_plan->ShouldStop (event_ptr);
+ if (log)
+ log->Printf("Base plan says should stop: %i.", should_stop);
+ }
+ else
+ {
+ // Otherwise, don't let the base plan override what the other plans say to do, since
+ // presumably if there were other plans they would know what to do...
+ while (1)
+ {
+ if (PlanIsBasePlan(current_plan))
+ break;
+
+ should_stop = current_plan->ShouldStop(event_ptr);
+ if (log)
+ log->Printf("Plan %s should stop: %d.", current_plan->GetName(), should_stop);
+ if (current_plan->MischiefManaged())
+ {
+ if (should_stop)
+ current_plan->WillStop();
+
+ // If a Master Plan wants to stop, and wants to stick on the stack, we let it.
+ // Otherwise, see if the plan's parent wants to stop.
+
+ if (should_stop && current_plan->IsMasterPlan() && !current_plan->OkayToDiscard())
+ {
+ PopPlan();
+ break;
+ }
+ else
+ {
+
+ PopPlan();
+
+ current_plan = GetCurrentPlan();
+ if (current_plan == NULL)
+ {
+ break;
+ }
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+
+ if (over_ride_stop)
+ should_stop = false;
+
+ // One other potential problem is that we set up a master plan, then stop in before it is complete - for instance
+ // by hitting a breakpoint during a step-over - then do some step/finish/etc operations that wind up
+ // past the end point condition of the initial plan. We don't want to strand the original plan on the stack,
+ // This code clears stale plans off the stack.
+
+ if (should_stop)
+ {
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+ while (!PlanIsBasePlan(plan_ptr))
+ {
+ bool stale = plan_ptr->IsPlanStale ();
+ ThreadPlan *examined_plan = plan_ptr;
+ plan_ptr = GetPreviousPlan (examined_plan);
+
+ if (stale)
+ {
+ if (log)
+ log->Printf("Plan %s being discarded in cleanup, it says it is already done.", examined_plan->GetName());
+ DiscardThreadPlansUpToPlan(examined_plan);
+ }
+ }
+ }
+
+ }
+
+ if (log)
+ {
+ StreamString s;
+ s.IndentMore();
+ DumpThreadPlans(&s);
+ log->Printf ("Plan stack final state:\n%s", s.GetData());
+ log->Printf ("vvvvvvvv Thread::ShouldStop End (returning %i) vvvvvvvv", should_stop);
+ }
+ return should_stop;
+}
+
+Vote
+Thread::ShouldReportStop (Event* event_ptr)
+{
+ StateType thread_state = GetResumeState ();
+ StateType temp_thread_state = GetTemporaryResumeState();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (thread_state == eStateSuspended || thread_state == eStateInvalid)
+ {
+ if (log)
+ log->Printf ("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (state was suspended or invalid)", GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (temp_thread_state == eStateSuspended || temp_thread_state == eStateInvalid)
+ {
+ if (log)
+ log->Printf ("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (temporary state was suspended or invalid)", GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (!ThreadStoppedForAReason())
+ {
+ if (log)
+ log->Printf ("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i (thread didn't stop for a reason.)", GetID(), eVoteNoOpinion);
+ return eVoteNoOpinion;
+ }
+
+ if (m_completed_plan_stack.size() > 0)
+ {
+ // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (log)
+ log->Printf ("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote for complete stack's back plan", GetID());
+ return m_completed_plan_stack.back()->ShouldReportStop (event_ptr);
+ }
+ else
+ {
+ Vote thread_vote = eVoteNoOpinion;
+ ThreadPlan *plan_ptr = GetCurrentPlan();
+ while (1)
+ {
+ if (plan_ptr->PlanExplainsStop(event_ptr))
+ {
+ thread_vote = plan_ptr->ShouldReportStop(event_ptr);
+ break;
+ }
+ if (PlanIsBasePlan(plan_ptr))
+ break;
+ else
+ plan_ptr = GetPreviousPlan(plan_ptr);
+ }
+ if (log)
+ log->Printf ("Thread::ShouldReportStop() tid = 0x%4.4" PRIx64 ": returning vote %i for current plan", GetID(), thread_vote);
+
+ return thread_vote;
+ }
+}
+
+Vote
+Thread::ShouldReportRun (Event* event_ptr)
+{
+ StateType thread_state = GetResumeState ();
+
+ if (thread_state == eStateSuspended
+ || thread_state == eStateInvalid)
+ {
+ return eVoteNoOpinion;
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (m_completed_plan_stack.size() > 0)
+ {
+ // Don't use GetCompletedPlan here, since that suppresses private plans.
+ if (log)
+ log->Printf ("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.",
+ GetIndexID(),
+ this,
+ GetID(),
+ StateAsCString(GetTemporaryResumeState()),
+ m_completed_plan_stack.back()->GetName());
+
+ return m_completed_plan_stack.back()->ShouldReportRun (event_ptr);
+ }
+ else
+ {
+ if (log)
+ log->Printf ("Current Plan for thread %d(%p) (0x%4.4" PRIx64 ", %s): %s being asked whether we should report run.",
+ GetIndexID(),
+ this,
+ GetID(),
+ StateAsCString(GetTemporaryResumeState()),
+ GetCurrentPlan()->GetName());
+
+ return GetCurrentPlan()->ShouldReportRun (event_ptr);
+ }
+}
+
+bool
+Thread::MatchesSpec (const ThreadSpec *spec)
+{
+ if (spec == NULL)
+ return true;
+
+ return spec->ThreadPassesBasicTests(*this);
+}
+
+void
+Thread::PushPlan (ThreadPlanSP &thread_plan_sp)
+{
+ if (thread_plan_sp)
+ {
+ // If the thread plan doesn't already have a tracer, give it its parent's tracer:
+ if (!thread_plan_sp->GetThreadPlanTracer())
+ thread_plan_sp->SetThreadPlanTracer(m_plan_stack.back()->GetThreadPlanTracer());
+ m_plan_stack.push_back (thread_plan_sp);
+
+ thread_plan_sp->DidPush();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ StreamString s;
+ thread_plan_sp->GetDescription (&s, lldb::eDescriptionLevelFull);
+ log->Printf("Thread::PushPlan(0x%p): \"%s\", tid = 0x%4.4" PRIx64 ".",
+ this,
+ s.GetData(),
+ thread_plan_sp->GetThread().GetID());
+ }
+ }
+}
+
+void
+Thread::PopPlan ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (m_plan_stack.size() <= 1)
+ return;
+ else
+ {
+ ThreadPlanSP &plan = m_plan_stack.back();
+ if (log)
+ {
+ log->Printf("Popping plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID());
+ }
+ m_completed_plan_stack.push_back (plan);
+ plan->WillPop();
+ m_plan_stack.pop_back();
+ }
+}
+
+void
+Thread::DiscardPlan ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (m_plan_stack.size() > 1)
+ {
+ ThreadPlanSP &plan = m_plan_stack.back();
+ if (log)
+ log->Printf("Discarding plan: \"%s\", tid = 0x%4.4" PRIx64 ".", plan->GetName(), plan->GetThread().GetID());
+
+ m_discarded_plan_stack.push_back (plan);
+ plan->WillPop();
+ m_plan_stack.pop_back();
+ }
+}
+
+ThreadPlan *
+Thread::GetCurrentPlan ()
+{
+ // There will always be at least the base plan. If somebody is mucking with a
+ // thread with an empty plan stack, we should assert right away.
+ if (m_plan_stack.empty())
+ return NULL;
+ return m_plan_stack.back().get();
+}
+
+ThreadPlanSP
+Thread::GetCompletedPlan ()
+{
+ ThreadPlanSP empty_plan_sp;
+ if (!m_completed_plan_stack.empty())
+ {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--)
+ {
+ ThreadPlanSP completed_plan_sp;
+ completed_plan_sp = m_completed_plan_stack[i];
+ if (!completed_plan_sp->GetPrivate ())
+ return completed_plan_sp;
+ }
+ }
+ return empty_plan_sp;
+}
+
+ValueObjectSP
+Thread::GetReturnValueObject ()
+{
+ if (!m_completed_plan_stack.empty())
+ {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--)
+ {
+ ValueObjectSP return_valobj_sp;
+ return_valobj_sp = m_completed_plan_stack[i]->GetReturnValueObject();
+ if (return_valobj_sp)
+ return return_valobj_sp;
+ }
+ }
+ return ValueObjectSP();
+}
+
+bool
+Thread::IsThreadPlanDone (ThreadPlan *plan)
+{
+ if (!m_completed_plan_stack.empty())
+ {
+ for (int i = m_completed_plan_stack.size() - 1; i >= 0; i--)
+ {
+ if (m_completed_plan_stack[i].get() == plan)
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+Thread::WasThreadPlanDiscarded (ThreadPlan *plan)
+{
+ if (!m_discarded_plan_stack.empty())
+ {
+ for (int i = m_discarded_plan_stack.size() - 1; i >= 0; i--)
+ {
+ if (m_discarded_plan_stack[i].get() == plan)
+ return true;
+ }
+ }
+ return false;
+}
+
+ThreadPlan *
+Thread::GetPreviousPlan (ThreadPlan *current_plan)
+{
+ if (current_plan == NULL)
+ return NULL;
+
+ int stack_size = m_completed_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ if (current_plan == m_completed_plan_stack[i].get())
+ return m_completed_plan_stack[i-1].get();
+ }
+
+ if (stack_size > 0 && m_completed_plan_stack[0].get() == current_plan)
+ {
+ if (m_plan_stack.size() > 0)
+ return m_plan_stack.back().get();
+ else
+ return NULL;
+ }
+
+ stack_size = m_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ if (current_plan == m_plan_stack[i].get())
+ return m_plan_stack[i-1].get();
+ }
+ return NULL;
+}
+
+void
+Thread::QueueThreadPlan (ThreadPlanSP &thread_plan_sp, bool abort_other_plans)
+{
+ if (abort_other_plans)
+ DiscardThreadPlans(true);
+
+ PushPlan (thread_plan_sp);
+}
+
+
+void
+Thread::EnableTracer (bool value, bool single_stepping)
+{
+ int stack_size = m_plan_stack.size();
+ for (int i = 0; i < stack_size; i++)
+ {
+ if (m_plan_stack[i]->GetThreadPlanTracer())
+ {
+ m_plan_stack[i]->GetThreadPlanTracer()->EnableTracing(value);
+ m_plan_stack[i]->GetThreadPlanTracer()->EnableSingleStep(single_stepping);
+ }
+ }
+}
+
+void
+Thread::SetTracer (lldb::ThreadPlanTracerSP &tracer_sp)
+{
+ int stack_size = m_plan_stack.size();
+ for (int i = 0; i < stack_size; i++)
+ m_plan_stack[i]->SetThreadPlanTracer(tracer_sp);
+}
+
+void
+Thread::DiscardThreadPlansUpToPlan (lldb::ThreadPlanSP &up_to_plan_sp)
+{
+ DiscardThreadPlansUpToPlan (up_to_plan_sp.get());
+}
+
+void
+Thread::DiscardThreadPlansUpToPlan (ThreadPlan *up_to_plan_ptr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ log->Printf("Discarding thread plans for thread tid = 0x%4.4" PRIx64 ", up to %p", GetID(), up_to_plan_ptr);
+ }
+
+ int stack_size = m_plan_stack.size();
+
+ // If the input plan is NULL, discard all plans. Otherwise make sure this plan is in the
+ // stack, and if so discard up to and including it.
+
+ if (up_to_plan_ptr == NULL)
+ {
+ for (int i = stack_size - 1; i > 0; i--)
+ DiscardPlan();
+ }
+ else
+ {
+ bool found_it = false;
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ if (m_plan_stack[i].get() == up_to_plan_ptr)
+ found_it = true;
+ }
+ if (found_it)
+ {
+ bool last_one = false;
+ for (int i = stack_size - 1; i > 0 && !last_one ; i--)
+ {
+ if (GetCurrentPlan() == up_to_plan_ptr)
+ last_one = true;
+ DiscardPlan();
+ }
+ }
+ }
+ return;
+}
+
+void
+Thread::DiscardThreadPlans(bool force)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ log->Printf("Discarding thread plans for thread (tid = 0x%4.4" PRIx64 ", force %d)", GetID(), force);
+ }
+
+ if (force)
+ {
+ int stack_size = m_plan_stack.size();
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ DiscardPlan();
+ }
+ return;
+ }
+
+ while (1)
+ {
+
+ int master_plan_idx;
+ bool discard = true;
+
+ // Find the first master plan, see if it wants discarding, and if yes discard up to it.
+ for (master_plan_idx = m_plan_stack.size() - 1; master_plan_idx >= 0; master_plan_idx--)
+ {
+ if (m_plan_stack[master_plan_idx]->IsMasterPlan())
+ {
+ discard = m_plan_stack[master_plan_idx]->OkayToDiscard();
+ break;
+ }
+ }
+
+ if (discard)
+ {
+ // First pop all the dependent plans:
+ for (int i = m_plan_stack.size() - 1; i > master_plan_idx; i--)
+ {
+
+ // FIXME: Do we need a finalize here, or is the rule that "PrepareForStop"
+ // for the plan leaves it in a state that it is safe to pop the plan
+ // with no more notice?
+ DiscardPlan();
+ }
+
+ // Now discard the master plan itself.
+ // The bottom-most plan never gets discarded. "OkayToDiscard" for it means
+ // discard it's dependent plans, but not it...
+ if (master_plan_idx > 0)
+ {
+ DiscardPlan();
+ }
+ }
+ else
+ {
+ // If the master plan doesn't want to get discarded, then we're done.
+ break;
+ }
+
+ }
+}
+
+bool
+Thread::PlanIsBasePlan (ThreadPlan *plan_ptr)
+{
+ if (plan_ptr->IsBasePlan())
+ return true;
+ else if (m_plan_stack.size() == 0)
+ return false;
+ else
+ return m_plan_stack[0].get() == plan_ptr;
+}
+
+Error
+Thread::UnwindInnermostExpression()
+{
+ Error error;
+ int stack_size = m_plan_stack.size();
+
+ // If the input plan is NULL, discard all plans. Otherwise make sure this plan is in the
+ // stack, and if so discard up to and including it.
+
+ for (int i = stack_size - 1; i > 0; i--)
+ {
+ if (m_plan_stack[i]->GetKind() == ThreadPlan::eKindCallFunction)
+ {
+ DiscardThreadPlansUpToPlan(m_plan_stack[i].get());
+ return error;
+ }
+ }
+ error.SetErrorString("No expressions currently active on this thread");
+ return error;
+}
+
+
+ThreadPlanSP
+Thread::QueueFundamentalPlan (bool abort_other_plans)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanBase(*this));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepSingleInstruction
+(
+ bool step_over,
+ bool abort_other_plans,
+ bool stop_other_threads
+)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanStepInstruction (*this, step_over, stop_other_threads, eVoteNoOpinion, eVoteNoOpinion));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepOverRange
+(
+ bool abort_other_plans,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ lldb::RunMode stop_other_threads
+)
+{
+ ThreadPlanSP thread_plan_sp;
+ thread_plan_sp.reset (new ThreadPlanStepOverRange (*this, range, addr_context, stop_other_threads));
+
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepInRange
+(
+ bool abort_other_plans,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ const char *step_in_target,
+ lldb::RunMode stop_other_threads,
+ bool avoid_code_without_debug_info
+)
+{
+ ThreadPlanSP thread_plan_sp;
+ ThreadPlanStepInRange *plan = new ThreadPlanStepInRange (*this, range, addr_context, stop_other_threads);
+ if (avoid_code_without_debug_info)
+ plan->GetFlags().Set (ThreadPlanShouldStopHere::eAvoidNoDebug);
+ else
+ plan->GetFlags().Clear (ThreadPlanShouldStopHere::eAvoidNoDebug);
+ if (step_in_target)
+ plan->SetStepInTarget(step_in_target);
+ thread_plan_sp.reset (plan);
+
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepOverBreakpointPlan (bool abort_other_plans)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanStepOverBreakpoint (*this));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepOut
+(
+ bool abort_other_plans,
+ SymbolContext *addr_context,
+ bool first_insn,
+ bool stop_other_threads,
+ Vote stop_vote,
+ Vote run_vote,
+ uint32_t frame_idx
+)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanStepOut (*this,
+ addr_context,
+ first_insn,
+ stop_other_threads,
+ stop_vote,
+ run_vote,
+ frame_idx));
+
+ if (thread_plan_sp->ValidatePlan(NULL))
+ {
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+ }
+ else
+ {
+ return ThreadPlanSP();
+ }
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepThrough (StackID &return_stack_id, bool abort_other_plans, bool stop_other_threads)
+{
+ ThreadPlanSP thread_plan_sp(new ThreadPlanStepThrough (*this, return_stack_id, stop_other_threads));
+ if (!thread_plan_sp || !thread_plan_sp->ValidatePlan (NULL))
+ return ThreadPlanSP();
+
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForCallFunction (bool abort_other_plans,
+ Address& function,
+ lldb::addr_t arg,
+ bool stop_other_threads,
+ bool unwind_on_error,
+ bool ignore_breakpoints)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanCallFunction (*this,
+ function,
+ ClangASTType(),
+ arg,
+ stop_other_threads,
+ unwind_on_error,
+ ignore_breakpoints));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForRunToAddress (bool abort_other_plans,
+ Address &target_addr,
+ bool stop_other_threads)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanRunToAddress (*this, target_addr, stop_other_threads));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+}
+
+ThreadPlanSP
+Thread::QueueThreadPlanForStepUntil (bool abort_other_plans,
+ lldb::addr_t *address_list,
+ size_t num_addresses,
+ bool stop_other_threads,
+ uint32_t frame_idx)
+{
+ ThreadPlanSP thread_plan_sp (new ThreadPlanStepUntil (*this, address_list, num_addresses, stop_other_threads, frame_idx));
+ QueueThreadPlan (thread_plan_sp, abort_other_plans);
+ return thread_plan_sp;
+
+}
+
+uint32_t
+Thread::GetIndexID () const
+{
+ return m_index_id;
+}
+
+void
+Thread::DumpThreadPlans (lldb_private::Stream *s) const
+{
+ uint32_t stack_size = m_plan_stack.size();
+ int i;
+ s->Indent();
+ s->Printf ("Plan Stack for thread #%u: tid = 0x%4.4" PRIx64 ", stack_size = %d\n", GetIndexID(), GetID(), stack_size);
+ for (i = stack_size - 1; i >= 0; i--)
+ {
+ s->IndentMore();
+ s->Indent();
+ s->Printf ("Element %d: ", i);
+ m_plan_stack[i]->GetDescription (s, eDescriptionLevelFull);
+ s->EOL();
+ s->IndentLess();
+ }
+
+ stack_size = m_completed_plan_stack.size();
+ if (stack_size > 0)
+ {
+ s->Indent();
+ s->Printf ("Completed Plan Stack: %d elements.\n", stack_size);
+ for (i = stack_size - 1; i >= 0; i--)
+ {
+ s->IndentMore();
+ s->Indent();
+ s->Printf ("Element %d: ", i);
+ m_completed_plan_stack[i]->GetDescription (s, eDescriptionLevelFull);
+ s->EOL();
+ s->IndentLess();
+ }
+ }
+
+ stack_size = m_discarded_plan_stack.size();
+ if (stack_size > 0)
+ {
+ s->Indent();
+ s->Printf ("Discarded Plan Stack: %d elements.\n", stack_size);
+ for (i = stack_size - 1; i >= 0; i--)
+ {
+ s->IndentMore();
+ s->Indent();
+ s->Printf ("Element %d: ", i);
+ m_discarded_plan_stack[i]->GetDescription (s, eDescriptionLevelFull);
+ s->EOL();
+ s->IndentLess();
+ }
+ }
+
+}
+
+TargetSP
+Thread::CalculateTarget ()
+{
+ TargetSP target_sp;
+ ProcessSP process_sp(GetProcess());
+ if (process_sp)
+ target_sp = process_sp->CalculateTarget();
+ return target_sp;
+
+}
+
+ProcessSP
+Thread::CalculateProcess ()
+{
+ return GetProcess();
+}
+
+ThreadSP
+Thread::CalculateThread ()
+{
+ return shared_from_this();
+}
+
+StackFrameSP
+Thread::CalculateStackFrame ()
+{
+ return StackFrameSP();
+}
+
+void
+Thread::CalculateExecutionContext (ExecutionContext &exe_ctx)
+{
+ exe_ctx.SetContext (shared_from_this());
+}
+
+
+StackFrameListSP
+Thread::GetStackFrameList ()
+{
+ StackFrameListSP frame_list_sp;
+ Mutex::Locker locker(m_frame_mutex);
+ if (m_curr_frames_sp)
+ {
+ frame_list_sp = m_curr_frames_sp;
+ }
+ else
+ {
+ frame_list_sp.reset(new StackFrameList (*this, m_prev_frames_sp, true));
+ m_curr_frames_sp = frame_list_sp;
+ }
+ return frame_list_sp;
+}
+
+void
+Thread::ClearStackFrames ()
+{
+ Mutex::Locker locker(m_frame_mutex);
+
+ Unwind *unwinder = GetUnwinder ();
+ if (unwinder)
+ unwinder->Clear();
+
+ // Only store away the old "reference" StackFrameList if we got all its frames:
+ // FIXME: At some point we can try to splice in the frames we have fetched into
+ // the new frame as we make it, but let's not try that now.
+ if (m_curr_frames_sp && m_curr_frames_sp->GetAllFramesFetched())
+ m_prev_frames_sp.swap (m_curr_frames_sp);
+ m_curr_frames_sp.reset();
+}
+
+lldb::StackFrameSP
+Thread::GetFrameWithConcreteFrameIndex (uint32_t unwind_idx)
+{
+ return GetStackFrameList()->GetFrameWithConcreteFrameIndex (unwind_idx);
+}
+
+
+Error
+Thread::ReturnFromFrameWithIndex (uint32_t frame_idx, lldb::ValueObjectSP return_value_sp, bool broadcast)
+{
+ StackFrameSP frame_sp = GetStackFrameAtIndex (frame_idx);
+ Error return_error;
+
+ if (!frame_sp)
+ {
+ return_error.SetErrorStringWithFormat("Could not find frame with index %d in thread 0x%" PRIx64 ".", frame_idx, GetID());
+ }
+
+ return ReturnFromFrame(frame_sp, return_value_sp, broadcast);
+}
+
+Error
+Thread::ReturnFromFrame (lldb::StackFrameSP frame_sp, lldb::ValueObjectSP return_value_sp, bool broadcast)
+{
+ Error return_error;
+
+ if (!frame_sp)
+ {
+ return_error.SetErrorString("Can't return to a null frame.");
+ return return_error;
+ }
+
+ Thread *thread = frame_sp->GetThread().get();
+ uint32_t older_frame_idx = frame_sp->GetFrameIndex() + 1;
+ StackFrameSP older_frame_sp = thread->GetStackFrameAtIndex(older_frame_idx);
+ if (!older_frame_sp)
+ {
+ return_error.SetErrorString("No older frame to return to.");
+ return return_error;
+ }
+
+ if (return_value_sp)
+ {
+ lldb::ABISP abi = thread->GetProcess()->GetABI();
+ if (!abi)
+ {
+ return_error.SetErrorString("Could not find ABI to set return value.");
+ return return_error;
+ }
+ SymbolContext sc = frame_sp->GetSymbolContext(eSymbolContextFunction);
+
+ // FIXME: ValueObject::Cast doesn't currently work correctly, at least not for scalars.
+ // Turn that back on when that works.
+ if (0 && sc.function != NULL)
+ {
+ Type *function_type = sc.function->GetType();
+ if (function_type)
+ {
+ ClangASTType return_type = sc.function->GetClangType().GetFunctionReturnType();
+ if (return_type)
+ {
+ StreamString s;
+ return_type.DumpTypeDescription(&s);
+ ValueObjectSP cast_value_sp = return_value_sp->Cast(return_type);
+ if (cast_value_sp)
+ {
+ cast_value_sp->SetFormat(eFormatHex);
+ return_value_sp = cast_value_sp;
+ }
+ }
+ }
+ }
+
+ return_error = abi->SetReturnValueObject(older_frame_sp, return_value_sp);
+ if (!return_error.Success())
+ return return_error;
+ }
+
+ // Now write the return registers for the chosen frame:
+ // Note, we can't use ReadAllRegisterValues->WriteAllRegisterValues, since the read & write
+ // cook their data
+
+ StackFrameSP youngest_frame_sp = thread->GetStackFrameAtIndex(0);
+ if (youngest_frame_sp)
+ {
+ lldb::RegisterContextSP reg_ctx_sp (youngest_frame_sp->GetRegisterContext());
+ if (reg_ctx_sp)
+ {
+ bool copy_success = reg_ctx_sp->CopyFromRegisterContext(older_frame_sp->GetRegisterContext());
+ if (copy_success)
+ {
+ thread->DiscardThreadPlans(true);
+ thread->ClearStackFrames();
+ if (broadcast && EventTypeHasListeners(eBroadcastBitStackChanged))
+ BroadcastEvent(eBroadcastBitStackChanged, new ThreadEventData (this->shared_from_this()));
+ }
+ else
+ {
+ return_error.SetErrorString("Could not reset register values.");
+ }
+ }
+ else
+ {
+ return_error.SetErrorString("Frame has no register context.");
+ }
+ }
+ else
+ {
+ return_error.SetErrorString("Returned past top frame.");
+ }
+ return return_error;
+}
+
+void
+Thread::DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx)
+{
+ ExecutionContext exe_ctx (shared_from_this());
+ Process *process = exe_ctx.GetProcessPtr();
+ if (process == NULL)
+ return;
+
+ StackFrameSP frame_sp;
+ SymbolContext frame_sc;
+ if (frame_idx != LLDB_INVALID_INDEX32)
+ {
+ frame_sp = GetStackFrameAtIndex (frame_idx);
+ if (frame_sp)
+ {
+ exe_ctx.SetFrameSP(frame_sp);
+ frame_sc = frame_sp->GetSymbolContext(eSymbolContextEverything);
+ }
+ }
+
+ const char *thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat();
+ assert (thread_format);
+ Debugger::FormatPrompt (thread_format,
+ frame_sp ? &frame_sc : NULL,
+ &exe_ctx,
+ NULL,
+ strm);
+}
+
+void
+Thread::SettingsInitialize ()
+{
+}
+
+void
+Thread::SettingsTerminate ()
+{
+}
+
+lldb::StackFrameSP
+Thread::GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr)
+{
+ return GetStackFrameList()->GetStackFrameSPForStackFramePtr (stack_frame_ptr);
+}
+
+const char *
+Thread::StopReasonAsCString (lldb::StopReason reason)
+{
+ switch (reason)
+ {
+ case eStopReasonInvalid: return "invalid";
+ case eStopReasonNone: return "none";
+ case eStopReasonTrace: return "trace";
+ case eStopReasonBreakpoint: return "breakpoint";
+ case eStopReasonWatchpoint: return "watchpoint";
+ case eStopReasonSignal: return "signal";
+ case eStopReasonException: return "exception";
+ case eStopReasonExec: return "exec";
+ case eStopReasonPlanComplete: return "plan complete";
+ case eStopReasonThreadExiting: return "thread exiting";
+ }
+
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof (unknown_state_string), "StopReason = %i", reason);
+ return unknown_state_string;
+}
+
+const char *
+Thread::RunModeAsCString (lldb::RunMode mode)
+{
+ switch (mode)
+ {
+ case eOnlyThisThread: return "only this thread";
+ case eAllThreads: return "all threads";
+ case eOnlyDuringStepping: return "only during stepping";
+ }
+
+ static char unknown_state_string[64];
+ snprintf(unknown_state_string, sizeof (unknown_state_string), "RunMode = %i", mode);
+ return unknown_state_string;
+}
+
+size_t
+Thread::GetStatus (Stream &strm, uint32_t start_frame, uint32_t num_frames, uint32_t num_frames_with_source)
+{
+ ExecutionContext exe_ctx (shared_from_this());
+ Target *target = exe_ctx.GetTargetPtr();
+ Process *process = exe_ctx.GetProcessPtr();
+ size_t num_frames_shown = 0;
+ strm.Indent();
+ bool is_selected = false;
+ if (process)
+ {
+ if (process->GetThreadList().GetSelectedThread().get() == this)
+ is_selected = true;
+ }
+ strm.Printf("%c ", is_selected ? '*' : ' ');
+ if (target && target->GetDebugger().GetUseExternalEditor())
+ {
+ StackFrameSP frame_sp = GetStackFrameAtIndex(start_frame);
+ if (frame_sp)
+ {
+ SymbolContext frame_sc(frame_sp->GetSymbolContext (eSymbolContextLineEntry));
+ if (frame_sc.line_entry.line != 0 && frame_sc.line_entry.file)
+ {
+ Host::OpenFileInExternalEditor (frame_sc.line_entry.file, frame_sc.line_entry.line);
+ }
+ }
+ }
+
+ DumpUsingSettingsFormat (strm, start_frame);
+
+ if (num_frames > 0)
+ {
+ strm.IndentMore();
+
+ const bool show_frame_info = true;
+ strm.IndentMore ();
+ num_frames_shown = GetStackFrameList ()->GetStatus (strm,
+ start_frame,
+ num_frames,
+ show_frame_info,
+ num_frames_with_source);
+ strm.IndentLess();
+ strm.IndentLess();
+ }
+ return num_frames_shown;
+}
+
+size_t
+Thread::GetStackFrameStatus (Stream& strm,
+ uint32_t first_frame,
+ uint32_t num_frames,
+ bool show_frame_info,
+ uint32_t num_frames_with_source)
+{
+ return GetStackFrameList()->GetStatus (strm,
+ first_frame,
+ num_frames,
+ show_frame_info,
+ num_frames_with_source);
+}
+
+bool
+Thread::SaveFrameZeroState (RegisterCheckpoint &checkpoint)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ checkpoint.SetStackID(frame_sp->GetStackID());
+ lldb::RegisterContextSP reg_ctx_sp (frame_sp->GetRegisterContext());
+ if (reg_ctx_sp)
+ return reg_ctx_sp->ReadAllRegisterValues (checkpoint.GetData());
+ }
+ return false;
+}
+
+bool
+Thread::RestoreSaveFrameZero (const RegisterCheckpoint &checkpoint)
+{
+ return ResetFrameZeroRegisters (checkpoint.GetData());
+}
+
+bool
+Thread::ResetFrameZeroRegisters (lldb::DataBufferSP register_data_sp)
+{
+ lldb::StackFrameSP frame_sp(GetStackFrameAtIndex (0));
+ if (frame_sp)
+ {
+ lldb::RegisterContextSP reg_ctx_sp (frame_sp->GetRegisterContext());
+ if (reg_ctx_sp)
+ {
+ bool ret = reg_ctx_sp->WriteAllRegisterValues (register_data_sp);
+
+ // Clear out all stack frames as our world just changed.
+ ClearStackFrames();
+ reg_ctx_sp->InvalidateIfNeeded(true);
+ if (m_unwinder_ap.get())
+ m_unwinder_ap->Clear();
+ return ret;
+ }
+ }
+ return false;
+}
+
+Unwind *
+Thread::GetUnwinder ()
+{
+ if (m_unwinder_ap.get() == NULL)
+ {
+ const ArchSpec target_arch (CalculateTarget()->GetArchitecture ());
+ const llvm::Triple::ArchType machine = target_arch.GetMachine();
+ switch (machine)
+ {
+ case llvm::Triple::x86_64:
+ case llvm::Triple::x86:
+ case llvm::Triple::arm:
+ case llvm::Triple::thumb:
+ m_unwinder_ap.reset (new UnwindLLDB (*this));
+ break;
+
+ default:
+ if (target_arch.GetTriple().getVendor() == llvm::Triple::Apple)
+ m_unwinder_ap.reset (new UnwindMacOSXFrameBackchain (*this));
+ break;
+ }
+ }
+ return m_unwinder_ap.get();
+}
+
+
+void
+Thread::Flush ()
+{
+ ClearStackFrames ();
+ m_reg_context_sp.reset();
+}
+
+bool
+Thread::IsStillAtLastBreakpointHit ()
+{
+ // If we are currently stopped at a breakpoint, always return that stopinfo and don't reset it.
+ // This allows threads to maintain their breakpoint stopinfo, such as when thread-stepping in
+ // multithreaded programs.
+ if (m_stop_info_sp) {
+ StopReason stop_reason = m_stop_info_sp->GetStopReason();
+ if (stop_reason == lldb::eStopReasonBreakpoint) {
+ uint64_t value = m_stop_info_sp->GetValue();
+ lldb::RegisterContextSP reg_ctx_sp (GetRegisterContext());
+ if (reg_ctx_sp)
+ {
+ lldb::addr_t pc = reg_ctx_sp->GetPC();
+ BreakpointSiteSP bp_site_sp = GetProcess()->GetBreakpointSiteList().FindByAddress(pc);
+ if (bp_site_sp && value == bp_site_sp->GetID())
+ return true;
+ }
+ }
+ }
+ return false;
+}
diff --git a/source/Target/ThreadList.cpp b/source/Target/ThreadList.cpp
new file mode 100644
index 000000000000..1f8b351100a6
--- /dev/null
+++ b/source/Target/ThreadList.cpp
@@ -0,0 +1,785 @@
+//===-- ThreadList.cpp ------------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/ThreadList.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/Process.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadList::ThreadList (Process *process) :
+ m_process (process),
+ m_stop_id (0),
+ m_threads(),
+ m_selected_tid (LLDB_INVALID_THREAD_ID)
+{
+}
+
+ThreadList::ThreadList (const ThreadList &rhs) :
+ m_process (rhs.m_process),
+ m_stop_id (rhs.m_stop_id),
+ m_threads (),
+ m_selected_tid ()
+{
+ // Use the assignment operator since it uses the mutex
+ *this = rhs;
+}
+
+const ThreadList&
+ThreadList::operator = (const ThreadList& rhs)
+{
+ if (this != &rhs)
+ {
+ // Lock both mutexes to make sure neither side changes anyone on us
+ // while the assignement occurs
+ Mutex::Locker locker(GetMutex());
+ m_process = rhs.m_process;
+ m_stop_id = rhs.m_stop_id;
+ m_threads = rhs.m_threads;
+ m_selected_tid = rhs.m_selected_tid;
+ }
+ return *this;
+}
+
+
+ThreadList::~ThreadList()
+{
+ // Clear the thread list. Clear will take the mutex lock
+ // which will ensure that if anyone is using the list
+ // they won't get it removed while using it.
+ Clear();
+}
+
+
+uint32_t
+ThreadList::GetStopID () const
+{
+ return m_stop_id;
+}
+
+void
+ThreadList::SetStopID (uint32_t stop_id)
+{
+ m_stop_id = stop_id;
+}
+
+
+void
+ThreadList::AddThread (const ThreadSP &thread_sp)
+{
+ Mutex::Locker locker(GetMutex());
+ m_threads.push_back(thread_sp);
+}
+
+uint32_t
+ThreadList::GetSize (bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+ return m_threads.size();
+}
+
+ThreadSP
+ThreadList::GetThreadAtIndex (uint32_t idx, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ if (idx < m_threads.size())
+ thread_sp = m_threads[idx];
+ return thread_sp;
+}
+
+ThreadSP
+ThreadList::FindThreadByID (lldb::tid_t tid, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetID() == tid)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP
+ThreadList::FindThreadByProtocolID (lldb::tid_t tid, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetProtocolID() == tid)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+
+ThreadSP
+ThreadList::RemoveThreadByID (lldb::tid_t tid, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetID() == tid)
+ {
+ thread_sp = m_threads[idx];
+ m_threads.erase(m_threads.begin()+idx);
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP
+ThreadList::RemoveThreadByProtocolID (lldb::tid_t tid, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetProtocolID() == tid)
+ {
+ thread_sp = m_threads[idx];
+ m_threads.erase(m_threads.begin()+idx);
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+ThreadSP
+ThreadList::GetThreadSPForThreadPtr (Thread *thread_ptr)
+{
+ ThreadSP thread_sp;
+ if (thread_ptr)
+ {
+ Mutex::Locker locker(GetMutex());
+
+ uint32_t idx = 0;
+ const uint32_t num_threads = m_threads.size();
+ for (idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx].get() == thread_ptr)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ }
+ return thread_sp;
+}
+
+
+
+ThreadSP
+ThreadList::FindThreadByIndexID (uint32_t index_id, bool can_update)
+{
+ Mutex::Locker locker(GetMutex());
+
+ if (can_update)
+ m_process->UpdateThreadListIfNeeded();
+
+ ThreadSP thread_sp;
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetIndexID() == index_id)
+ {
+ thread_sp = m_threads[idx];
+ break;
+ }
+ }
+ return thread_sp;
+}
+
+bool
+ThreadList::ShouldStop (Event *event_ptr)
+{
+ // Running events should never stop, obviously...
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ // The ShouldStop method of the threads can do a whole lot of work,
+ // running breakpoint commands & conditions, etc. So we don't want
+ // to keep the ThreadList locked the whole time we are doing this.
+ // FIXME: It is possible that running code could cause new threads
+ // to be created. If that happens we will miss asking them whether
+ // then should stop. This is not a big deal, since we haven't had
+ // a chance to hang any interesting operations on those threads yet.
+
+ collection threads_copy;
+ {
+ // Scope for locker
+ Mutex::Locker locker(GetMutex());
+
+ m_process->UpdateThreadListIfNeeded();
+ threads_copy = m_threads;
+ }
+
+ collection::iterator pos, end = threads_copy.end();
+
+ if (log)
+ {
+ log->PutCString("");
+ log->Printf ("ThreadList::%s: %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size());
+ }
+
+ bool did_anybody_stop_for_a_reason = false;
+ bool should_stop = false;
+
+ // Now we run through all the threads and get their stop info's. We want to make sure to do this first before
+ // we start running the ShouldStop, because one thread's ShouldStop could destroy information (like deleting a
+ // thread specific breakpoint another thread had stopped at) which could lead us to compute the StopInfo incorrectly.
+ // We don't need to use it here, we just want to make sure it gets computed.
+
+ for (pos = threads_copy.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ thread_sp->GetStopInfo();
+ }
+
+ for (pos = threads_copy.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+
+ did_anybody_stop_for_a_reason |= thread_sp->ThreadStoppedForAReason();
+
+ const bool thread_should_stop = thread_sp->ShouldStop(event_ptr);
+ if (thread_should_stop)
+ should_stop |= true;
+ }
+
+ // We should never get a stop for which no thread had a stop reason, but sometimes we do see this -
+ // for instance when we first connect to a remote stub. In that case we should stop, since we can't figure out
+ // the right thing to do and stopping gives the user control over what to do in this instance.
+
+ if (!should_stop && !did_anybody_stop_for_a_reason)
+ {
+ should_stop = true;
+ if (log)
+ log->Printf ("ThreadList::%s we stopped but no threads had a stop reason, overriding should_stop and stopping.", __FUNCTION__);
+ }
+
+ if (log)
+ log->Printf ("ThreadList::%s overall should_stop = %i", __FUNCTION__, should_stop);
+
+ if (should_stop)
+ {
+ for (pos = threads_copy.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ thread_sp->WillStop ();
+ }
+ }
+
+ return should_stop;
+}
+
+Vote
+ThreadList::ShouldReportStop (Event *event_ptr)
+{
+ Mutex::Locker locker(GetMutex());
+
+ Vote result = eVoteNoOpinion;
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (log)
+ log->Printf ("ThreadList::%s %" PRIu64 " threads", __FUNCTION__, (uint64_t)m_threads.size());
+
+ // Run through the threads and ask whether we should report this event.
+ // For stopping, a YES vote wins over everything. A NO vote wins over NO opinion.
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ const Vote vote = thread_sp->ShouldReportStop (event_ptr);
+ switch (vote)
+ {
+ case eVoteNoOpinion:
+ continue;
+
+ case eVoteYes:
+ result = eVoteYes;
+ break;
+
+ case eVoteNo:
+ if (result == eVoteNoOpinion)
+ {
+ result = eVoteNo;
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ThreadList::%s thread 0x%4.4" PRIx64 ": voted %s, but lost out because result was %s",
+ __FUNCTION__,
+ thread_sp->GetID (),
+ GetVoteAsCString (vote),
+ GetVoteAsCString (result));
+ }
+ break;
+ }
+ }
+ if (log)
+ log->Printf ("ThreadList::%s returning %s", __FUNCTION__, GetVoteAsCString (result));
+ return result;
+}
+
+void
+ThreadList::SetShouldReportStop (Vote vote)
+{
+ Mutex::Locker locker(GetMutex());
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ thread_sp->SetShouldReportStop (vote);
+ }
+}
+
+Vote
+ThreadList::ShouldReportRun (Event *event_ptr)
+{
+
+ Mutex::Locker locker(GetMutex());
+
+ Vote result = eVoteNoOpinion;
+ m_process->UpdateThreadListIfNeeded();
+ collection::iterator pos, end = m_threads.end();
+
+ // Run through the threads and ask whether we should report this event.
+ // The rule is NO vote wins over everything, a YES vote wins over no opinion.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ if ((*pos)->GetResumeState () != eStateSuspended)
+ {
+ switch ((*pos)->ShouldReportRun (event_ptr))
+ {
+ case eVoteNoOpinion:
+ continue;
+ case eVoteYes:
+ if (result == eVoteNoOpinion)
+ result = eVoteYes;
+ break;
+ case eVoteNo:
+ if (log)
+ log->Printf ("ThreadList::ShouldReportRun() thread %d (0x%4.4" PRIx64 ") says don't report.",
+ (*pos)->GetIndexID(),
+ (*pos)->GetID());
+ result = eVoteNo;
+ break;
+ }
+ }
+ }
+ return result;
+}
+
+void
+ThreadList::Clear()
+{
+ Mutex::Locker locker(GetMutex());
+ m_stop_id = 0;
+ m_threads.clear();
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+}
+
+void
+ThreadList::Destroy()
+{
+ Mutex::Locker locker(GetMutex());
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ m_threads[idx]->DestroyThread();
+ }
+}
+
+void
+ThreadList::RefreshStateAfterStop ()
+{
+ Mutex::Locker locker(GetMutex());
+
+ m_process->UpdateThreadListIfNeeded();
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("Turning off notification of new threads while single stepping a thread.");
+
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->RefreshStateAfterStop ();
+}
+
+void
+ThreadList::DiscardThreadPlans ()
+{
+ // You don't need to update the thread list here, because only threads
+ // that you currently know about have any thread plans.
+ Mutex::Locker locker(GetMutex());
+
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->DiscardThreadPlans (true);
+
+}
+
+bool
+ThreadList::WillResume ()
+{
+ // Run through the threads and perform their momentary actions.
+ // But we only do this for threads that are running, user suspended
+ // threads stay where they are.
+
+ Mutex::Locker locker(GetMutex());
+ m_process->UpdateThreadListIfNeeded();
+
+ collection::iterator pos, end = m_threads.end();
+
+ // See if any thread wants to run stopping others. If it does, then we won't
+ // setup the other threads for resume, since they aren't going to get a chance
+ // to run. This is necessary because the SetupForResume might add "StopOthers"
+ // plans which would then get to be part of the who-gets-to-run negotiation, but
+ // they're coming in after the fact, and the threads that are already set up should
+ // take priority.
+
+ bool wants_solo_run = false;
+
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ if ((*pos)->GetResumeState() != eStateSuspended &&
+ (*pos)->GetCurrentPlan()->StopOthers())
+ {
+ if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
+ continue;
+ wants_solo_run = true;
+ break;
+ }
+ }
+
+ if (wants_solo_run)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("Turning on notification of new threads while single stepping a thread.");
+ m_process->StartNoticingNewThreads();
+ }
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log && log->GetVerbose())
+ log->Printf ("Turning off notification of new threads while single stepping a thread.");
+ m_process->StopNoticingNewThreads();
+ }
+
+ // Give all the threads that are likely to run a last chance to set up their state before we
+ // negotiate who is actually going to get a chance to run...
+ // Don't set to resume suspended threads, and if any thread wanted to stop others, only
+ // call setup on the threads that request StopOthers...
+
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ if ((*pos)->GetResumeState() != eStateSuspended
+ && (!wants_solo_run || (*pos)->GetCurrentPlan()->StopOthers()))
+ {
+ if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
+ continue;
+ (*pos)->SetupForResume ();
+ }
+ }
+
+ // Now go through the threads and see if any thread wants to run just itself.
+ // if so then pick one and run it.
+
+ ThreadList run_me_only_list (m_process);
+
+ run_me_only_list.SetStopID(m_process->GetStopID());
+
+ bool run_only_current_thread = false;
+
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ if (thread_sp->GetResumeState() != eStateSuspended &&
+ thread_sp->GetCurrentPlan()->StopOthers())
+ {
+ if ((*pos)->IsOperatingSystemPluginThread() && !(*pos)->GetBackingThread())
+ continue;
+
+ // You can't say "stop others" and also want yourself to be suspended.
+ assert (thread_sp->GetCurrentPlan()->RunState() != eStateSuspended);
+
+ if (thread_sp == GetSelectedThread())
+ {
+ run_only_current_thread = true;
+ run_me_only_list.Clear();
+ run_me_only_list.AddThread (thread_sp);
+ break;
+ }
+
+ run_me_only_list.AddThread (thread_sp);
+ }
+
+ }
+
+ bool need_to_resume = true;
+
+ if (run_me_only_list.GetSize (false) == 0)
+ {
+ // Everybody runs as they wish:
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ StateType run_state;
+ if (thread_sp->GetResumeState() != eStateSuspended)
+ run_state = thread_sp->GetCurrentPlan()->RunState();
+ else
+ run_state = eStateSuspended;
+ if (!thread_sp->ShouldResume(run_state))
+ need_to_resume = false;
+ }
+ }
+ else
+ {
+ ThreadSP thread_to_run;
+
+ if (run_only_current_thread)
+ {
+ thread_to_run = GetSelectedThread();
+ }
+ else if (run_me_only_list.GetSize (false) == 1)
+ {
+ thread_to_run = run_me_only_list.GetThreadAtIndex (0);
+ }
+ else
+ {
+ int random_thread = (int)
+ ((run_me_only_list.GetSize (false) * (double) rand ()) / (RAND_MAX + 1.0));
+ thread_to_run = run_me_only_list.GetThreadAtIndex (random_thread);
+ }
+
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ ThreadSP thread_sp(*pos);
+ if (thread_sp == thread_to_run)
+ {
+ if (!thread_sp->ShouldResume(thread_sp->GetCurrentPlan()->RunState()))
+ need_to_resume = false;
+ }
+ else
+ thread_sp->ShouldResume (eStateSuspended);
+ }
+ }
+
+ return need_to_resume;
+}
+
+void
+ThreadList::DidResume ()
+{
+ Mutex::Locker locker(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ // Don't clear out threads that aren't going to get a chance to run, rather
+ // leave their state for the next time around.
+ ThreadSP thread_sp(*pos);
+ if (thread_sp->GetResumeState() != eStateSuspended)
+ thread_sp->DidResume ();
+ }
+}
+
+void
+ThreadList::DidStop ()
+{
+ Mutex::Locker locker(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ {
+ // Notify threads that the process just stopped.
+ // Note, this currently assumes that all threads in the list
+ // stop when the process stops. In the future we will want to support
+ // a debugging model where some threads continue to run while others
+ // are stopped. We either need to handle that somehow here or
+ // create a special thread list containing only threads which will
+ // stop in the code that calls this method (currently
+ // Process::SetPrivateState).
+ ThreadSP thread_sp(*pos);
+ if (StateIsRunningState(thread_sp->GetState()))
+ thread_sp->DidStop ();
+ }
+}
+
+ThreadSP
+ThreadList::GetSelectedThread ()
+{
+ Mutex::Locker locker(GetMutex());
+ ThreadSP thread_sp = FindThreadByID(m_selected_tid);
+ if (!thread_sp.get())
+ {
+ if (m_threads.size() == 0)
+ return thread_sp;
+ m_selected_tid = m_threads[0]->GetID();
+ thread_sp = m_threads[0];
+ }
+ return thread_sp;
+}
+
+bool
+ThreadList::SetSelectedThreadByID (lldb::tid_t tid, bool notify)
+{
+ Mutex::Locker locker(GetMutex());
+ ThreadSP selected_thread_sp(FindThreadByID(tid));
+ if (selected_thread_sp)
+ {
+ m_selected_tid = tid;
+ selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
+ }
+ else
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+
+ if (notify)
+ NotifySelectedThreadChanged(m_selected_tid);
+
+ return m_selected_tid != LLDB_INVALID_THREAD_ID;
+}
+
+bool
+ThreadList::SetSelectedThreadByIndexID (uint32_t index_id, bool notify)
+{
+ Mutex::Locker locker(GetMutex());
+ ThreadSP selected_thread_sp (FindThreadByIndexID(index_id));
+ if (selected_thread_sp.get())
+ {
+ m_selected_tid = selected_thread_sp->GetID();
+ selected_thread_sp->SetDefaultFileAndLineToSelectedFrame();
+ }
+ else
+ m_selected_tid = LLDB_INVALID_THREAD_ID;
+
+ if (notify)
+ NotifySelectedThreadChanged(m_selected_tid);
+
+ return m_selected_tid != LLDB_INVALID_THREAD_ID;
+}
+
+void
+ThreadList::NotifySelectedThreadChanged (lldb::tid_t tid)
+{
+ ThreadSP selected_thread_sp (FindThreadByID(tid));
+ if (selected_thread_sp->EventTypeHasListeners(Thread::eBroadcastBitThreadSelected))
+ selected_thread_sp->BroadcastEvent(Thread::eBroadcastBitThreadSelected,
+ new Thread::ThreadEventData(selected_thread_sp));
+}
+
+void
+ThreadList::Update (ThreadList &rhs)
+{
+ if (this != &rhs)
+ {
+ // Lock both mutexes to make sure neither side changes anyone on us
+ // while the assignement occurs
+ Mutex::Locker locker(GetMutex());
+ m_process = rhs.m_process;
+ m_stop_id = rhs.m_stop_id;
+ m_threads.swap(rhs.m_threads);
+ m_selected_tid = rhs.m_selected_tid;
+
+
+ // Now we look for threads that we are done with and
+ // make sure to clear them up as much as possible so
+ // anyone with a shared pointer will still have a reference,
+ // but the thread won't be of much use. Using std::weak_ptr
+ // for all backward references (such as a thread to a process)
+ // will eventually solve this issue for us, but for now, we
+ // need to work around the issue
+ collection::iterator rhs_pos, rhs_end = rhs.m_threads.end();
+ for (rhs_pos = rhs.m_threads.begin(); rhs_pos != rhs_end; ++rhs_pos)
+ {
+ const lldb::tid_t tid = (*rhs_pos)->GetID();
+ bool thread_is_alive = false;
+ const uint32_t num_threads = m_threads.size();
+ for (uint32_t idx = 0; idx < num_threads; ++idx)
+ {
+ if (m_threads[idx]->GetID() == tid)
+ {
+ thread_is_alive = true;
+ break;
+ }
+ }
+ if (!thread_is_alive)
+ (*rhs_pos)->DestroyThread();
+ }
+ }
+}
+
+void
+ThreadList::Flush ()
+{
+ Mutex::Locker locker(GetMutex());
+ collection::iterator pos, end = m_threads.end();
+ for (pos = m_threads.begin(); pos != end; ++pos)
+ (*pos)->Flush ();
+}
+
+Mutex &
+ThreadList::GetMutex ()
+{
+ return m_process->m_thread_mutex;
+}
+
diff --git a/source/Target/ThreadPlan.cpp b/source/Target/ThreadPlan.cpp
new file mode 100644
index 000000000000..65d51bd01734
--- /dev/null
+++ b/source/Target/ThreadPlan.cpp
@@ -0,0 +1,353 @@
+//===-- ThreadPlan.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/lldb-python.h"
+
+#include "lldb/Target/ThreadPlan.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/State.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlan constructor
+//----------------------------------------------------------------------
+ThreadPlan::ThreadPlan(ThreadPlanKind kind, const char *name, Thread &thread, Vote stop_vote, Vote run_vote) :
+ m_thread (thread),
+ m_stop_vote (stop_vote),
+ m_run_vote (run_vote),
+ m_kind (kind),
+ m_name (name),
+ m_plan_complete_mutex (Mutex::eMutexTypeRecursive),
+ m_cached_plan_explains_stop (eLazyBoolCalculate),
+ m_plan_complete (false),
+ m_plan_private (false),
+ m_okay_to_discard (true),
+ m_is_master_plan (false),
+ m_plan_succeeded(true)
+{
+ SetID (GetNextID());
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ThreadPlan::~ThreadPlan()
+{
+}
+
+bool
+ThreadPlan::PlanExplainsStop (Event *event_ptr)
+{
+ if (m_cached_plan_explains_stop == eLazyBoolCalculate)
+ {
+ bool actual_value = DoPlanExplainsStop(event_ptr);
+ m_cached_plan_explains_stop = actual_value ? eLazyBoolYes : eLazyBoolNo;
+ return actual_value;
+ }
+ else
+ {
+ return m_cached_plan_explains_stop == eLazyBoolYes;
+ }
+}
+
+bool
+ThreadPlan::IsPlanComplete ()
+{
+ Mutex::Locker locker(m_plan_complete_mutex);
+ return m_plan_complete;
+}
+
+void
+ThreadPlan::SetPlanComplete (bool success)
+{
+ Mutex::Locker locker(m_plan_complete_mutex);
+ m_plan_complete = true;
+ m_plan_succeeded = success;
+}
+
+bool
+ThreadPlan::MischiefManaged ()
+{
+ Mutex::Locker locker(m_plan_complete_mutex);
+ // Mark the plan is complete, but don't override the success flag.
+ m_plan_complete = true;
+ return true;
+}
+
+Vote
+ThreadPlan::ShouldReportStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (m_stop_vote == eVoteNoOpinion)
+ {
+ ThreadPlan *prev_plan = GetPreviousPlan ();
+ if (prev_plan)
+ {
+ Vote prev_vote = prev_plan->ShouldReportStop (event_ptr);
+ if (log)
+ log->Printf ("ThreadPlan::ShouldReportStop() returning previous thread plan vote: %s",
+ GetVoteAsCString (prev_vote));
+ return prev_vote;
+ }
+ }
+ if (log)
+ log->Printf ("ThreadPlan::ShouldReportStop() returning vote: %s", GetVoteAsCString (m_stop_vote));
+ return m_stop_vote;
+}
+
+Vote
+ThreadPlan::ShouldReportRun (Event *event_ptr)
+{
+ if (m_run_vote == eVoteNoOpinion)
+ {
+ ThreadPlan *prev_plan = GetPreviousPlan ();
+ if (prev_plan)
+ return prev_plan->ShouldReportRun (event_ptr);
+ }
+ return m_run_vote;
+}
+
+bool
+ThreadPlan::StopOthers ()
+{
+ ThreadPlan *prev_plan;
+ prev_plan = GetPreviousPlan ();
+ if (prev_plan == NULL)
+ return false;
+ else
+ return prev_plan->StopOthers();
+}
+
+void
+ThreadPlan::SetStopOthers (bool new_value)
+{
+ // SetStopOthers doesn't work up the hierarchy. You have to set the
+ // explicit ThreadPlan you want to affect.
+}
+
+bool
+ThreadPlan::WillResume (StateType resume_state, bool current_plan)
+{
+ m_cached_plan_explains_stop = eLazyBoolCalculate;
+
+ if (current_plan)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (log)
+ {
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+ addr_t pc = reg_ctx->GetPC();
+ addr_t sp = reg_ctx->GetSP();
+ addr_t fp = reg_ctx->GetFP();
+ log->Printf("%s Thread #%u (0x%p): tid = 0x%4.4" PRIx64 ", pc = 0x%8.8" PRIx64 ", sp = 0x%8.8" PRIx64 ", fp = 0x%8.8" PRIx64 ", "
+ "plan = '%s', state = %s, stop others = %d",
+ __FUNCTION__,
+ m_thread.GetIndexID(),
+ &m_thread,
+ m_thread.GetID(),
+ (uint64_t)pc,
+ (uint64_t)sp,
+ (uint64_t)fp,
+ m_name.c_str(),
+ StateAsCString(resume_state),
+ StopOthers());
+ }
+ }
+ return DoWillResume (resume_state, current_plan);
+}
+
+lldb::user_id_t
+ThreadPlan::GetNextID()
+{
+ static uint32_t g_nextPlanID = 0;
+ return ++g_nextPlanID;
+}
+
+void
+ThreadPlan::DidPush()
+{
+}
+
+void
+ThreadPlan::WillPop()
+{
+}
+
+bool
+ThreadPlan::OkayToDiscard()
+{
+ if (!IsMasterPlan())
+ return true;
+ else
+ return m_okay_to_discard;
+}
+
+lldb::StateType
+ThreadPlan::RunState ()
+{
+ if (m_tracer_sp && m_tracer_sp->TracingEnabled() && m_tracer_sp->SingleStepEnabled())
+ return eStateStepping;
+ else
+ return GetPlanRunState();
+}
+
+//----------------------------------------------------------------------
+// ThreadPlanNull
+//----------------------------------------------------------------------
+
+ThreadPlanNull::ThreadPlanNull (Thread &thread) :
+ ThreadPlan (ThreadPlan::eKindNull,
+ "Null Thread Plan",
+ thread,
+ eVoteNoOpinion,
+ eVoteNoOpinion)
+{
+}
+
+ThreadPlanNull::~ThreadPlanNull ()
+{
+}
+
+void
+ThreadPlanNull::GetDescription (Stream *s,
+ lldb::DescriptionLevel level)
+{
+ s->PutCString("Null thread plan - thread has been destroyed.");
+}
+
+bool
+ThreadPlanNull::ValidatePlan (Stream *error)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool
+ThreadPlanNull::ShouldStop (Event *event_ptr)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool
+ThreadPlanNull::WillStop ()
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+bool
+ThreadPlanNull::DoPlanExplainsStop (Event *event_ptr)
+{
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return true;
+}
+
+// The null plan is never done.
+bool
+ThreadPlanNull::MischiefManaged ()
+{
+ // The null plan is never done.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return false;
+}
+
+lldb::StateType
+ThreadPlanNull::GetPlanRunState ()
+{
+ // Not sure what to return here. This is a dead thread.
+#ifdef LLDB_CONFIGURATION_DEBUG
+ fprintf(stderr, "error: %s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#else
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD));
+ if (log)
+ log->Error("%s called on thread that has been destroyed (tid = 0x%" PRIx64 ", ptid = 0x%" PRIx64 ")",
+ __PRETTY_FUNCTION__,
+ m_thread.GetID(),
+ m_thread.GetProtocolID());
+#endif
+ return eStateRunning;
+}
diff --git a/source/Target/ThreadPlanBase.cpp b/source/Target/ThreadPlanBase.cpp
new file mode 100644
index 000000000000..240f23a0b8f4
--- /dev/null
+++ b/source/Target/ThreadPlanBase.cpp
@@ -0,0 +1,237 @@
+//===-- ThreadPlanBase.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/Target/ThreadPlanBase.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+//
+#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanBase: This one always stops, and never has anything particular
+// to do.
+// FIXME: The "signal handling" policies should probably go here.
+//----------------------------------------------------------------------
+
+ThreadPlanBase::ThreadPlanBase (Thread &thread) :
+ ThreadPlan(ThreadPlan::eKindBase, "base plan", thread, eVoteYes, eVoteNoOpinion)
+{
+ // Set the tracer to a default tracer.
+ // FIXME: need to add a thread settings variable to pix various tracers...
+#define THREAD_PLAN_USE_ASSEMBLY_TRACER 1
+
+#ifdef THREAD_PLAN_USE_ASSEMBLY_TRACER
+ ThreadPlanTracerSP new_tracer_sp (new ThreadPlanAssemblyTracer (m_thread));
+#else
+ ThreadPlanTracerSP new_tracer_sp (new ThreadPlanTracer (m_thread));
+#endif
+ new_tracer_sp->EnableTracing (m_thread.GetTraceEnabledState());
+ SetThreadPlanTracer(new_tracer_sp);
+ SetIsMasterPlan (true);
+}
+
+ThreadPlanBase::~ThreadPlanBase ()
+{
+
+}
+
+void
+ThreadPlanBase::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ s->Printf ("Base thread plan.");
+}
+
+bool
+ThreadPlanBase::ValidatePlan (Stream *error)
+{
+ return true;
+}
+
+bool
+ThreadPlanBase::DoPlanExplainsStop (Event *event_ptr)
+{
+ // The base plan should defer to its tracer, since by default it
+ // always handles the stop.
+ if (TracerExplainsStop())
+ return false;
+ else
+ return true;
+}
+
+Vote
+ThreadPlanBase::ShouldReportStop(Event *event_ptr)
+{
+ StopInfoSP stop_info_sp = m_thread.GetStopInfo ();
+ if (stop_info_sp)
+ {
+ bool should_notify = stop_info_sp->ShouldNotify(event_ptr);
+ if (should_notify)
+ return eVoteYes;
+ else
+ return eVoteNoOpinion;
+ }
+ else
+ return eVoteNoOpinion;
+}
+
+bool
+ThreadPlanBase::ShouldStop (Event *event_ptr)
+{
+ m_stop_vote = eVoteYes;
+ m_run_vote = eVoteYes;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason ();
+ switch (reason)
+ {
+ case eStopReasonInvalid:
+ case eStopReasonNone:
+ // This
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ return false;
+
+ case eStopReasonBreakpoint:
+ case eStopReasonWatchpoint:
+ if (stop_info_sp->ShouldStopSynchronous(event_ptr))
+ {
+ // If we are going to stop for a breakpoint, then unship the other plans
+ // at this point. Don't force the discard, however, so Master plans can stay
+ // in place if they want to.
+ if (log)
+ log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (breakpoint hit.)", m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+ }
+ // If we aren't going to stop at this breakpoint, and it is internal,
+ // don't report this stop or the subsequent running event.
+ // Otherwise we will post the stopped & running, but the stopped event will get marked
+ // with "restarted" so the UI will know to wait and expect the consequent "running".
+ if (stop_info_sp->ShouldNotify (event_ptr))
+ {
+ m_stop_vote = eVoteYes;
+ m_run_vote = eVoteYes;
+ }
+ else
+ {
+ m_stop_vote = eVoteNo;
+ m_run_vote = eVoteNo;
+ }
+ return false;
+
+ // TODO: the break below was missing, was this intentional??? If so
+ // please mention it
+ break;
+
+ case eStopReasonException:
+ // If we crashed, discard thread plans and stop. Don't force the discard, however,
+ // since on rerun the target may clean up this exception and continue normally from there.
+ if (log)
+ log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (exception.)", m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+
+ case eStopReasonExec:
+ // If we crashed, discard thread plans and stop. Don't force the discard, however,
+ // since on rerun the target may clean up this exception and continue normally from there.
+ if (log)
+ log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (exec.)", m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+
+ case eStopReasonThreadExiting:
+ case eStopReasonSignal:
+ if (stop_info_sp->ShouldStop(event_ptr))
+ {
+ if (log)
+ log->Printf("Base plan discarding thread plans for thread tid = 0x%4.4" PRIx64 " (signal.)", m_thread.GetID());
+ m_thread.DiscardThreadPlans(false);
+ return true;
+ }
+ else
+ {
+ // We're not going to stop, but while we are here, let's figure out
+ // whether to report this.
+ if (stop_info_sp->ShouldNotify(event_ptr))
+ m_stop_vote = eVoteYes;
+ else
+ m_stop_vote = eVoteNo;
+ }
+ return false;
+
+ default:
+ return true;
+ }
+
+ }
+ else
+ {
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ }
+
+ // If there's no explicit reason to stop, then we will continue.
+ return false;
+}
+
+bool
+ThreadPlanBase::StopOthers ()
+{
+ return false;
+}
+
+StateType
+ThreadPlanBase::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+bool
+ThreadPlanBase::WillStop ()
+{
+ return true;
+}
+
+bool
+ThreadPlanBase::DoWillResume (lldb::StateType resume_state, bool current_plan)
+{
+ // Reset these to the default values so we don't set them wrong, then not get asked
+ // for a while, then return the wrong answer.
+ m_run_vote = eVoteNoOpinion;
+ m_stop_vote = eVoteNo;
+ return true;
+}
+
+
+// The base plan is never done.
+bool
+ThreadPlanBase::MischiefManaged ()
+{
+ // The base plan is never done.
+ return false;
+}
+
diff --git a/source/Target/ThreadPlanCallFunction.cpp b/source/Target/ThreadPlanCallFunction.cpp
new file mode 100644
index 000000000000..c9baaafffd6a
--- /dev/null
+++ b/source/Target/ThreadPlanCallFunction.cpp
@@ -0,0 +1,614 @@
+//===-- ThreadPlanCallFunction.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/Target/ThreadPlanCallFunction.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "llvm/Support/MachO.h"
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanCallFunction: Plan to call a single function
+//----------------------------------------------------------------------
+bool
+ThreadPlanCallFunction::ConstructorSetup (Thread &thread,
+ ABI *& abi,
+ lldb::addr_t &start_load_addr,
+ lldb::addr_t &function_load_addr)
+{
+ SetIsMasterPlan (true);
+ SetOkayToDiscard (false);
+ SetPrivate (true);
+
+ ProcessSP process_sp (thread.GetProcess());
+ if (!process_sp)
+ return false;
+
+ abi = process_sp->GetABI().get();
+
+ if (!abi)
+ return false;
+
+ TargetSP target_sp (thread.CalculateTarget());
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+
+ SetBreakpoints();
+
+ m_function_sp = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize();
+ // If we can't read memory at the point of the process where we are planning to put our function, we're
+ // not going to get any further...
+ Error error;
+ process_sp->ReadUnsignedIntegerFromMemory(m_function_sp, 4, 0, error);
+ if (!error.Success())
+ {
+ m_constructor_errors.Printf ("Trying to put the stack in unreadable memory at: 0x%" PRIx64 ".", m_function_sp);
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+
+ if (exe_module == NULL)
+ {
+ m_constructor_errors.Printf ("Can't execute code without an executable module.");
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ else
+ {
+ ObjectFile *objectFile = exe_module->GetObjectFile();
+ if (!objectFile)
+ {
+ m_constructor_errors.Printf ("Could not find object file for module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+
+ m_start_addr = objectFile->GetEntryPointAddress();
+ if (!m_start_addr.IsValid())
+ {
+ m_constructor_errors.Printf ("Could not find entry point address for executable module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ }
+
+ start_load_addr = m_start_addr.GetLoadAddress (target_sp.get());
+
+ // Checkpoint the thread state so we can restore it later.
+ if (log && log->GetVerbose())
+ ReportRegisterState ("About to checkpoint thread before function call. Original register state was:");
+
+ if (!thread.CheckpointThreadState (m_stored_thread_state))
+ {
+ m_constructor_errors.Printf ("Setting up ThreadPlanCallFunction, failed to checkpoint thread state.");
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ function_load_addr = m_function_addr.GetLoadAddress (target_sp.get());
+
+ return true;
+}
+
+ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
+ const Address &function,
+ const ClangASTType &return_type,
+ addr_t arg,
+ bool stop_other_threads,
+ bool unwind_on_error,
+ bool ignore_breakpoints,
+ addr_t *this_arg,
+ addr_t *cmd_arg) :
+ ThreadPlan (ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_valid (false),
+ m_stop_other_threads (stop_other_threads),
+ m_function_addr (function),
+ m_function_sp (0),
+ m_return_type (return_type),
+ m_takedown_done (false),
+ m_stop_address (LLDB_INVALID_ADDRESS),
+ m_unwind_on_error (unwind_on_error),
+ m_ignore_breakpoints (ignore_breakpoints)
+{
+ lldb::addr_t start_load_addr;
+ ABI *abi;
+ lldb::addr_t function_load_addr;
+ if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr))
+ return;
+
+ if (this_arg && cmd_arg)
+ {
+ if (!abi->PrepareTrivialCall (thread,
+ m_function_sp,
+ function_load_addr,
+ start_load_addr,
+ this_arg,
+ cmd_arg,
+ &arg))
+ return;
+ }
+ else if (this_arg)
+ {
+ if (!abi->PrepareTrivialCall (thread,
+ m_function_sp,
+ function_load_addr,
+ start_load_addr,
+ this_arg,
+ &arg))
+ return;
+ }
+ else
+ {
+ if (!abi->PrepareTrivialCall (thread,
+ m_function_sp,
+ function_load_addr,
+ start_load_addr,
+ &arg))
+ return;
+ }
+
+ ReportRegisterState ("Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+
+ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
+ const Address &function,
+ const ClangASTType &return_type,
+ bool stop_other_threads,
+ bool unwind_on_error,
+ bool ignore_breakpoints,
+ addr_t *arg1_ptr,
+ addr_t *arg2_ptr,
+ addr_t *arg3_ptr,
+ addr_t *arg4_ptr,
+ addr_t *arg5_ptr,
+ addr_t *arg6_ptr) :
+ ThreadPlan (ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_valid (false),
+ m_stop_other_threads (stop_other_threads),
+ m_function_addr (function),
+ m_function_sp (0),
+ m_return_type (return_type),
+ m_takedown_done (false),
+ m_stop_address (LLDB_INVALID_ADDRESS),
+ m_unwind_on_error (unwind_on_error),
+ m_ignore_breakpoints (ignore_breakpoints)
+{
+ lldb::addr_t start_load_addr;
+ ABI *abi;
+ lldb::addr_t function_load_addr;
+ if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr))
+ return;
+
+ if (!abi->PrepareTrivialCall (thread,
+ m_function_sp,
+ function_load_addr,
+ start_load_addr,
+ arg1_ptr,
+ arg2_ptr,
+ arg3_ptr,
+ arg4_ptr,
+ arg5_ptr,
+ arg6_ptr))
+ {
+ return;
+ }
+
+ ReportRegisterState ("Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+ThreadPlanCallFunction::~ThreadPlanCallFunction ()
+{
+ DoTakedown(PlanSucceeded());
+}
+
+void
+ThreadPlanCallFunction::ReportRegisterState (const char *message)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ {
+ StreamString strm;
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ log->PutCString(message);
+
+ RegisterValue reg_value;
+
+ for (uint32_t reg_idx = 0, num_registers = reg_ctx->GetRegisterCount();
+ reg_idx < num_registers;
+ ++reg_idx)
+ {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_idx);
+ if (reg_ctx->ReadRegister(reg_info, reg_value))
+ {
+ reg_value.Dump(&strm, reg_info, true, false, eFormatDefault);
+ strm.EOL();
+ }
+ }
+ log->PutCString(strm.GetData());
+ }
+}
+
+void
+ThreadPlanCallFunction::DoTakedown (bool success)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (!m_valid)
+ {
+ //Don't call DoTakedown if we were never valid to begin with.
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): Log called on ThreadPlanCallFunction that was never valid.", this);
+ return;
+ }
+
+ if (!m_takedown_done)
+ {
+ if (success)
+ {
+ ProcessSP process_sp (m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+ if (abi && m_return_type.IsValid())
+ {
+ const bool persistent = false;
+ m_return_valobj_sp = abi->GetReturnValueObject (m_thread, m_return_type, persistent);
+ }
+ }
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): DoTakedown called for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", this, m_thread.GetID(), m_valid, IsPlanComplete());
+ m_takedown_done = true;
+ m_stop_address = m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
+ m_real_stop_info_sp = GetPrivateStopInfo ();
+ m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state);
+ SetPlanComplete(success);
+ ClearBreakpoints();
+ if (log && log->GetVerbose())
+ ReportRegisterState ("Restoring thread state after function call. Restored register state:");
+
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ThreadPlanCallFunction(%p): DoTakedown called as no-op for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", this, m_thread.GetID(), m_valid, IsPlanComplete());
+ }
+}
+
+void
+ThreadPlanCallFunction::WillPop ()
+{
+ DoTakedown(PlanSucceeded());
+}
+
+void
+ThreadPlanCallFunction::GetDescription (Stream *s, DescriptionLevel level)
+{
+ if (level == eDescriptionLevelBrief)
+ {
+ s->Printf("Function call thread plan");
+ }
+ else
+ {
+ TargetSP target_sp (m_thread.CalculateTarget());
+ s->Printf("Thread plan to call 0x%" PRIx64, m_function_addr.GetLoadAddress(target_sp.get()));
+ }
+}
+
+bool
+ThreadPlanCallFunction::ValidatePlan (Stream *error)
+{
+ if (!m_valid)
+ {
+ if (error)
+ {
+ if (m_constructor_errors.GetSize() > 0)
+ error->PutCString (m_constructor_errors.GetData());
+ else
+ error->PutCString ("Unknown error");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+Vote
+ThreadPlanCallFunction::ShouldReportStop(Event *event_ptr)
+{
+ if (m_takedown_done || IsPlanComplete())
+ return eVoteYes;
+ else
+ return ThreadPlan::ShouldReportStop(event_ptr);
+}
+
+bool
+ThreadPlanCallFunction::DoPlanExplainsStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP|LIBLLDB_LOG_PROCESS));
+ m_real_stop_info_sp = GetPrivateStopInfo ();
+
+ // If our subplan knows why we stopped, even if it's done (which would forward the question to us)
+ // we answer yes.
+ if (m_subplan_sp.get() != NULL && m_subplan_sp->PlanExplainsStop(event_ptr))
+ {
+ SetPlanComplete();
+ return true;
+ }
+
+ // Check if the breakpoint is one of ours.
+
+ StopReason stop_reason;
+ if (!m_real_stop_info_sp)
+ stop_reason = eStopReasonNone;
+ else
+ stop_reason = m_real_stop_info_sp->GetStopReason();
+ if (log)
+ log->Printf ("ThreadPlanCallFunction::PlanExplainsStop: Got stop reason - %s.", Thread::StopReasonAsCString(stop_reason));
+
+ if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop())
+ return true;
+
+ // We control breakpoints separately from other "stop reasons." So first,
+ // check the case where we stopped for an internal breakpoint, in that case, continue on.
+ // If it is not an internal breakpoint, consult m_ignore_breakpoints.
+
+
+ if (stop_reason == eStopReasonBreakpoint)
+ {
+ ProcessSP process_sp (m_thread.CalculateProcess());
+ uint64_t break_site_id = m_real_stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByID(break_site_id);
+ if (bp_site_sp)
+ {
+ uint32_t num_owners = bp_site_sp->GetNumberOfOwners();
+ bool is_internal = true;
+ for (uint32_t i = 0; i < num_owners; i++)
+ {
+ Breakpoint &bp = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (log)
+ log->Printf ("ThreadPlanCallFunction::PlanExplainsStop: hit breakpoint %d while calling function", bp.GetID());
+
+ if (!bp.IsInternal())
+ {
+ is_internal = false;
+ break;
+ }
+ }
+ if (is_internal)
+ {
+ if (log)
+ log->Printf ("ThreadPlanCallFunction::PlanExplainsStop hit an internal breakpoint, not stopping.");
+ return false;
+ }
+ }
+
+ if (m_ignore_breakpoints)
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: we are ignoring breakpoints, overriding breakpoint stop info ShouldStop, returning true");
+ m_real_stop_info_sp->OverrideShouldStop(false);
+ return true;
+ }
+ else
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunction::PlanExplainsStop: we are not ignoring breakpoints, overriding breakpoint stop info ShouldStop, returning true");
+ m_real_stop_info_sp->OverrideShouldStop(true);
+ return false;
+ }
+ }
+ else if (!m_unwind_on_error)
+ {
+ // If we don't want to discard this plan, than any stop we don't understand should be propagated up the stack.
+ return false;
+ }
+ else
+ {
+ // If the subplan is running, any crashes are attributable to us.
+ // If we want to discard the plan, then we say we explain the stop
+ // but if we are going to be discarded, let whoever is above us
+ // explain the stop.
+ // But don't discard the plan if the stop would restart itself (for instance if it is a
+ // signal that is set not to stop. Check that here first. We just say we explain the stop
+ // but aren't done and everything will continue on from there.
+
+ if (m_real_stop_info_sp->ShouldStopSynchronous(event_ptr))
+ {
+ SetPlanComplete(false);
+ if (m_subplan_sp)
+ {
+ if (m_unwind_on_error)
+ return true;
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ else
+ return true;
+ }
+}
+
+bool
+ThreadPlanCallFunction::ShouldStop (Event *event_ptr)
+{
+ // We do some computation in DoPlanExplainsStop that may or may not set the plan as complete.
+ // We need to do that here to make sure our state is correct.
+ DoPlanExplainsStop(event_ptr);
+
+ if (IsPlanComplete())
+ {
+ ReportRegisterState ("Function completed. Register state was:");
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool
+ThreadPlanCallFunction::StopOthers ()
+{
+ return m_stop_other_threads;
+}
+
+void
+ThreadPlanCallFunction::SetStopOthers (bool new_value)
+{
+ if (m_subplan_sp)
+ {
+ ThreadPlanRunToAddress *address_plan = static_cast<ThreadPlanRunToAddress *>(m_subplan_sp.get());
+ address_plan->SetStopOthers(new_value);
+ }
+ m_stop_other_threads = new_value;
+}
+
+StateType
+ThreadPlanCallFunction::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+void
+ThreadPlanCallFunction::DidPush ()
+{
+//#define SINGLE_STEP_EXPRESSIONS
+
+ // Now set the thread state to "no reason" so we don't run with whatever signal was outstanding...
+ // Wait till the plan is pushed so we aren't changing the stop info till we're about to run.
+
+ GetThread().SetStopInfoToNothing();
+
+#ifndef SINGLE_STEP_EXPRESSIONS
+ m_subplan_sp.reset(new ThreadPlanRunToAddress(m_thread, m_start_addr, m_stop_other_threads));
+
+ m_thread.QueueThreadPlan(m_subplan_sp, false);
+ m_subplan_sp->SetPrivate (true);
+#endif
+}
+
+bool
+ThreadPlanCallFunction::WillStop ()
+{
+ return true;
+}
+
+bool
+ThreadPlanCallFunction::MischiefManaged ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (IsPlanComplete())
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunction(%p): Completed call function plan.", this);
+
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void
+ThreadPlanCallFunction::SetBreakpoints ()
+{
+ ProcessSP process_sp (m_thread.CalculateProcess());
+ if (process_sp)
+ {
+ m_cxx_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeC_plus_plus);
+ m_objc_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeObjC);
+
+ if (m_cxx_language_runtime)
+ m_cxx_language_runtime->SetExceptionBreakpoints();
+ if (m_objc_language_runtime)
+ m_objc_language_runtime->SetExceptionBreakpoints();
+ }
+}
+
+void
+ThreadPlanCallFunction::ClearBreakpoints ()
+{
+ if (m_cxx_language_runtime)
+ m_cxx_language_runtime->ClearExceptionBreakpoints();
+ if (m_objc_language_runtime)
+ m_objc_language_runtime->ClearExceptionBreakpoints();
+}
+
+bool
+ThreadPlanCallFunction::BreakpointsExplainStop()
+{
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+
+ if ((m_cxx_language_runtime &&
+ m_cxx_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp))
+ ||(m_objc_language_runtime &&
+ m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)))
+ {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf ("ThreadPlanCallFunction::BreakpointsExplainStop - Hit an exception breakpoint, setting plan complete.");
+
+ SetPlanComplete(false);
+
+ // If the user has set the ObjC language breakpoint, it would normally get priority over our internal
+ // catcher breakpoint, but in this case we can't let that happen, so force the ShouldStop here.
+ stop_info_sp->OverrideShouldStop (true);
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ThreadPlanCallFunction::RestoreThreadState()
+{
+ return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state);
+}
+
diff --git a/source/Target/ThreadPlanCallUserExpression.cpp b/source/Target/ThreadPlanCallUserExpression.cpp
new file mode 100644
index 000000000000..70de1cbe86e1
--- /dev/null
+++ b/source/Target/ThreadPlanCallUserExpression.cpp
@@ -0,0 +1,82 @@
+//===-- ThreadPlanCallUserExpression.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/Target/ThreadPlanCallUserExpression.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "llvm/Support/MachO.h"
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Expression/ClangUserExpression.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanCallUserExpression: Plan to call a single function
+//----------------------------------------------------------------------
+
+ThreadPlanCallUserExpression::ThreadPlanCallUserExpression (Thread &thread,
+ Address &function,
+ lldb::addr_t arg,
+ bool stop_other_threads,
+ bool unwind_on_error,
+ bool ignore_breakpoints,
+ lldb::addr_t *this_arg,
+ lldb::addr_t *cmd_arg,
+ ClangUserExpression::ClangUserExpressionSP &user_expression_sp) :
+ ThreadPlanCallFunction (thread, function, ClangASTType(), arg, stop_other_threads, unwind_on_error, ignore_breakpoints, this_arg, cmd_arg),
+ m_user_expression_sp (user_expression_sp)
+{
+ // User expressions are generally "User generated" so we should set them up to stop when done.
+ SetIsMasterPlan (true);
+ SetOkayToDiscard(false);
+}
+
+ThreadPlanCallUserExpression::~ThreadPlanCallUserExpression ()
+{
+}
+
+void
+ThreadPlanCallUserExpression::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ ThreadPlanCallFunction::GetDescription (s, level);
+}
+
+StopInfoSP
+ThreadPlanCallUserExpression::GetRealStopInfo()
+{
+ StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo();
+
+ if (stop_info_sp)
+ {
+ lldb::addr_t addr = GetStopAddress();
+ DynamicCheckerFunctions *checkers = m_thread.GetProcess()->GetDynamicCheckers();
+ StreamString s;
+
+ if (checkers && checkers->DoCheckersExplainStop(addr, s))
+ stop_info_sp->SetDescription(s.GetData());
+ }
+
+ return stop_info_sp;
+}
diff --git a/source/Target/ThreadPlanRunToAddress.cpp b/source/Target/ThreadPlanRunToAddress.cpp
new file mode 100644
index 000000000000..86825d2eb26b
--- /dev/null
+++ b/source/Target/ThreadPlanRunToAddress.cpp
@@ -0,0 +1,268 @@
+//===-- ThreadPlanRunToAddress.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/Target/ThreadPlanRunToAddress.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/RegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanRunToAddress: Continue plan
+//----------------------------------------------------------------------
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress
+(
+ Thread &thread,
+ Address &address,
+ bool stop_others
+) :
+ ThreadPlan (ThreadPlan::eKindRunToAddress, "Run to address plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others (stop_others),
+ m_addresses (),
+ m_break_ids ()
+{
+ m_addresses.push_back (address.GetOpcodeLoadAddress (m_thread.CalculateTarget().get()));
+ SetInitialBreakpoints();
+}
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress
+(
+ Thread &thread,
+ lldb::addr_t address,
+ bool stop_others
+) :
+ ThreadPlan (ThreadPlan::eKindRunToAddress, "Run to address plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others (stop_others),
+ m_addresses (),
+ m_break_ids ()
+{
+ m_addresses.push_back(m_thread.CalculateTarget()->GetOpcodeLoadAddress(address));
+ SetInitialBreakpoints();
+}
+
+ThreadPlanRunToAddress::ThreadPlanRunToAddress
+(
+ Thread &thread,
+ const std::vector<lldb::addr_t> &addresses,
+ bool stop_others
+) :
+ ThreadPlan (ThreadPlan::eKindRunToAddress, "Run to address plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_stop_others (stop_others),
+ m_addresses (addresses),
+ m_break_ids ()
+{
+ // Convert all addressses into opcode addresses to make sure we set
+ // breakpoints at the correct address.
+ Target &target = thread.GetProcess()->GetTarget();
+ std::vector<lldb::addr_t>::iterator pos, end = m_addresses.end();
+ for (pos = m_addresses.begin(); pos != end; ++pos)
+ *pos = target.GetOpcodeLoadAddress (*pos);
+
+ SetInitialBreakpoints();
+}
+
+void
+ThreadPlanRunToAddress::SetInitialBreakpoints ()
+{
+ size_t num_addresses = m_addresses.size();
+ m_break_ids.resize(num_addresses);
+
+ for (size_t i = 0; i < num_addresses; i++)
+ {
+ Breakpoint *breakpoint;
+ breakpoint = m_thread.CalculateTarget()->CreateBreakpoint (m_addresses[i], true).get();
+ if (breakpoint != NULL)
+ {
+ m_break_ids[i] = breakpoint->GetID();
+ breakpoint->SetThreadID(m_thread.GetID());
+ breakpoint->SetBreakpointKind("run-to-address");
+ }
+ }
+}
+
+ThreadPlanRunToAddress::~ThreadPlanRunToAddress ()
+{
+ size_t num_break_ids = m_break_ids.size();
+ for (size_t i = 0; i < num_break_ids; i++)
+ {
+ m_thread.CalculateTarget()->RemoveBreakpointByID (m_break_ids[i]);
+ }
+}
+
+void
+ThreadPlanRunToAddress::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ size_t num_addresses = m_addresses.size();
+
+ if (level == lldb::eDescriptionLevelBrief)
+ {
+ if (num_addresses == 0)
+ {
+ s->Printf ("run to address with no addresses given.");
+ return;
+ }
+ else if (num_addresses == 1)
+ s->Printf ("run to address: ");
+ else
+ s->Printf ("run to addresses: ");
+
+ for (size_t i = 0; i < num_addresses; i++)
+ {
+ s->Address (m_addresses[i], sizeof (addr_t));
+ s->Printf(" ");
+ }
+ }
+ else
+ {
+ if (num_addresses == 0)
+ {
+ s->Printf ("run to address with no addresses given.");
+ return;
+ }
+ else if (num_addresses == 1)
+ s->Printf ("Run to address: ");
+ else
+ {
+ s->Printf ("Run to addresses: ");
+ }
+
+ for (size_t i = 0; i < num_addresses; i++)
+ {
+ if (num_addresses > 1)
+ {
+ s->Printf("\n");
+ s->Indent();
+ }
+
+ s->Address(m_addresses[i], sizeof (addr_t));
+ s->Printf (" using breakpoint: %d - ", m_break_ids[i]);
+ Breakpoint *breakpoint = m_thread.CalculateTarget()->GetBreakpointByID (m_break_ids[i]).get();
+ if (breakpoint)
+ breakpoint->Dump (s);
+ else
+ s->Printf ("but the breakpoint has been deleted.");
+ }
+ }
+}
+
+bool
+ThreadPlanRunToAddress::ValidatePlan (Stream *error)
+{
+ // If we couldn't set the breakpoint for some reason, then this won't
+ // work.
+ bool all_bps_good = true;
+ size_t num_break_ids = m_break_ids.size();
+
+ for (size_t i = 0; i < num_break_ids; i++)
+ {
+ if (m_break_ids[i] == LLDB_INVALID_BREAK_ID)
+ {
+ all_bps_good = false;
+ if (error)
+ {
+ error->Printf ("Could not set breakpoint for address: ");
+ error->Address (m_addresses[i], sizeof (addr_t));
+ error->Printf ("\n");
+ }
+ }
+ }
+ return all_bps_good;
+}
+
+bool
+ThreadPlanRunToAddress::DoPlanExplainsStop (Event *event_ptr)
+{
+ return AtOurAddress();
+}
+
+bool
+ThreadPlanRunToAddress::ShouldStop (Event *event_ptr)
+{
+ return false;
+}
+
+bool
+ThreadPlanRunToAddress::StopOthers ()
+{
+ return m_stop_others;
+}
+
+void
+ThreadPlanRunToAddress::SetStopOthers (bool new_value)
+{
+ m_stop_others = new_value;
+}
+
+StateType
+ThreadPlanRunToAddress::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+bool
+ThreadPlanRunToAddress::WillStop ()
+{
+ return true;
+}
+
+bool
+ThreadPlanRunToAddress::MischiefManaged ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (AtOurAddress())
+ {
+ // Remove the breakpoint
+ size_t num_break_ids = m_break_ids.size();
+
+ for (size_t i = 0; i < num_break_ids; i++)
+ {
+ if (m_break_ids[i] != LLDB_INVALID_BREAK_ID)
+ {
+ m_thread.CalculateTarget()->RemoveBreakpointByID (m_break_ids[i]);
+ m_break_ids[i] = LLDB_INVALID_BREAK_ID;
+ }
+ }
+ if (log)
+ log->Printf("Completed run to address plan.");
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ return false;
+}
+
+bool
+ThreadPlanRunToAddress::AtOurAddress ()
+{
+ lldb::addr_t current_address = m_thread.GetRegisterContext()->GetPC();
+ bool found_it = false;
+ size_t num_addresses = m_addresses.size();
+ for (size_t i = 0; i < num_addresses; i++)
+ {
+ if (m_addresses[i] == current_address)
+ {
+ found_it = true;
+ break;
+ }
+ }
+ return found_it;
+}
diff --git a/source/Target/ThreadPlanShouldStopHere.cpp b/source/Target/ThreadPlanShouldStopHere.cpp
new file mode 100644
index 000000000000..87662345a06d
--- /dev/null
+++ b/source/Target/ThreadPlanShouldStopHere.cpp
@@ -0,0 +1,74 @@
+//===-- ThreadPlanShouldStopHere.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/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanShouldStopHere.h"
+#include "lldb/Core/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+//----------------------------------------------------------------------
+// ThreadPlanShouldStopHere constructor
+//----------------------------------------------------------------------
+ThreadPlanShouldStopHere::ThreadPlanShouldStopHere(ThreadPlan *owner, ThreadPlanShouldStopHereCallback callback, void *baton) :
+ m_callback (callback),
+ m_baton (baton),
+ m_owner (owner),
+ m_flags (ThreadPlanShouldStopHere::eNone)
+{
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+ThreadPlanShouldStopHere::~ThreadPlanShouldStopHere()
+{
+}
+
+void
+ThreadPlanShouldStopHere::SetShouldStopHereCallback (ThreadPlanShouldStopHereCallback callback, void *baton)
+{
+ m_callback = callback;
+ m_baton = baton;
+}
+
+ThreadPlanSP
+ThreadPlanShouldStopHere::InvokeShouldStopHereCallback ()
+{
+ if (m_callback)
+ {
+ ThreadPlanSP return_plan_sp(m_callback (m_owner, m_flags, m_baton));
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ lldb::addr_t current_addr = m_owner->GetThread().GetRegisterContext()->GetPC(0);
+
+ if (return_plan_sp)
+ {
+ StreamString s;
+ return_plan_sp->GetDescription (&s, lldb::eDescriptionLevelFull);
+ log->Printf ("ShouldStopHere callback found a step out plan from 0x%" PRIx64 ": %s.", current_addr, s.GetData());
+ }
+ else
+ {
+ log->Printf ("ShouldStopHere callback didn't find a step out plan from: 0x%" PRIx64 ".", current_addr);
+ }
+ }
+ return return_plan_sp;
+ }
+ else
+ return ThreadPlanSP();
+}
diff --git a/source/Target/ThreadPlanStepInRange.cpp b/source/Target/ThreadPlanStepInRange.cpp
new file mode 100644
index 000000000000..c1f14bd216de
--- /dev/null
+++ b/source/Target/ThreadPlanStepInRange.cpp
@@ -0,0 +1,485 @@
+//===-- ThreadPlanStepInRange.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/Target/ThreadPlanStepInRange.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+#include "lldb/Core/RegularExpression.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+uint32_t ThreadPlanStepInRange::s_default_flag_values = ThreadPlanShouldStopHere::eAvoidNoDebug;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepInRange: Step through a stack range, either stepping over or into
+// based on the value of \a type.
+//----------------------------------------------------------------------
+
+ThreadPlanStepInRange::ThreadPlanStepInRange
+(
+ Thread &thread,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ lldb::RunMode stop_others
+) :
+ ThreadPlanStepRange (ThreadPlan::eKindStepInRange, "Step Range stepping in", thread, range, addr_context, stop_others),
+ ThreadPlanShouldStopHere (this, ThreadPlanStepInRange::DefaultShouldStopHereCallback, NULL),
+ m_step_past_prologue (true),
+ m_virtual_step (false)
+{
+ SetFlagsToDefault ();
+}
+
+ThreadPlanStepInRange::ThreadPlanStepInRange
+(
+ Thread &thread,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ const char *step_into_target,
+ lldb::RunMode stop_others
+) :
+ ThreadPlanStepRange (ThreadPlan::eKindStepInRange, "Step Range stepping in", thread, range, addr_context, stop_others),
+ ThreadPlanShouldStopHere (this, ThreadPlanStepInRange::DefaultShouldStopHereCallback, NULL),
+ m_step_past_prologue (true),
+ m_virtual_step (false),
+ m_step_into_target (step_into_target)
+{
+ SetFlagsToDefault ();
+}
+
+ThreadPlanStepInRange::~ThreadPlanStepInRange ()
+{
+}
+
+void
+ThreadPlanStepInRange::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("step in");
+ else
+ {
+ s->Printf ("Stepping through range (stepping into functions): ");
+ DumpRanges(s);
+ const char *step_into_target = m_step_into_target.AsCString();
+ if (step_into_target && step_into_target[0] != '\0')
+ s->Printf (" targeting %s.", m_step_into_target.AsCString());
+ else
+ s->PutChar('.');
+ }
+}
+
+bool
+ThreadPlanStepInRange::ShouldStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (log)
+ {
+ StreamString s;
+ s.Address (m_thread.GetRegisterContext()->GetPC(),
+ m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ log->Printf("ThreadPlanStepInRange reached %s.", s.GetData());
+ }
+
+ if (IsPlanComplete())
+ return true;
+
+ m_no_more_plans = false;
+ if (m_sub_plan_sp && m_sub_plan_sp->IsPlanComplete())
+ {
+ if (!m_sub_plan_sp->PlanSucceeded())
+ {
+ SetPlanComplete();
+ m_no_more_plans = true;
+ return true;
+ }
+ else
+ m_sub_plan_sp.reset();
+ }
+
+ if (m_virtual_step)
+ {
+ // If we've just completed a virtual step, all we need to do is check for a ShouldStopHere plan, and otherwise
+ // we're done.
+ m_sub_plan_sp = InvokeShouldStopHereCallback();
+ }
+ else
+ {
+ // Stepping through should be done running other threads in general, since we're setting a breakpoint and
+ // continuing. So only stop others if we are explicitly told to do so.
+
+ bool stop_others;
+ if (m_stop_others == lldb::eOnlyThisThread)
+ stop_others = false;
+ else
+ stop_others = true;
+
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder)
+ {
+ // If we're in an older frame then we should stop.
+ //
+ // A caveat to this is if we think the frame is older but we're actually in a trampoline.
+ // I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are
+ // in a trampoline we think the frame is older because the trampoline confused the backtracer.
+ m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others);
+ if (!m_sub_plan_sp)
+ return true;
+ else if (log)
+ {
+ log->Printf("Thought I stepped out, but in fact arrived at a trampoline.");
+ }
+
+ }
+ else if (frame_order == eFrameCompareEqual && InSymbol())
+ {
+ // If we are not in a place we should step through, we're done.
+ // One tricky bit here is that some stubs don't push a frame, so we have to check
+ // both the case of a frame that is younger, or the same as this frame.
+ // However, if the frame is the same, and we are still in the symbol we started
+ // in, the we don't need to do this. This first check isn't strictly necessary,
+ // but it is more efficient.
+
+ // If we're still in the range, keep going, either by running to the next branch breakpoint, or by
+ // stepping.
+ if (InRange())
+ {
+ SetNextBranchBreakpoint();
+ return false;
+ }
+
+ SetPlanComplete();
+ m_no_more_plans = true;
+ return true;
+ }
+
+ // If we get to this point, we're not going to use a previously set "next branch" breakpoint, so delete it:
+ ClearNextBranchBreakpoint();
+
+ // We may have set the plan up above in the FrameIsOlder section:
+
+ if (!m_sub_plan_sp)
+ m_sub_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others);
+
+ if (log)
+ {
+ if (m_sub_plan_sp)
+ log->Printf ("Found a step through plan: %s", m_sub_plan_sp->GetName());
+ else
+ log->Printf ("No step through plan found.");
+ }
+
+ // If not, give the "should_stop" callback a chance to push a plan to get us out of here.
+ // But only do that if we actually have stepped in.
+ if (!m_sub_plan_sp && frame_order == eFrameCompareYounger)
+ m_sub_plan_sp = InvokeShouldStopHereCallback();
+
+ // If we've stepped in and we are going to stop here, check to see if we were asked to
+ // run past the prologue, and if so do that.
+
+ if (!m_sub_plan_sp && frame_order == eFrameCompareYounger && m_step_past_prologue)
+ {
+ lldb::StackFrameSP curr_frame = m_thread.GetStackFrameAtIndex(0);
+ if (curr_frame)
+ {
+ size_t bytes_to_skip = 0;
+ lldb::addr_t curr_addr = m_thread.GetRegisterContext()->GetPC();
+ Address func_start_address;
+
+ SymbolContext sc = curr_frame->GetSymbolContext (eSymbolContextFunction | eSymbolContextSymbol);
+
+ if (sc.function)
+ {
+ func_start_address = sc.function->GetAddressRange().GetBaseAddress();
+ if (curr_addr == func_start_address.GetLoadAddress(m_thread.CalculateTarget().get()))
+ bytes_to_skip = sc.function->GetPrologueByteSize();
+ }
+ else if (sc.symbol)
+ {
+ func_start_address = sc.symbol->GetAddress();
+ if (curr_addr == func_start_address.GetLoadAddress(m_thread.CalculateTarget().get()))
+ bytes_to_skip = sc.symbol->GetPrologueByteSize();
+ }
+
+ if (bytes_to_skip != 0)
+ {
+ func_start_address.Slide (bytes_to_skip);
+ log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP);
+ if (log)
+ log->Printf ("Pushing past prologue ");
+
+ m_sub_plan_sp = m_thread.QueueThreadPlanForRunToAddress(false, func_start_address,true);
+ }
+ }
+ }
+ }
+
+ if (!m_sub_plan_sp)
+ {
+ m_no_more_plans = true;
+ SetPlanComplete();
+ return true;
+ }
+ else
+ {
+ m_no_more_plans = false;
+ return false;
+ }
+}
+
+void
+ThreadPlanStepInRange::SetFlagsToDefault ()
+{
+ GetFlags().Set(ThreadPlanStepInRange::s_default_flag_values);
+}
+
+void
+ThreadPlanStepInRange::SetAvoidRegexp(const char *name)
+{
+ if (m_avoid_regexp_ap.get() == NULL)
+ m_avoid_regexp_ap.reset (new RegularExpression(name));
+
+ m_avoid_regexp_ap->Compile (name);
+}
+
+void
+ThreadPlanStepInRange::SetDefaultFlagValue (uint32_t new_value)
+{
+ // TODO: Should we test this for sanity?
+ ThreadPlanStepInRange::s_default_flag_values = new_value;
+}
+
+bool
+ThreadPlanStepInRange::FrameMatchesAvoidRegexp ()
+{
+ StackFrame *frame = GetThread().GetStackFrameAtIndex(0).get();
+
+ const RegularExpression *avoid_regexp_to_use = m_avoid_regexp_ap.get();
+ if (avoid_regexp_to_use == NULL)
+ avoid_regexp_to_use = GetThread().GetSymbolsToAvoidRegexp();
+
+ if (avoid_regexp_to_use != NULL)
+ {
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction|eSymbolContextBlock|eSymbolContextSymbol);
+ if (sc.symbol != NULL)
+ {
+ const char *frame_function_name = sc.GetFunctionName().GetCString();
+ if (frame_function_name)
+ {
+ size_t num_matches = 0;
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ num_matches = 1;
+
+ RegularExpression::Match regex_match(num_matches);
+
+ bool return_value = avoid_regexp_to_use->Execute(frame_function_name, &regex_match);
+ if (return_value)
+ {
+ if (log)
+ {
+ std::string match;
+ regex_match.GetMatchAtIndex(frame_function_name,0, match);
+ log->Printf ("Stepping out of function \"%s\" because it matches the avoid regexp \"%s\" - match substring: \"%s\".",
+ frame_function_name,
+ avoid_regexp_to_use->GetText(),
+ match.c_str());
+ }
+
+ }
+ return return_value;
+ }
+ }
+ }
+ return false;
+}
+
+ThreadPlanSP
+ThreadPlanStepInRange::DefaultShouldStopHereCallback (ThreadPlan *current_plan, Flags &flags, void *baton)
+{
+ bool should_step_out = false;
+ StackFrame *frame = current_plan->GetThread().GetStackFrameAtIndex(0).get();
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (flags.Test(eAvoidNoDebug))
+ {
+ if (!frame->HasDebugInformation())
+ {
+ if (log)
+ log->Printf ("Stepping out of frame with no debug info");
+
+ should_step_out = true;
+ }
+ }
+
+ if (current_plan->GetKind() == eKindStepInRange)
+ {
+ ThreadPlanStepInRange *step_in_range_plan = static_cast<ThreadPlanStepInRange *> (current_plan);
+ if (step_in_range_plan->m_step_into_target)
+ {
+ SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction|eSymbolContextBlock|eSymbolContextSymbol);
+ if (sc.symbol != NULL)
+ {
+ // First try an exact match, since that's cheap with ConstStrings. Then do a strstr compare.
+ if (step_in_range_plan->m_step_into_target == sc.GetFunctionName())
+ {
+ should_step_out = false;
+ }
+ else
+ {
+ const char *target_name = step_in_range_plan->m_step_into_target.AsCString();
+ const char *function_name = sc.GetFunctionName().AsCString();
+
+ if (function_name == NULL)
+ should_step_out = true;
+ else if (strstr (function_name, target_name) == NULL)
+ should_step_out = true;
+ }
+ if (log && should_step_out)
+ log->Printf("Stepping out of frame %s which did not match step into target %s.",
+ sc.GetFunctionName().AsCString(),
+ step_in_range_plan->m_step_into_target.AsCString());
+ }
+ }
+
+ if (!should_step_out)
+ {
+ ThreadPlanStepInRange *step_in_range_plan = static_cast<ThreadPlanStepInRange *> (current_plan);
+ // Don't log the should_step_out here, it's easier to do it in FrameMatchesAvoidRegexp.
+ should_step_out = step_in_range_plan->FrameMatchesAvoidRegexp ();
+ }
+ }
+
+
+ if (should_step_out)
+ {
+ // FIXME: Make sure the ThreadPlanForStepOut does the right thing with inlined functions.
+ // We really should have all plans take the tri-state for "stop others" so we can do the right
+ // thing. For now let's be safe and always run others when we are likely to run arbitrary code.
+ const bool stop_others = false;
+ return current_plan->GetThread().QueueThreadPlanForStepOut (false,
+ NULL,
+ true,
+ stop_others,
+ eVoteNo,
+ eVoteNoOpinion,
+ 0); // Frame index
+ }
+
+ return ThreadPlanSP();
+}
+
+bool
+ThreadPlanStepInRange::DoPlanExplainsStop (Event *event_ptr)
+{
+ // We always explain a stop. Either we've just done a single step, in which
+ // case we'll do our ordinary processing, or we stopped for some
+ // reason that isn't handled by our sub-plans, in which case we want to just stop right
+ // away.
+ // In general, we don't want to mark the plan as complete for unexplained stops.
+ // For instance, if you step in to some code with no debug info, so you step out
+ // and in the course of that hit a breakpoint, then you want to stop & show the user
+ // the breakpoint, but not unship the step in plan, since you still may want to complete that
+ // plan when you continue. This is particularly true when doing "step in to target function."
+ // stepping.
+ //
+ // The only variation is that if we are doing "step by running to next branch" in which case
+ // if we hit our branch breakpoint we don't set the plan to complete.
+
+ bool return_value;
+
+ if (m_virtual_step)
+ {
+ return_value = true;
+ }
+ else
+ {
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ switch (reason)
+ {
+ case eStopReasonBreakpoint:
+ if (NextRangeBreakpointExplainsStop(stop_info_sp))
+ {
+ return_value = true;
+ break;
+ }
+ case eStopReasonWatchpoint:
+ case eStopReasonSignal:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step.");
+ }
+ return_value = false;
+ break;
+ default:
+ return_value = true;
+ break;
+ }
+ }
+ else
+ return_value = true;
+ }
+
+ return return_value;
+}
+
+bool
+ThreadPlanStepInRange::DoWillResume (lldb::StateType resume_state, bool current_plan)
+{
+ if (resume_state == eStateStepping && current_plan)
+ {
+ // See if we are about to step over a virtual inlined call.
+ bool step_without_resume = m_thread.DecrementCurrentInlinedDepth();
+ if (step_without_resume)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf ("ThreadPlanStepInRange::DoWillResume: returning false, inline_depth: %d",
+ m_thread.GetCurrentInlinedDepth());
+ SetStopInfo(StopInfo::CreateStopReasonToTrace(m_thread));
+
+ // FIXME: Maybe it would be better to create a InlineStep stop reason, but then
+ // the whole rest of the world would have to handle that stop reason.
+ m_virtual_step = true;
+ }
+ return !step_without_resume;
+ }
+ return true;
+}
+
+bool
+ThreadPlanStepInRange::IsVirtualStep()
+{
+ return m_virtual_step;
+}
diff --git a/source/Target/ThreadPlanStepInstruction.cpp b/source/Target/ThreadPlanStepInstruction.cpp
new file mode 100644
index 000000000000..f644ee88f701
--- /dev/null
+++ b/source/Target/ThreadPlanStepInstruction.cpp
@@ -0,0 +1,227 @@
+//===-- ThreadPlanStepInstruction.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/Target/ThreadPlanStepInstruction.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepInstruction: Step over the current instruction
+//----------------------------------------------------------------------
+
+ThreadPlanStepInstruction::ThreadPlanStepInstruction
+(
+ Thread &thread,
+ bool step_over,
+ bool stop_other_threads,
+ Vote stop_vote,
+ Vote run_vote
+) :
+ ThreadPlan (ThreadPlan::eKindStepInstruction, "Step over single instruction", thread, stop_vote, run_vote),
+ m_instruction_addr (0),
+ m_stop_other_threads (stop_other_threads),
+ m_step_over (step_over)
+{
+ m_instruction_addr = m_thread.GetRegisterContext()->GetPC(0);
+ StackFrameSP m_start_frame_sp(m_thread.GetStackFrameAtIndex(0));
+ m_stack_id = m_start_frame_sp->GetStackID();
+
+ m_start_has_symbol = m_start_frame_sp->GetSymbolContext(eSymbolContextSymbol).symbol != NULL;
+
+ StackFrameSP parent_frame_sp = m_thread.GetStackFrameAtIndex(1);
+ if (parent_frame_sp)
+ m_parent_frame_id = parent_frame_sp->GetStackID();
+}
+
+ThreadPlanStepInstruction::~ThreadPlanStepInstruction ()
+{
+}
+
+void
+ThreadPlanStepInstruction::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ {
+ if (m_step_over)
+ s->Printf ("instruction step over");
+ else
+ s->Printf ("instruction step into");
+ }
+ else
+ {
+ s->Printf ("Stepping one instruction past ");
+ s->Address(m_instruction_addr, sizeof (addr_t));
+ if (!m_start_has_symbol)
+ s->Printf(" which has no symbol");
+
+ if (m_step_over)
+ s->Printf(" stepping over calls");
+ else
+ s->Printf(" stepping into calls");
+ }
+}
+
+bool
+ThreadPlanStepInstruction::ValidatePlan (Stream *error)
+{
+ // Since we read the instruction we're stepping over from the thread,
+ // this plan will always work.
+ return true;
+}
+
+bool
+ThreadPlanStepInstruction::DoPlanExplainsStop (Event *event_ptr)
+{
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonTrace || reason == eStopReasonNone)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+bool
+ThreadPlanStepInstruction::ShouldStop (Event *event_ptr)
+{
+ if (m_step_over)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ StackID cur_frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (cur_frame_zero_id == m_stack_id || m_stack_id < cur_frame_zero_id)
+ {
+ if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr)
+ {
+ SetPlanComplete();
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ {
+ // We've stepped in, step back out again:
+ StackFrame *return_frame = m_thread.GetStackFrameAtIndex(1).get();
+ if (return_frame)
+ {
+ if (return_frame->GetStackID() != m_parent_frame_id || m_start_has_symbol)
+ {
+ if (log)
+ {
+ StreamString s;
+ s.PutCString ("Stepped in to: ");
+ addr_t stop_addr = m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
+ s.Address (stop_addr, m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ s.PutCString (" stepping out to: ");
+ addr_t return_addr = return_frame->GetRegisterContext()->GetPC();
+ s.Address (return_addr, m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ log->Printf("%s.", s.GetData());
+ }
+
+ // StepInstruction should probably have the tri-state RunMode, but for now it is safer to
+ // run others.
+ const bool stop_others = false;
+ m_thread.QueueThreadPlanForStepOut(false,
+ NULL,
+ true,
+ stop_others,
+ eVoteNo,
+ eVoteNoOpinion,
+ 0);
+ return false;
+ }
+ else
+ {
+ if (log)
+ {
+ log->PutCString("The stack id we are stepping in changed, but our parent frame did not when stepping from code with no symbols. "
+ "We are probably just confused about where we are, stopping.");
+ }
+ SetPlanComplete();
+ return true;
+ }
+ }
+ else
+ {
+ if (log)
+ log->Printf("Could not find previous frame, stopping.");
+ SetPlanComplete();
+ return true;
+ }
+
+ }
+
+ }
+ else
+ {
+ if (m_thread.GetRegisterContext()->GetPC(0) != m_instruction_addr)
+ {
+ SetPlanComplete();
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+bool
+ThreadPlanStepInstruction::StopOthers ()
+{
+ return m_stop_other_threads;
+}
+
+StateType
+ThreadPlanStepInstruction::GetPlanRunState ()
+{
+ return eStateStepping;
+}
+
+bool
+ThreadPlanStepInstruction::WillStop ()
+{
+ return true;
+}
+
+bool
+ThreadPlanStepInstruction::MischiefManaged ()
+{
+ if (IsPlanComplete())
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed single instruction step plan.");
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
diff --git a/source/Target/ThreadPlanStepOut.cpp b/source/Target/ThreadPlanStepOut.cpp
new file mode 100644
index 000000000000..ba529587437d
--- /dev/null
+++ b/source/Target/ThreadPlanStepOut.cpp
@@ -0,0 +1,489 @@
+//===-- ThreadPlanStepOut.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/Target/ThreadPlanStepOut.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Core/ValueObjectConstResult.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Type.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlanStepOverRange.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepOut: Step out of the current frame
+//----------------------------------------------------------------------
+ThreadPlanStepOut::ThreadPlanStepOut
+(
+ Thread &thread,
+ SymbolContext *context,
+ bool first_insn,
+ bool stop_others,
+ Vote stop_vote,
+ Vote run_vote,
+ uint32_t frame_idx
+) :
+ ThreadPlan (ThreadPlan::eKindStepOut, "Step out", thread, stop_vote, run_vote),
+ m_step_from_context (context),
+ m_step_from_insn (LLDB_INVALID_ADDRESS),
+ m_return_bp_id (LLDB_INVALID_BREAK_ID),
+ m_return_addr (LLDB_INVALID_ADDRESS),
+ m_first_insn (first_insn),
+ m_stop_others (stop_others),
+ m_step_through_inline_plan_sp(),
+ m_step_out_plan_sp (),
+ m_immediate_step_from_function(NULL)
+
+{
+ m_step_from_insn = m_thread.GetRegisterContext()->GetPC(0);
+
+ StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
+ StackFrameSP immediate_return_from_sp (m_thread.GetStackFrameAtIndex (frame_idx));
+
+ if (!return_frame_sp || !immediate_return_from_sp)
+ return; // we can't do anything here. ValidatePlan() will return false.
+
+ m_step_out_to_id = return_frame_sp->GetStackID();
+ m_immediate_step_from_id = immediate_return_from_sp->GetStackID();
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ // If the frame directly below the one we are returning to is inlined, we have to be
+ // a little more careful. It is non-trivial to determine the real "return code address" for
+ // an inlined frame, so we have to work our way to that frame and then step out.
+ if (immediate_return_from_sp && immediate_return_from_sp->IsInlined())
+ {
+ if (frame_idx > 0)
+ {
+ // First queue a plan that gets us to this inlined frame, and when we get there we'll queue a second
+ // plan that walks us out of this frame.
+ m_step_out_plan_sp.reset (new ThreadPlanStepOut(m_thread,
+ NULL,
+ false,
+ stop_others,
+ eVoteNoOpinion,
+ eVoteNoOpinion,
+ frame_idx - 1));
+ }
+ else
+ {
+ // If we're already at the inlined frame we're stepping through, then just do that now.
+ QueueInlinedStepPlan(false);
+ }
+
+ }
+ else if (return_frame_sp)
+ {
+ // Find the return address and set a breakpoint there:
+ // FIXME - can we do this more securely if we know first_insn?
+
+ m_return_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(&m_thread.GetProcess()->GetTarget());
+
+ if (m_return_addr == LLDB_INVALID_ADDRESS)
+ return;
+
+ Breakpoint *return_bp = m_thread.CalculateTarget()->CreateBreakpoint (m_return_addr, true).get();
+ if (return_bp != NULL)
+ {
+ return_bp->SetThreadID(m_thread.GetID());
+ m_return_bp_id = return_bp->GetID();
+ return_bp->SetBreakpointKind ("step-out");
+ }
+
+ if (immediate_return_from_sp)
+ {
+ const SymbolContext &sc = immediate_return_from_sp->GetSymbolContext(eSymbolContextFunction);
+ if (sc.function)
+ {
+ m_immediate_step_from_function = sc.function;
+ }
+ }
+ }
+
+}
+
+void
+ThreadPlanStepOut::DidPush()
+{
+ if (m_step_out_plan_sp)
+ m_thread.QueueThreadPlan(m_step_out_plan_sp, false);
+ else if (m_step_through_inline_plan_sp)
+ m_thread.QueueThreadPlan(m_step_through_inline_plan_sp, false);
+}
+
+ThreadPlanStepOut::~ThreadPlanStepOut ()
+{
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
+ m_thread.CalculateTarget()->RemoveBreakpointByID(m_return_bp_id);
+}
+
+void
+ThreadPlanStepOut::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf ("step out");
+ else
+ {
+ if (m_step_out_plan_sp)
+ s->Printf ("Stepping out to inlined frame so we can walk through it.");
+ else if (m_step_through_inline_plan_sp)
+ s->Printf ("Stepping out by stepping through inlined function.");
+ else
+ s->Printf ("Stepping out from address 0x%" PRIx64 " to return address 0x%" PRIx64 " using breakpoint site %d",
+ (uint64_t)m_step_from_insn,
+ (uint64_t)m_return_addr,
+ m_return_bp_id);
+ }
+}
+
+bool
+ThreadPlanStepOut::ValidatePlan (Stream *error)
+{
+ if (m_step_out_plan_sp)
+ return m_step_out_plan_sp->ValidatePlan (error);
+ else if (m_step_through_inline_plan_sp)
+ return m_step_through_inline_plan_sp->ValidatePlan (error);
+ else if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
+ {
+ if (error)
+ error->PutCString("Could not create return address breakpoint.");
+ return false;
+ }
+ else
+ return true;
+}
+
+bool
+ThreadPlanStepOut::DoPlanExplainsStop (Event *event_ptr)
+{
+ // If one of our child plans just finished, then we do explain the stop.
+ if (m_step_out_plan_sp)
+ {
+ if (m_step_out_plan_sp->MischiefManaged())
+ {
+ // If this one is done, then we are all done.
+ CalculateReturnValue();
+ SetPlanComplete();
+ return true;
+ }
+ else
+ return false;
+ }
+ else if (m_step_through_inline_plan_sp)
+ {
+ if (m_step_through_inline_plan_sp->MischiefManaged())
+ return true;
+ else
+ return false;
+ }
+
+ // We don't explain signals or breakpoints (breakpoints that handle stepping in or
+ // out will be handled by a child plan.
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+ switch (reason)
+ {
+ case eStopReasonBreakpoint:
+ {
+ // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
+ BreakpointSiteSP site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByID (stop_info_sp->GetValue()));
+ if (site_sp && site_sp->IsBreakpointAtThisSite (m_return_bp_id))
+ {
+ bool done;
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (m_step_out_to_id == frame_zero_id)
+ done = true;
+ else if (m_step_out_to_id < frame_zero_id)
+ {
+ // Either we stepped past the breakpoint, or the stack ID calculation
+ // was incorrect and we should probably stop.
+ done = true;
+ }
+ else
+ {
+ if (m_immediate_step_from_id < frame_zero_id)
+ done = true;
+ else
+ done = false;
+ }
+
+ if (done)
+ {
+ CalculateReturnValue();
+ SetPlanComplete();
+ }
+
+ // If there was only one owner, then we're done. But if we also hit some
+ // user breakpoint on our way out, we should mark ourselves as done, but
+ // also not claim to explain the stop, since it is more important to report
+ // the user breakpoint than the step out completion.
+
+ if (site_sp->GetNumberOfOwners() == 1)
+ return true;
+
+ }
+ return false;
+ }
+ case eStopReasonWatchpoint:
+ case eStopReasonSignal:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ return false;
+
+ default:
+ return true;
+ }
+ }
+ return true;
+}
+
+bool
+ThreadPlanStepOut::ShouldStop (Event *event_ptr)
+{
+ if (IsPlanComplete())
+ return true;
+
+ bool done;
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ if (frame_zero_id < m_step_out_to_id)
+ done = false;
+ else
+ done = true;
+
+ if (done)
+ {
+ CalculateReturnValue();
+ SetPlanComplete();
+ return true;
+ }
+ else
+ {
+ if (m_step_out_plan_sp)
+ {
+ if (m_step_out_plan_sp->MischiefManaged())
+ {
+ // Now step through the inlined stack we are in:
+ if (QueueInlinedStepPlan(true))
+ {
+ return false;
+ }
+ else
+ {
+ CalculateReturnValue();
+ SetPlanComplete ();
+ return true;
+ }
+ }
+ else
+ return m_step_out_plan_sp->ShouldStop(event_ptr);
+ }
+ else if (m_step_through_inline_plan_sp)
+ {
+ if (m_step_through_inline_plan_sp->MischiefManaged())
+ {
+ // We don't calculate the return value here because we don't know how to.
+ // But in case we had a return value sitting around from our process in
+ // getting here, let's clear it out.
+ m_return_valobj_sp.reset();
+ SetPlanComplete();
+ return true;
+ }
+ else
+ return m_step_through_inline_plan_sp->ShouldStop(event_ptr);
+ }
+ else
+ return false;
+ }
+}
+
+bool
+ThreadPlanStepOut::StopOthers ()
+{
+ return m_stop_others;
+}
+
+StateType
+ThreadPlanStepOut::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+bool
+ThreadPlanStepOut::DoWillResume (StateType resume_state, bool current_plan)
+{
+ if (m_step_out_plan_sp || m_step_through_inline_plan_sp)
+ return true;
+
+ if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
+ return false;
+
+ if (current_plan)
+ {
+ Breakpoint *return_bp = m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != NULL)
+ return_bp->SetEnabled (true);
+ }
+ return true;
+}
+
+bool
+ThreadPlanStepOut::WillStop ()
+{
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
+ {
+ Breakpoint *return_bp = m_thread.CalculateTarget()->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != NULL)
+ return_bp->SetEnabled (false);
+ }
+
+ return true;
+}
+
+bool
+ThreadPlanStepOut::MischiefManaged ()
+{
+ if (IsPlanComplete())
+ {
+ // Did I reach my breakpoint? If so I'm done.
+ //
+ // I also check the stack depth, since if we've blown past the breakpoint for some
+ // reason and we're now stopping for some other reason altogether, then we're done
+ // with this step out operation.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step out plan.");
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
+ {
+ m_thread.CalculateTarget()->RemoveBreakpointByID (m_return_bp_id);
+ m_return_bp_id = LLDB_INVALID_BREAK_ID;
+ }
+
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool
+ThreadPlanStepOut::QueueInlinedStepPlan (bool queue_now)
+{
+ // Now figure out the range of this inlined block, and set up a "step through range"
+ // plan for that. If we've been provided with a context, then use the block in that
+ // context.
+ StackFrameSP immediate_return_from_sp (m_thread.GetStackFrameAtIndex (0));
+ if (!immediate_return_from_sp)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ StreamString s;
+ immediate_return_from_sp->Dump(&s, true, false);
+ log->Printf("Queuing inlined frame to step past: %s.", s.GetData());
+ }
+
+ Block *from_block = immediate_return_from_sp->GetFrameBlock();
+ if (from_block)
+ {
+ Block *inlined_block = from_block->GetContainingInlinedBlock();
+ if (inlined_block)
+ {
+ size_t num_ranges = inlined_block->GetNumRanges();
+ AddressRange inline_range;
+ if (inlined_block->GetRangeAtIndex(0, inline_range))
+ {
+ SymbolContext inlined_sc;
+ inlined_block->CalculateSymbolContext(&inlined_sc);
+ inlined_sc.target_sp = GetTarget().shared_from_this();
+ RunMode run_mode = m_stop_others ? lldb::eOnlyThisThread : lldb::eAllThreads;
+ ThreadPlanStepOverRange *step_through_inline_plan_ptr = new ThreadPlanStepOverRange(m_thread,
+ inline_range,
+ inlined_sc,
+ run_mode);
+ step_through_inline_plan_ptr->SetOkayToDiscard(true);
+ StreamString errors;
+ if (!step_through_inline_plan_ptr->ValidatePlan(&errors))
+ {
+ //FIXME: Log this failure.
+ delete step_through_inline_plan_ptr;
+ return false;
+ }
+
+ for (size_t i = 1; i < num_ranges; i++)
+ {
+ if (inlined_block->GetRangeAtIndex (i, inline_range))
+ step_through_inline_plan_ptr->AddRange (inline_range);
+ }
+ m_step_through_inline_plan_sp.reset (step_through_inline_plan_ptr);
+ if (queue_now)
+ m_thread.QueueThreadPlan (m_step_through_inline_plan_sp, false);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+ThreadPlanStepOut::CalculateReturnValue ()
+{
+ if (m_return_valobj_sp)
+ return;
+
+ if (m_immediate_step_from_function != NULL)
+ {
+ ClangASTType return_clang_type = m_immediate_step_from_function->GetClangType().GetFunctionReturnType();
+ if (return_clang_type)
+ {
+ lldb::ABISP abi_sp = m_thread.GetProcess()->GetABI();
+ if (abi_sp)
+ m_return_valobj_sp = abi_sp->GetReturnValueObject(m_thread, return_clang_type);
+ }
+ }
+}
+
+bool
+ThreadPlanStepOut::IsPlanStale()
+{
+ // If we are still lower on the stack than the frame we are returning to, then
+ // there's something for us to do. Otherwise, we're stale.
+
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+ if (frame_zero_id < m_step_out_to_id)
+ return false;
+ else
+ return true;
+}
+
diff --git a/source/Target/ThreadPlanStepOverBreakpoint.cpp b/source/Target/ThreadPlanStepOverBreakpoint.cpp
new file mode 100644
index 000000000000..dc011e545402
--- /dev/null
+++ b/source/Target/ThreadPlanStepOverBreakpoint.cpp
@@ -0,0 +1,165 @@
+//===-- ThreadPlanStepOverBreakpoint.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/Target/ThreadPlanStepOverBreakpoint.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepOverBreakpoint: Single steps over a breakpoint bp_site_sp at the pc.
+//----------------------------------------------------------------------
+
+ThreadPlanStepOverBreakpoint::ThreadPlanStepOverBreakpoint (Thread &thread) :
+ ThreadPlan (ThreadPlan::eKindStepOverBreakpoint, "Step over breakpoint trap",
+ thread,
+ eVoteNo,
+ eVoteNoOpinion), // We need to report the run since this happens
+ // first in the thread plan stack when stepping
+ // over a breakpoint
+ m_breakpoint_addr (LLDB_INVALID_ADDRESS),
+ m_auto_continue(false),
+ m_reenabled_breakpoint_site (false)
+
+{
+ m_breakpoint_addr = m_thread.GetRegisterContext()->GetPC();
+ m_breakpoint_site_id = m_thread.GetProcess()->GetBreakpointSiteList().FindIDByAddress (m_breakpoint_addr);
+}
+
+ThreadPlanStepOverBreakpoint::~ThreadPlanStepOverBreakpoint ()
+{
+}
+
+void
+ThreadPlanStepOverBreakpoint::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ s->Printf("Single stepping past breakpoint site %" PRIu64 " at 0x%" PRIx64, m_breakpoint_site_id, (uint64_t)m_breakpoint_addr);
+}
+
+bool
+ThreadPlanStepOverBreakpoint::ValidatePlan (Stream *error)
+{
+ return true;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr)
+{
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+ if (reason == eStopReasonTrace || reason == eStopReasonNone)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::ShouldStop (Event *event_ptr)
+{
+ return false;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::StopOthers ()
+{
+ return true;
+}
+
+StateType
+ThreadPlanStepOverBreakpoint::GetPlanRunState ()
+{
+ return eStateStepping;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::DoWillResume (StateType resume_state, bool current_plan)
+{
+ if (current_plan)
+ {
+ BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress (m_breakpoint_addr));
+ if (bp_site_sp && bp_site_sp->IsEnabled())
+ m_thread.GetProcess()->DisableBreakpointSite (bp_site_sp.get());
+ }
+ return true;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::WillStop ()
+{
+ ReenableBreakpointSite ();
+ return true;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::MischiefManaged ()
+{
+ lldb::addr_t pc_addr = m_thread.GetRegisterContext()->GetPC();
+
+ if (pc_addr == m_breakpoint_addr)
+ {
+ // If we are still at the PC of our breakpoint, then for some reason we didn't
+ // get a chance to run.
+ return false;
+ }
+ else
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step over breakpoint plan.");
+ // Otherwise, re-enable the breakpoint we were stepping over, and we're done.
+ ReenableBreakpointSite ();
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+}
+
+void
+ThreadPlanStepOverBreakpoint::ReenableBreakpointSite ()
+{
+ if (!m_reenabled_breakpoint_site)
+ {
+ m_reenabled_breakpoint_site = true;
+ BreakpointSiteSP bp_site_sp (m_thread.GetProcess()->GetBreakpointSiteList().FindByAddress (m_breakpoint_addr));
+ if (bp_site_sp)
+ {
+ m_thread.GetProcess()->EnableBreakpointSite (bp_site_sp.get());
+ }
+ }
+}
+void
+ThreadPlanStepOverBreakpoint::ThreadDestroyed ()
+{
+ ReenableBreakpointSite ();
+}
+
+void
+ThreadPlanStepOverBreakpoint::SetAutoContinue (bool do_it)
+{
+ m_auto_continue = do_it;
+}
+
+bool
+ThreadPlanStepOverBreakpoint::ShouldAutoContinue (Event *event_ptr)
+{
+ return m_auto_continue;
+}
diff --git a/source/Target/ThreadPlanStepOverRange.cpp b/source/Target/ThreadPlanStepOverRange.cpp
new file mode 100644
index 000000000000..7b8539ce422b
--- /dev/null
+++ b/source/Target/ThreadPlanStepOverRange.cpp
@@ -0,0 +1,388 @@
+//===-- ThreadPlanStepOverRange.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/Target/ThreadPlanStepOverRange.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/Block.h"
+#include "lldb/Symbol/CompileUnit.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/LineTable.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanStepOut.h"
+#include "lldb/Target/ThreadPlanStepThrough.h"
+
+using namespace lldb_private;
+using namespace lldb;
+
+
+//----------------------------------------------------------------------
+// ThreadPlanStepOverRange: Step through a stack range, either stepping over or into
+// based on the value of \a type.
+//----------------------------------------------------------------------
+
+ThreadPlanStepOverRange::ThreadPlanStepOverRange
+(
+ Thread &thread,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ lldb::RunMode stop_others
+) :
+ ThreadPlanStepRange (ThreadPlan::eKindStepOverRange, "Step range stepping over", thread, range, addr_context, stop_others),
+ m_first_resume(true)
+{
+}
+
+ThreadPlanStepOverRange::~ThreadPlanStepOverRange ()
+{
+}
+
+void
+ThreadPlanStepOverRange::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf("step over");
+ else
+ {
+ s->Printf ("stepping through range (stepping over functions): ");
+ DumpRanges(s);
+ }
+}
+
+bool
+ThreadPlanStepOverRange::ShouldStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (log)
+ {
+ StreamString s;
+ s.Address (m_thread.GetRegisterContext()->GetPC(),
+ m_thread.CalculateTarget()->GetArchitecture().GetAddressByteSize());
+ log->Printf("ThreadPlanStepOverRange reached %s.", s.GetData());
+ }
+
+ // If we're out of the range but in the same frame or in our caller's frame
+ // then we should stop.
+ // When stepping out we only stop others if we are forcing running one thread.
+ bool stop_others;
+ if (m_stop_others == lldb::eOnlyThisThread)
+ stop_others = true;
+ else
+ stop_others = false;
+
+ ThreadPlanSP new_plan_sp;
+
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder)
+ {
+ // If we're in an older frame then we should stop.
+ //
+ // A caveat to this is if we think the frame is older but we're actually in a trampoline.
+ // I'm going to make the assumption that you wouldn't RETURN to a trampoline. So if we are
+ // in a trampoline we think the frame is older because the trampoline confused the backtracer.
+ // As below, we step through first, and then try to figure out how to get back out again.
+
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others);
+
+ if (new_plan_sp && log)
+ log->Printf("Thought I stepped out, but in fact arrived at a trampoline.");
+ }
+ else if (frame_order == eFrameCompareYounger)
+ {
+ // Make sure we really are in a new frame. Do that by unwinding and seeing if the
+ // start function really is our start function...
+ StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1);
+
+ // But if we can't even unwind one frame we should just get out of here & stop...
+ if (older_frame_sp)
+ {
+ const SymbolContext &older_context = older_frame_sp->GetSymbolContext(eSymbolContextEverything);
+
+ // Match as much as is specified in the m_addr_context:
+ // This is a fairly loose sanity check. Note, sometimes the target doesn't get filled
+ // in so I left out the target check. And sometimes the module comes in as the .o file from the
+ // inlined range, so I left that out too...
+
+ bool older_ctx_is_equivalent = true;
+ if (m_addr_context.comp_unit)
+ {
+ if (m_addr_context.comp_unit == older_context.comp_unit)
+ {
+ if (m_addr_context.function && m_addr_context.function == older_context.function)
+ {
+ if (m_addr_context.block && m_addr_context.block == older_context.block)
+ {
+ older_ctx_is_equivalent = true;
+ }
+ }
+ }
+ }
+ else if (m_addr_context.symbol && m_addr_context.symbol == older_context.symbol)
+ {
+ older_ctx_is_equivalent = true;
+ }
+
+ if (older_ctx_is_equivalent)
+ {
+ new_plan_sp = m_thread.QueueThreadPlanForStepOut (false,
+ NULL,
+ true,
+ stop_others,
+ eVoteNo,
+ eVoteNoOpinion,
+ 0);
+ }
+ else
+ {
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others);
+
+ }
+ }
+ }
+ else
+ {
+ // If we're still in the range, keep going.
+ if (InRange())
+ {
+ SetNextBranchBreakpoint();
+ return false;
+ }
+
+
+ if (!InSymbol())
+ {
+ // This one is a little tricky. Sometimes we may be in a stub or something similar,
+ // in which case we need to get out of there. But if we are in a stub then it's
+ // likely going to be hard to get out from here. It is probably easiest to step into the
+ // stub, and then it will be straight-forward to step out.
+ new_plan_sp = m_thread.QueueThreadPlanForStepThrough (m_stack_id, false, stop_others);
+ }
+ else
+ {
+ // The current clang (at least through 424) doesn't always get the address range for the
+ // DW_TAG_inlined_subroutines right, so that when you leave the inlined range the line table says
+ // you are still in the source file of the inlining function. This is bad, because now you are missing
+ // the stack frame for the function containing the inlining, and if you sensibly do "finish" to get
+ // out of this function you will instead exit the containing function.
+ // To work around this, we check whether we are still in the source file we started in, and if not assume
+ // it is an error, and push a plan to get us out of this line and back to the containing file.
+
+ if (m_addr_context.line_entry.IsValid())
+ {
+ SymbolContext sc;
+ StackFrameSP frame_sp = m_thread.GetStackFrameAtIndex(0);
+ sc = frame_sp->GetSymbolContext (eSymbolContextEverything);
+ if (sc.line_entry.IsValid())
+ {
+ if (sc.line_entry.file != m_addr_context.line_entry.file
+ && sc.comp_unit == m_addr_context.comp_unit
+ && sc.function == m_addr_context.function)
+ {
+ // Okay, find the next occurance of this file in the line table:
+ LineTable *line_table = m_addr_context.comp_unit->GetLineTable();
+ if (line_table)
+ {
+ Address cur_address = frame_sp->GetFrameCodeAddress();
+ uint32_t entry_idx;
+ LineEntry line_entry;
+ if (line_table->FindLineEntryByAddress (cur_address, line_entry, &entry_idx))
+ {
+ LineEntry next_line_entry;
+ bool step_past_remaining_inline = false;
+ if (entry_idx > 0)
+ {
+ // We require the the previous line entry and the current line entry come
+ // from the same file.
+ // The other requirement is that the previous line table entry be part of an
+ // inlined block, we don't want to step past cases where people have inlined
+ // some code fragment by using #include <source-fragment.c> directly.
+ LineEntry prev_line_entry;
+ if (line_table->GetLineEntryAtIndex(entry_idx - 1, prev_line_entry)
+ && prev_line_entry.file == line_entry.file)
+ {
+ SymbolContext prev_sc;
+ Address prev_address = prev_line_entry.range.GetBaseAddress();
+ prev_address.CalculateSymbolContext(&prev_sc);
+ if (prev_sc.block)
+ {
+ Block *inlined_block = prev_sc.block->GetContainingInlinedBlock();
+ if (inlined_block)
+ {
+ AddressRange inline_range;
+ inlined_block->GetRangeContainingAddress(prev_address, inline_range);
+ if (!inline_range.ContainsFileAddress(cur_address))
+ {
+
+ step_past_remaining_inline = true;
+ }
+
+ }
+ }
+ }
+ }
+
+ if (step_past_remaining_inline)
+ {
+ uint32_t look_ahead_step = 1;
+ while (line_table->GetLineEntryAtIndex(entry_idx + look_ahead_step, next_line_entry))
+ {
+ // Make sure we haven't wandered out of the function we started from...
+ Address next_line_address = next_line_entry.range.GetBaseAddress();
+ Function *next_line_function = next_line_address.CalculateSymbolContextFunction();
+ if (next_line_function != m_addr_context.function)
+ break;
+
+ if (next_line_entry.file == m_addr_context.line_entry.file)
+ {
+ const bool abort_other_plans = false;
+ const bool stop_other_threads = false;
+ new_plan_sp = m_thread.QueueThreadPlanForRunToAddress(abort_other_plans,
+ next_line_address,
+ stop_other_threads);
+ break;
+ }
+ look_ahead_step++;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // If we get to this point, we're not going to use a previously set "next branch" breakpoint, so delete it:
+ ClearNextBranchBreakpoint();
+
+ if (!new_plan_sp)
+ m_no_more_plans = true;
+ else
+ m_no_more_plans = false;
+
+ if (!new_plan_sp)
+ {
+ // For efficiencies sake, we know we're done here so we don't have to do this
+ // calculation again in MischiefManaged.
+ SetPlanComplete();
+ return true;
+ }
+ else
+ return false;
+}
+
+bool
+ThreadPlanStepOverRange::DoPlanExplainsStop (Event *event_ptr)
+{
+ // For crashes, breakpoint hits, signals, etc, let the base plan (or some plan above us)
+ // handle the stop. That way the user can see the stop, step around, and then when they
+ // are done, continue and have their step complete. The exception is if we've hit our
+ // "run to next branch" breakpoint.
+ // Note, unlike the step in range plan, we don't mark ourselves complete if we hit an
+ // unexplained breakpoint/crash.
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ bool return_value;
+
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ switch (reason)
+ {
+ case eStopReasonTrace:
+ return_value = true;
+ break;
+ case eStopReasonBreakpoint:
+ if (NextRangeBreakpointExplainsStop(stop_info_sp))
+ return_value = true;
+ else
+ return_value = false;
+ break;
+ case eStopReasonWatchpoint:
+ case eStopReasonSignal:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ default:
+ if (log)
+ log->PutCString ("ThreadPlanStepInRange got asked if it explains the stop for some reason other than step.");
+ return_value = false;
+ break;
+ }
+ }
+ else
+ return_value = true;
+
+ return return_value;
+}
+
+bool
+ThreadPlanStepOverRange::DoWillResume (lldb::StateType resume_state, bool current_plan)
+{
+ if (resume_state != eStateSuspended && m_first_resume)
+ {
+ m_first_resume = false;
+ if (resume_state == eStateStepping && current_plan)
+ {
+ // See if we are about to step over an inlined call in the middle of the inlined stack, if so figure
+ // out its extents and reset our range to step over that.
+ bool in_inlined_stack = m_thread.DecrementCurrentInlinedDepth();
+ if (in_inlined_stack)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf ("ThreadPlanStepInRange::DoWillResume: adjusting range to the frame at inlined depth %d.",
+ m_thread.GetCurrentInlinedDepth());
+ StackFrameSP stack_sp = m_thread.GetStackFrameAtIndex(0);
+ if (stack_sp)
+ {
+ Block *frame_block = stack_sp->GetFrameBlock();
+ lldb::addr_t curr_pc = m_thread.GetRegisterContext()->GetPC();
+ AddressRange my_range;
+ if (frame_block->GetRangeContainingLoadAddress(curr_pc, m_thread.GetProcess()->GetTarget(), my_range))
+ {
+ m_address_ranges.clear();
+ m_address_ranges.push_back(my_range);
+ if (log)
+ {
+ StreamString s;
+ const InlineFunctionInfo *inline_info = frame_block->GetInlinedFunctionInfo();
+ const char *name;
+ if (inline_info)
+ name = inline_info->GetName().AsCString();
+ else
+ name = "<unknown-notinlined>";
+
+ s.Printf ("Stepping over inlined function \"%s\" in inlined stack: ", name);
+ DumpRanges(&s);
+ log->PutCString(s.GetData());
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ return true;
+}
diff --git a/source/Target/ThreadPlanStepRange.cpp b/source/Target/ThreadPlanStepRange.cpp
new file mode 100644
index 000000000000..3940d542c10d
--- /dev/null
+++ b/source/Target/ThreadPlanStepRange.cpp
@@ -0,0 +1,522 @@
+//===-- ThreadPlanStepRange.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/Target/ThreadPlanStepRange.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+
+#include "lldb/lldb-private-log.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/BreakpointSite.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/Function.h"
+#include "lldb/Symbol/Symbol.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+
+//----------------------------------------------------------------------
+// ThreadPlanStepRange: Step through a stack range, either stepping over or into
+// based on the value of \a type.
+//----------------------------------------------------------------------
+
+ThreadPlanStepRange::ThreadPlanStepRange (ThreadPlanKind kind,
+ const char *name,
+ Thread &thread,
+ const AddressRange &range,
+ const SymbolContext &addr_context,
+ lldb::RunMode stop_others) :
+ ThreadPlan (kind, name, thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_addr_context (addr_context),
+ m_address_ranges (),
+ m_stop_others (stop_others),
+ m_stack_id (),
+ m_no_more_plans (false),
+ m_first_run_event (true),
+ m_use_fast_step(false)
+{
+ m_use_fast_step = GetTarget().GetUseFastStepping();
+ AddRange(range);
+ m_stack_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+}
+
+ThreadPlanStepRange::~ThreadPlanStepRange ()
+{
+ ClearNextBranchBreakpoint();
+
+ size_t num_instruction_ranges = m_instruction_ranges.size();
+
+ // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions.
+ // I'll fix that but for now, just clear the list and it will go away nicely.
+ for (size_t i = 0; i < num_instruction_ranges; i++)
+ {
+ if (m_instruction_ranges[i])
+ m_instruction_ranges[i]->GetInstructionList().Clear();
+ }
+}
+
+void
+ThreadPlanStepRange::DidPush ()
+{
+ // See if we can find a "next range" breakpoint:
+ SetNextBranchBreakpoint();
+}
+
+bool
+ThreadPlanStepRange::ValidatePlan (Stream *error)
+{
+ return true;
+}
+
+Vote
+ThreadPlanStepRange::ShouldReportStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ const Vote vote = IsPlanComplete() ? eVoteYes : eVoteNo;
+ if (log)
+ log->Printf ("ThreadPlanStepRange::ShouldReportStop() returning vote %i\n", vote);
+ return vote;
+}
+
+void
+ThreadPlanStepRange::AddRange(const AddressRange &new_range)
+{
+ // For now I'm just adding the ranges. At some point we may want to
+ // condense the ranges if they overlap, though I don't think it is likely
+ // to be very important.
+ m_address_ranges.push_back (new_range);
+
+ // Fill the slot for this address range with an empty DisassemblerSP in the instruction ranges. I want the
+ // indices to match, but I don't want to do the work to disassemble this range if I don't step into it.
+ m_instruction_ranges.push_back (DisassemblerSP());
+}
+
+void
+ThreadPlanStepRange::DumpRanges(Stream *s)
+{
+ size_t num_ranges = m_address_ranges.size();
+ if (num_ranges == 1)
+ {
+ m_address_ranges[0].Dump (s, m_thread.CalculateTarget().get(), Address::DumpStyleLoadAddress);
+ }
+ else
+ {
+ for (size_t i = 0; i < num_ranges; i++)
+ {
+ s->PutCString("%d: ");
+ m_address_ranges[i].Dump (s, m_thread.CalculateTarget().get(), Address::DumpStyleLoadAddress);
+ }
+ }
+}
+
+bool
+ThreadPlanStepRange::InRange ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ bool ret_value = false;
+
+ lldb::addr_t pc_load_addr = m_thread.GetRegisterContext()->GetPC();
+
+ size_t num_ranges = m_address_ranges.size();
+ for (size_t i = 0; i < num_ranges; i++)
+ {
+ ret_value = m_address_ranges[i].ContainsLoadAddress(pc_load_addr, m_thread.CalculateTarget().get());
+ if (ret_value)
+ break;
+ }
+
+ if (!ret_value)
+ {
+ // See if we've just stepped to another part of the same line number...
+ StackFrame *frame = m_thread.GetStackFrameAtIndex(0).get();
+
+ SymbolContext new_context(frame->GetSymbolContext(eSymbolContextEverything));
+ if (m_addr_context.line_entry.IsValid() && new_context.line_entry.IsValid())
+ {
+ if (m_addr_context.line_entry.file == new_context.line_entry.file)
+ {
+ if (m_addr_context.line_entry.line == new_context.line_entry.line)
+ {
+ m_addr_context = new_context;
+ AddRange(m_addr_context.line_entry.range);
+ ret_value = true;
+ if (log)
+ {
+ StreamString s;
+ m_addr_context.line_entry.Dump (&s,
+ m_thread.CalculateTarget().get(),
+ true,
+ Address::DumpStyleLoadAddress,
+ Address::DumpStyleLoadAddress,
+ true);
+
+ log->Printf ("Step range plan stepped to another range of same line: %s", s.GetData());
+ }
+ }
+ else if (new_context.line_entry.range.GetBaseAddress().GetLoadAddress(m_thread.CalculateTarget().get())
+ != pc_load_addr)
+ {
+ // Another thing that sometimes happens here is that we step out of one line into the MIDDLE of another
+ // line. So far I mostly see this due to bugs in the debug information.
+ // But we probably don't want to be in the middle of a line range, so in that case reset the stepping
+ // range to the line we've stepped into the middle of and continue.
+ m_addr_context = new_context;
+ m_address_ranges.clear();
+ AddRange(m_addr_context.line_entry.range);
+ ret_value = true;
+ if (log)
+ {
+ StreamString s;
+ m_addr_context.line_entry.Dump (&s,
+ m_thread.CalculateTarget().get(),
+ true,
+ Address::DumpStyleLoadAddress,
+ Address::DumpStyleLoadAddress,
+ true);
+
+ log->Printf ("Step range plan stepped to the middle of new line(%d): %s, continuing to clear this line.",
+ new_context.line_entry.line,
+ s.GetData());
+ }
+
+ }
+ }
+
+ }
+
+ }
+
+ if (!ret_value && log)
+ log->Printf ("Step range plan out of range to 0x%" PRIx64, pc_load_addr);
+
+ return ret_value;
+}
+
+bool
+ThreadPlanStepRange::InSymbol()
+{
+ lldb::addr_t cur_pc = m_thread.GetRegisterContext()->GetPC();
+ if (m_addr_context.function != NULL)
+ {
+ return m_addr_context.function->GetAddressRange().ContainsLoadAddress (cur_pc, m_thread.CalculateTarget().get());
+ }
+ else if (m_addr_context.symbol)
+ {
+ AddressRange range(m_addr_context.symbol->GetAddress(), m_addr_context.symbol->GetByteSize());
+ return range.ContainsLoadAddress (cur_pc, m_thread.CalculateTarget().get());
+ }
+ return false;
+}
+
+// FIXME: This should also handle inlining if we aren't going to do inlining in the
+// main stack.
+//
+// Ideally we should remember the whole stack frame list, and then compare that
+// to the current list.
+
+lldb::FrameComparison
+ThreadPlanStepRange::CompareCurrentFrameToStartFrame()
+{
+ FrameComparison frame_order;
+
+ StackID cur_frame_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (cur_frame_id == m_stack_id)
+ {
+ frame_order = eFrameCompareEqual;
+ }
+ else if (cur_frame_id < m_stack_id)
+ {
+ frame_order = eFrameCompareYounger;
+ }
+ else
+ {
+ frame_order = eFrameCompareOlder;
+ }
+ return frame_order;
+}
+
+bool
+ThreadPlanStepRange::StopOthers ()
+{
+ if (m_stop_others == lldb::eOnlyThisThread
+ || m_stop_others == lldb::eOnlyDuringStepping)
+ return true;
+ else
+ return false;
+}
+
+InstructionList *
+ThreadPlanStepRange::GetInstructionsForAddress(lldb::addr_t addr, size_t &range_index, size_t &insn_offset)
+{
+ size_t num_ranges = m_address_ranges.size();
+ for (size_t i = 0; i < num_ranges; i++)
+ {
+ if (m_address_ranges[i].ContainsLoadAddress(addr, &GetTarget()))
+ {
+ // Some joker added a zero size range to the stepping range...
+ if (m_address_ranges[i].GetByteSize() == 0)
+ return NULL;
+
+ if (!m_instruction_ranges[i])
+ {
+ //Disassemble the address range given:
+ ExecutionContext exe_ctx (m_thread.GetProcess());
+ const char *plugin_name = NULL;
+ const char *flavor = NULL;
+ m_instruction_ranges[i] = Disassembler::DisassembleRange(GetTarget().GetArchitecture(),
+ plugin_name,
+ flavor,
+ exe_ctx,
+ m_address_ranges[i]);
+
+ }
+ if (!m_instruction_ranges[i])
+ return NULL;
+ else
+ {
+ // Find where we are in the instruction list as well. If we aren't at an instruction,
+ // return NULL. In this case, we're probably lost, and shouldn't try to do anything fancy.
+
+ insn_offset = m_instruction_ranges[i]->GetInstructionList().GetIndexOfInstructionAtLoadAddress(addr, GetTarget());
+ if (insn_offset == UINT32_MAX)
+ return NULL;
+ else
+ {
+ range_index = i;
+ return &m_instruction_ranges[i]->GetInstructionList();
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+void
+ThreadPlanStepRange::ClearNextBranchBreakpoint()
+{
+ if (m_next_branch_bp_sp)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf ("Removing next branch breakpoint: %d.", m_next_branch_bp_sp->GetID());
+ GetTarget().RemoveBreakpointByID (m_next_branch_bp_sp->GetID());
+ m_next_branch_bp_sp.reset();
+ }
+}
+
+bool
+ThreadPlanStepRange::SetNextBranchBreakpoint ()
+{
+ if (m_next_branch_bp_sp)
+ return true;
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ // Stepping through ranges using breakpoints doesn't work yet, but with this off we fall back to instruction
+ // single stepping.
+ if (!m_use_fast_step)
+ return false;
+
+ lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC();
+ // Find the current address in our address ranges, and fetch the disassembly if we haven't already:
+ size_t pc_index;
+ size_t range_index;
+ InstructionList *instructions = GetInstructionsForAddress (cur_addr, range_index, pc_index);
+ if (instructions == NULL)
+ return false;
+ else
+ {
+ uint32_t branch_index;
+ branch_index = instructions->GetIndexOfNextBranchInstruction (pc_index);
+
+ Address run_to_address;
+
+ // If we didn't find a branch, run to the end of the range.
+ if (branch_index == UINT32_MAX)
+ {
+ branch_index = instructions->GetSize() - 1;
+ }
+
+ if (branch_index - pc_index > 1)
+ {
+ const bool is_internal = true;
+ run_to_address = instructions->GetInstructionAtIndex(branch_index)->GetAddress();
+ m_next_branch_bp_sp = GetTarget().CreateBreakpoint(run_to_address, is_internal);
+ if (m_next_branch_bp_sp)
+ {
+ if (log)
+ {
+ lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID;
+ BreakpointLocationSP bp_loc = m_next_branch_bp_sp->GetLocationAtIndex(0);
+ if (bp_loc)
+ {
+ BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite();
+ if (bp_site)
+ {
+ bp_site_id = bp_site->GetID();
+ }
+ }
+ log->Printf ("ThreadPlanStepRange::SetNextBranchBreakpoint - Setting breakpoint %d (site %d) to run to address 0x%" PRIx64,
+ m_next_branch_bp_sp->GetID(),
+ bp_site_id,
+ run_to_address.GetLoadAddress(&m_thread.GetProcess()->GetTarget()));
+ }
+ m_next_branch_bp_sp->SetThreadID(m_thread.GetID());
+ m_next_branch_bp_sp->SetBreakpointKind ("next-branch-location");
+ return true;
+ }
+ else
+ return false;
+ }
+ }
+ return false;
+}
+
+bool
+ThreadPlanStepRange::NextRangeBreakpointExplainsStop (lldb::StopInfoSP stop_info_sp)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (!m_next_branch_bp_sp)
+ return false;
+
+ break_id_t bp_site_id = stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp = m_thread.GetProcess()->GetBreakpointSiteList().FindByID(bp_site_id);
+ if (!bp_site_sp)
+ return false;
+ else if (!bp_site_sp->IsBreakpointAtThisSite (m_next_branch_bp_sp->GetID()))
+ return false;
+ else
+ {
+ // If we've hit the next branch breakpoint, then clear it.
+ size_t num_owners = bp_site_sp->GetNumberOfOwners();
+ bool explains_stop = true;
+ // If all the owners are internal, then we are probably just stepping over this range from multiple threads,
+ // or multiple frames, so we want to continue. If one is not internal, then we should not explain the stop,
+ // and let the user breakpoint handle the stop.
+ for (size_t i = 0; i < num_owners; i++)
+ {
+ if (!bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint().IsInternal())
+ {
+ explains_stop = false;
+ break;
+ }
+ }
+ if (log)
+ log->Printf ("ThreadPlanStepRange::NextRangeBreakpointExplainsStop - Hit next range breakpoint which has %zu owners - explains stop: %u.",
+ num_owners,
+ explains_stop);
+ ClearNextBranchBreakpoint();
+ return explains_stop;
+ }
+}
+
+bool
+ThreadPlanStepRange::WillStop ()
+{
+ return true;
+}
+
+StateType
+ThreadPlanStepRange::GetPlanRunState ()
+{
+ if (m_next_branch_bp_sp)
+ return eStateRunning;
+ else
+ return eStateStepping;
+}
+
+bool
+ThreadPlanStepRange::MischiefManaged ()
+{
+ // If we have pushed some plans between ShouldStop & MischiefManaged, then we're not done...
+ // I do this check first because we might have stepped somewhere that will fool InRange into
+ // thinking it needs to step past the end of that line. This happens, for instance, when stepping
+ // over inlined code that is in the middle of the current line.
+
+ if (!m_no_more_plans)
+ return false;
+
+ bool done = true;
+ if (!IsPlanComplete())
+ {
+ if (InRange())
+ {
+ done = false;
+ }
+ else
+ {
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+ if (frame_order != eFrameCompareOlder)
+ {
+ if (m_no_more_plans)
+ done = true;
+ else
+ done = false;
+ }
+ else
+ done = true;
+ }
+ }
+
+ if (done)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step through range plan.");
+ ClearNextBranchBreakpoint();
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+}
+
+bool
+ThreadPlanStepRange::IsPlanStale ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ FrameComparison frame_order = CompareCurrentFrameToStartFrame();
+
+ if (frame_order == eFrameCompareOlder)
+ {
+ if (log)
+ {
+ log->Printf("ThreadPlanStepRange::IsPlanStale returning true, we've stepped out.");
+ }
+ return true;
+ }
+ else if (frame_order == eFrameCompareEqual && InSymbol())
+ {
+ // If we are not in a place we should step through, we've gotten stale.
+ // One tricky bit here is that some stubs don't push a frame, so we should.
+ // check that we are in the same symbol.
+ if (!InRange())
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/source/Target/ThreadPlanStepThrough.cpp b/source/Target/ThreadPlanStepThrough.cpp
new file mode 100644
index 000000000000..92d1fcd850d8
--- /dev/null
+++ b/source/Target/ThreadPlanStepThrough.cpp
@@ -0,0 +1,290 @@
+//===-- ThreadPlanStepThrough.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/Target/ThreadPlanStepThrough.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/DynamicLoader.h"
+#include "lldb/Target/ObjCLanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepThrough: If the current instruction is a trampoline, step through it
+// If it is the beginning of the prologue of a function, step through that as well.
+// FIXME: At present only handles DYLD trampolines.
+//----------------------------------------------------------------------
+
+ThreadPlanStepThrough::ThreadPlanStepThrough (Thread &thread, StackID &m_stack_id, bool stop_others) :
+ ThreadPlan (ThreadPlan::eKindStepThrough, "Step through trampolines and prologues", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_start_address (0),
+ m_backstop_bkpt_id (LLDB_INVALID_BREAK_ID),
+ m_backstop_addr(LLDB_INVALID_ADDRESS),
+ m_return_stack_id (m_stack_id),
+ m_stop_others (stop_others)
+{
+
+ LookForPlanToStepThroughFromCurrentPC();
+
+ // If we don't get a valid step through plan, don't bother to set up a backstop.
+ if (m_sub_plan_sp)
+ {
+ m_start_address = GetThread().GetRegisterContext()->GetPC(0);
+
+ // We are going to return back to the concrete frame 1, we might pass by some inlined code that we're in
+ // the middle of by doing this, but it's easier than trying to figure out where the inlined code might return to.
+
+ StackFrameSP return_frame_sp = m_thread.GetFrameWithStackID (m_stack_id);
+
+ if (return_frame_sp)
+ {
+ m_backstop_addr = return_frame_sp->GetFrameCodeAddress().GetLoadAddress(m_thread.CalculateTarget().get());
+ Breakpoint *return_bp = m_thread.GetProcess()->GetTarget().CreateBreakpoint (m_backstop_addr, true).get();
+ if (return_bp != NULL)
+ {
+ return_bp->SetThreadID(m_thread.GetID());
+ m_backstop_bkpt_id = return_bp->GetID();
+ return_bp->SetBreakpointKind("step-through-backstop");
+ }
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ log->Printf ("Setting backstop breakpoint %d at address: 0x%" PRIx64, m_backstop_bkpt_id, m_backstop_addr);
+ }
+ }
+ }
+}
+
+ThreadPlanStepThrough::~ThreadPlanStepThrough ()
+{
+ ClearBackstopBreakpoint ();
+}
+
+void
+ThreadPlanStepThrough::DidPush ()
+{
+ if (m_sub_plan_sp)
+ PushPlan(m_sub_plan_sp);
+}
+
+void
+ThreadPlanStepThrough::LookForPlanToStepThroughFromCurrentPC()
+{
+ m_sub_plan_sp = m_thread.GetProcess()->GetDynamicLoader()->GetStepThroughTrampolinePlan (m_thread, m_stop_others);
+ // If that didn't come up with anything, try the ObjC runtime plugin:
+ if (!m_sub_plan_sp.get())
+ {
+ ObjCLanguageRuntime *objc_runtime = m_thread.GetProcess()->GetObjCLanguageRuntime();
+ if (objc_runtime)
+ m_sub_plan_sp = objc_runtime->GetStepThroughTrampolinePlan (m_thread, m_stop_others);
+ }
+
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ {
+ lldb::addr_t current_address = GetThread().GetRegisterContext()->GetPC(0);
+ if (m_sub_plan_sp)
+ {
+ StreamString s;
+ m_sub_plan_sp->GetDescription(&s, lldb::eDescriptionLevelFull);
+ log->Printf ("Found step through plan from 0x%" PRIx64 ": %s", current_address, s.GetData());
+ }
+ else
+ {
+ log->Printf ("Couldn't find step through plan from address 0x%" PRIx64 ".", current_address);
+ }
+ }
+}
+
+void
+ThreadPlanStepThrough::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ s->Printf ("Step through");
+ else
+ {
+ s->PutCString ("Stepping through trampoline code from: ");
+ s->Address(m_start_address, sizeof (addr_t));
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID)
+ {
+ s->Printf (" with backstop breakpoint id: %d at address: ", m_backstop_bkpt_id);
+ s->Address (m_backstop_addr, sizeof (addr_t));
+ }
+ else
+ s->PutCString (" unable to set a backstop breakpoint.");
+ }
+}
+
+bool
+ThreadPlanStepThrough::ValidatePlan (Stream *error)
+{
+ return m_sub_plan_sp.get() != NULL;
+}
+
+bool
+ThreadPlanStepThrough::DoPlanExplainsStop (Event *event_ptr)
+{
+ // If we have a sub-plan, it will have been asked first if we explain the stop, and
+ // we won't get asked. The only time we would be the one directly asked this question
+ // is if we hit our backstop breakpoint.
+
+ if (HitOurBackstopBreakpoint())
+ return true;
+ else
+ return false;
+}
+
+bool
+ThreadPlanStepThrough::ShouldStop (Event *event_ptr)
+{
+ // If we've already marked ourselves done, then we're done...
+ if (IsPlanComplete())
+ return true;
+
+ // First, did we hit the backstop breakpoint?
+ if (HitOurBackstopBreakpoint())
+ {
+ SetPlanComplete(false);
+ return true;
+ }
+
+ // If we don't have a sub-plan, then we're also done (can't see how we would ever get here
+ // without a plan, but just in case.
+
+ if (!m_sub_plan_sp)
+ {
+ SetPlanComplete();
+ return true;
+ }
+
+ // If the current sub plan is not done, we don't want to stop. Actually, we probably won't
+ // ever get here in this state, since we generally won't get asked any questions if out
+ // current sub-plan is not done...
+ if (!m_sub_plan_sp->IsPlanComplete())
+ return false;
+
+ // If our current sub plan failed, then let's just run to our backstop. If we can't do that then just stop.
+ if (!m_sub_plan_sp->PlanSucceeded())
+ {
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID)
+ {
+ m_sub_plan_sp.reset();
+ return false;
+ }
+ else
+ {
+ SetPlanComplete(false);
+ return true;
+ }
+ }
+
+ // Next see if there is a specific step through plan at our current pc (these might
+ // chain, for instance stepping through a dylib trampoline to the objc dispatch function...)
+ LookForPlanToStepThroughFromCurrentPC();
+ if (m_sub_plan_sp)
+ {
+ PushPlan (m_sub_plan_sp);
+ return false;
+ }
+ else
+ {
+ SetPlanComplete();
+ return true;
+ }
+}
+
+bool
+ThreadPlanStepThrough::StopOthers ()
+{
+ return m_stop_others;
+}
+
+StateType
+ThreadPlanStepThrough::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+bool
+ThreadPlanStepThrough::DoWillResume (StateType resume_state, bool current_plan)
+{
+ return true;
+}
+
+bool
+ThreadPlanStepThrough::WillStop ()
+{
+ return true;
+}
+
+void
+ThreadPlanStepThrough::ClearBackstopBreakpoint ()
+{
+ if (m_backstop_bkpt_id != LLDB_INVALID_BREAK_ID)
+ {
+ m_thread.GetProcess()->GetTarget().RemoveBreakpointByID (m_backstop_bkpt_id);
+ m_backstop_bkpt_id = LLDB_INVALID_BREAK_ID;
+ }
+}
+
+bool
+ThreadPlanStepThrough::MischiefManaged ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (!IsPlanComplete())
+ {
+ return false;
+ }
+ else
+ {
+ if (log)
+ log->Printf("Completed step through step plan.");
+
+ ClearBackstopBreakpoint ();
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+}
+
+bool
+ThreadPlanStepThrough::HitOurBackstopBreakpoint()
+{
+ StopInfoSP stop_info_sp(m_thread.GetStopInfo());
+ if (stop_info_sp && stop_info_sp->GetStopReason() == eStopReasonBreakpoint)
+ {
+ break_id_t stop_value = (break_id_t) stop_info_sp->GetValue();
+ BreakpointSiteSP cur_site_sp = m_thread.GetProcess()->GetBreakpointSiteList().FindByID(stop_value);
+ if (cur_site_sp && cur_site_sp->IsBreakpointAtThisSite(m_backstop_bkpt_id))
+ {
+ StackID cur_frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (cur_frame_zero_id == m_return_stack_id)
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->PutCString ("ThreadPlanStepThrough hit backstop breakpoint.");
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
diff --git a/source/Target/ThreadPlanStepUntil.cpp b/source/Target/ThreadPlanStepUntil.cpp
new file mode 100644
index 000000000000..e1b5a0560c75
--- /dev/null
+++ b/source/Target/ThreadPlanStepUntil.cpp
@@ -0,0 +1,413 @@
+//===-- ThreadPlanStepUntil.cpp ---------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//m_should_stop
+
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Target/ThreadPlanStepUntil.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/lldb-private-log.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanStepUntil: Run until we reach a given line number or step out of the current frame
+//----------------------------------------------------------------------
+
+ThreadPlanStepUntil::ThreadPlanStepUntil
+(
+ Thread &thread,
+ lldb::addr_t *address_list,
+ size_t num_addresses,
+ bool stop_others,
+ uint32_t frame_idx
+) :
+ ThreadPlan (ThreadPlan::eKindStepUntil, "Step until", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_step_from_insn (LLDB_INVALID_ADDRESS),
+ m_return_bp_id (LLDB_INVALID_BREAK_ID),
+ m_return_addr (LLDB_INVALID_ADDRESS),
+ m_stepped_out (false),
+ m_should_stop (false),
+ m_ran_analyze (false),
+ m_explains_stop (false),
+ m_until_points (),
+ m_stop_others (stop_others)
+{
+ // Stash away our "until" addresses:
+ TargetSP target_sp (m_thread.CalculateTarget());
+
+ StackFrameSP frame_sp (m_thread.GetStackFrameAtIndex (frame_idx));
+ if (frame_sp)
+ {
+ m_step_from_insn = frame_sp->GetStackID().GetPC();
+ lldb::user_id_t thread_id = m_thread.GetID();
+
+ // Find the return address and set a breakpoint there:
+ // FIXME - can we do this more securely if we know first_insn?
+
+ StackFrameSP return_frame_sp (m_thread.GetStackFrameAtIndex(frame_idx + 1));
+ if (return_frame_sp)
+ {
+ // TODO: add inline functionality
+ m_return_addr = return_frame_sp->GetStackID().GetPC();
+ Breakpoint *return_bp = target_sp->CreateBreakpoint (m_return_addr, true).get();
+ if (return_bp != NULL)
+ {
+ return_bp->SetThreadID(thread_id);
+ m_return_bp_id = return_bp->GetID();
+ return_bp->SetBreakpointKind ("until-return-backstop");
+ }
+ }
+
+ m_stack_id = m_thread.GetStackFrameAtIndex(frame_idx)->GetStackID();
+
+ // Now set breakpoints on all our return addresses:
+ for (size_t i = 0; i < num_addresses; i++)
+ {
+ Breakpoint *until_bp = target_sp->CreateBreakpoint (address_list[i], true).get();
+ if (until_bp != NULL)
+ {
+ until_bp->SetThreadID(thread_id);
+ m_until_points[address_list[i]] = until_bp->GetID();
+ until_bp->SetBreakpointKind("until-target");
+ }
+ else
+ {
+ m_until_points[address_list[i]] = LLDB_INVALID_BREAK_ID;
+ }
+ }
+ }
+}
+
+ThreadPlanStepUntil::~ThreadPlanStepUntil ()
+{
+ Clear();
+}
+
+void
+ThreadPlanStepUntil::Clear()
+{
+ TargetSP target_sp (m_thread.CalculateTarget());
+ if (target_sp)
+ {
+ if (m_return_bp_id != LLDB_INVALID_BREAK_ID)
+ {
+ target_sp->RemoveBreakpointByID(m_return_bp_id);
+ m_return_bp_id = LLDB_INVALID_BREAK_ID;
+ }
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ target_sp->RemoveBreakpointByID((*pos).second);
+ }
+ }
+ m_until_points.clear();
+}
+
+void
+ThreadPlanStepUntil::GetDescription (Stream *s, lldb::DescriptionLevel level)
+{
+ if (level == lldb::eDescriptionLevelBrief)
+ {
+ s->Printf ("step until");
+ if (m_stepped_out)
+ s->Printf (" - stepped out");
+ }
+ else
+ {
+ if (m_until_points.size() == 1)
+ s->Printf ("Stepping from address 0x%" PRIx64 " until we reach 0x%" PRIx64 " using breakpoint %d",
+ (uint64_t)m_step_from_insn,
+ (uint64_t) (*m_until_points.begin()).first,
+ (*m_until_points.begin()).second);
+ else
+ {
+ until_collection::iterator pos, end = m_until_points.end();
+ s->Printf ("Stepping from address 0x%" PRIx64 " until we reach one of:",
+ (uint64_t)m_step_from_insn);
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ s->Printf ("\n\t0x%" PRIx64 " (bp: %d)", (uint64_t) (*pos).first, (*pos).second);
+ }
+ }
+ s->Printf(" stepped out address is 0x%" PRIx64 ".", (uint64_t) m_return_addr);
+ }
+}
+
+bool
+ThreadPlanStepUntil::ValidatePlan (Stream *error)
+{
+ if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
+ return false;
+ else
+ {
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ if (!LLDB_BREAK_ID_IS_VALID ((*pos).second))
+ return false;
+ }
+ return true;
+ }
+}
+
+void
+ThreadPlanStepUntil::AnalyzeStop()
+{
+ if (m_ran_analyze)
+ return;
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ m_should_stop = true;
+ m_explains_stop = false;
+
+ if (stop_info_sp)
+ {
+ StopReason reason = stop_info_sp->GetStopReason();
+
+ switch (reason)
+ {
+ case eStopReasonBreakpoint:
+ {
+ // If this is OUR breakpoint, we're fine, otherwise we don't know why this happened...
+ BreakpointSiteSP this_site = m_thread.GetProcess()->GetBreakpointSiteList().FindByID (stop_info_sp->GetValue());
+ if (!this_site)
+ {
+ m_explains_stop = false;
+ return;
+ }
+
+ if (this_site->IsBreakpointAtThisSite (m_return_bp_id))
+ {
+ // If we are at our "step out" breakpoint, and the stack depth has shrunk, then
+ // this is indeed our stop.
+ // If the stack depth has grown, then we've hit our step out breakpoint recursively.
+ // If we are the only breakpoint at that location, then we do explain the stop, and
+ // we'll just continue.
+ // If there was another breakpoint here, then we don't explain the stop, but we won't
+ // mark ourselves Completed, because maybe that breakpoint will continue, and then
+ // we'll finish the "until".
+ bool done;
+ StackID cur_frame_zero_id;
+
+ if (m_stack_id < cur_frame_zero_id)
+ done = true;
+ else
+ done = false;
+
+ if (done)
+ {
+ m_stepped_out = true;
+ SetPlanComplete();
+ }
+ else
+ m_should_stop = false;
+
+ if (this_site->GetNumberOfOwners() == 1)
+ m_explains_stop = true;
+ else
+ m_explains_stop = false;
+ return;
+ }
+ else
+ {
+ // Check if we've hit one of our "until" breakpoints.
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ if (this_site->IsBreakpointAtThisSite ((*pos).second))
+ {
+ // If we're at the right stack depth, then we're done.
+
+ bool done;
+ StackID frame_zero_id = m_thread.GetStackFrameAtIndex(0)->GetStackID();
+
+ if (frame_zero_id == m_stack_id)
+ done = true;
+ else if (frame_zero_id < m_stack_id)
+ done = false;
+ else
+ {
+ StackFrameSP older_frame_sp = m_thread.GetStackFrameAtIndex(1);
+
+ // But if we can't even unwind one frame we should just get out of here & stop...
+ if (older_frame_sp)
+ {
+ const SymbolContext &older_context
+ = older_frame_sp->GetSymbolContext(eSymbolContextEverything);
+ SymbolContext stack_context;
+ m_stack_id.GetSymbolContextScope()->CalculateSymbolContext(&stack_context);
+
+ if (older_context == stack_context)
+ done = true;
+ else
+ done = false;
+ }
+ else
+ done = false;
+ }
+
+ if (done)
+ SetPlanComplete();
+ else
+ m_should_stop = false;
+
+ // Otherwise we've hit this breakpoint recursively. If we're the
+ // only breakpoint here, then we do explain the stop, and we'll continue.
+ // If not then we should let higher plans handle this stop.
+ if (this_site->GetNumberOfOwners() == 1)
+ m_explains_stop = true;
+ else
+ {
+ m_should_stop = true;
+ m_explains_stop = false;
+ }
+ return;
+ }
+ }
+ }
+ // If we get here we haven't hit any of our breakpoints, so let the higher
+ // plans take care of the stop.
+ m_explains_stop = false;
+ return;
+ }
+ case eStopReasonWatchpoint:
+ case eStopReasonSignal:
+ case eStopReasonException:
+ case eStopReasonExec:
+ case eStopReasonThreadExiting:
+ m_explains_stop = false;
+ break;
+ default:
+ m_explains_stop = true;
+ break;
+ }
+ }
+}
+
+bool
+ThreadPlanStepUntil::DoPlanExplainsStop (Event *event_ptr)
+{
+ // We don't explain signals or breakpoints (breakpoints that handle stepping in or
+ // out will be handled by a child plan.
+ AnalyzeStop();
+ return m_explains_stop;
+}
+
+bool
+ThreadPlanStepUntil::ShouldStop (Event *event_ptr)
+{
+ // If we've told our self in ExplainsStop that we plan to continue, then
+ // do so here. Otherwise, as long as this thread has stopped for a reason,
+ // we will stop.
+
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+ if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone)
+ return false;
+
+ AnalyzeStop();
+ return m_should_stop;
+}
+
+bool
+ThreadPlanStepUntil::StopOthers ()
+{
+ return m_stop_others;
+}
+
+StateType
+ThreadPlanStepUntil::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+bool
+ThreadPlanStepUntil::DoWillResume (StateType resume_state, bool current_plan)
+{
+ if (current_plan)
+ {
+ TargetSP target_sp (m_thread.CalculateTarget());
+ if (target_sp)
+ {
+ Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != NULL)
+ return_bp->SetEnabled (true);
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get();
+ if (until_bp != NULL)
+ until_bp->SetEnabled (true);
+ }
+ }
+ }
+
+ m_should_stop = true;
+ m_ran_analyze = false;
+ m_explains_stop = false;
+ return true;
+}
+
+bool
+ThreadPlanStepUntil::WillStop ()
+{
+ TargetSP target_sp (m_thread.CalculateTarget());
+ if (target_sp)
+ {
+ Breakpoint *return_bp = target_sp->GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != NULL)
+ return_bp->SetEnabled (false);
+
+ until_collection::iterator pos, end = m_until_points.end();
+ for (pos = m_until_points.begin(); pos != end; pos++)
+ {
+ Breakpoint *until_bp = target_sp->GetBreakpointByID((*pos).second).get();
+ if (until_bp != NULL)
+ until_bp->SetEnabled (false);
+ }
+ }
+ return true;
+}
+
+bool
+ThreadPlanStepUntil::MischiefManaged ()
+{
+
+ // I'm letting "PlanExplainsStop" do all the work, and just reporting that here.
+ bool done = false;
+ if (IsPlanComplete())
+ {
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf("Completed step until plan.");
+
+ Clear();
+ done = true;
+ }
+ if (done)
+ ThreadPlan::MischiefManaged ();
+
+ return done;
+
+}
+
diff --git a/source/Target/ThreadPlanTracer.cpp b/source/Target/ThreadPlanTracer.cpp
new file mode 100644
index 000000000000..af6cef7ecc5a
--- /dev/null
+++ b/source/Target/ThreadPlanTracer.cpp
@@ -0,0 +1,286 @@
+//===-- ThreadPlan.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/lldb-python.h"
+
+#include "lldb/Target/ThreadPlan.h"
+
+// C Includes
+#include <string.h>
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Core/ArchSpec.h"
+#include "lldb/Core/DataBufferHeap.h"
+#include "lldb/Core/Debugger.h"
+#include "lldb/Core/Disassembler.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/State.h"
+#include "lldb/Core/Value.h"
+#include "lldb/Symbol/TypeList.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/Target.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+#pragma mark ThreadPlanTracer
+
+ThreadPlanTracer::ThreadPlanTracer (Thread &thread, lldb::StreamSP &stream_sp) :
+ m_thread (thread),
+ m_single_step(true),
+ m_enabled (false),
+ m_stream_sp (stream_sp)
+{
+}
+
+ThreadPlanTracer::ThreadPlanTracer (Thread &thread) :
+ m_thread (thread),
+ m_single_step(true),
+ m_enabled (false),
+ m_stream_sp ()
+{
+}
+
+Stream *
+ThreadPlanTracer::GetLogStream ()
+{
+
+ if (m_stream_sp.get())
+ return m_stream_sp.get();
+ else
+ {
+ TargetSP target_sp (m_thread.CalculateTarget());
+ if (target_sp)
+ return &target_sp->GetDebugger().GetOutputStream();
+ }
+ return NULL;
+}
+
+void
+ThreadPlanTracer::Log()
+{
+ SymbolContext sc;
+ bool show_frame_index = false;
+ bool show_fullpaths = false;
+
+ Stream *stream = GetLogStream();
+ if (stream)
+ {
+ m_thread.GetStackFrameAtIndex(0)->Dump (stream, show_frame_index, show_fullpaths);
+ stream->Printf("\n");
+ stream->Flush();
+ }
+
+}
+
+bool
+ThreadPlanTracer::TracerExplainsStop ()
+{
+ if (m_enabled && m_single_step)
+ {
+ lldb::StopInfoSP stop_info = m_thread.GetStopInfo();
+ if (stop_info->GetStopReason() == eStopReasonTrace)
+ return true;
+ else
+ return false;
+ }
+ else
+ return false;
+}
+
+#pragma mark ThreadPlanAssemblyTracer
+
+ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer (Thread &thread, lldb::StreamSP &stream_sp) :
+ ThreadPlanTracer (thread, stream_sp),
+ m_disassembler_sp (),
+ m_intptr_type (),
+ m_register_values ()
+{
+}
+
+ThreadPlanAssemblyTracer::ThreadPlanAssemblyTracer (Thread &thread) :
+ ThreadPlanTracer (thread),
+ m_disassembler_sp (),
+ m_intptr_type (),
+ m_register_values ()
+{
+}
+
+Disassembler *
+ThreadPlanAssemblyTracer::GetDisassembler ()
+{
+ if (m_disassembler_sp.get() == NULL)
+ m_disassembler_sp = Disassembler::FindPlugin(m_thread.GetProcess()->GetTarget().GetArchitecture(), NULL, NULL);
+ return m_disassembler_sp.get();
+}
+
+TypeFromUser
+ThreadPlanAssemblyTracer::GetIntPointerType()
+{
+ if (!m_intptr_type.IsValid ())
+ {
+ TargetSP target_sp (m_thread.CalculateTarget());
+ if (target_sp)
+ {
+ Module *exe_module = target_sp->GetExecutableModulePointer();
+
+ if (exe_module)
+ {
+ m_intptr_type = TypeFromUser(exe_module->GetClangASTContext().GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, target_sp->GetArchitecture().GetAddressByteSize() * 8));
+ }
+ }
+ }
+ return m_intptr_type;
+}
+
+
+
+ThreadPlanAssemblyTracer::~ThreadPlanAssemblyTracer()
+{
+}
+
+void
+ThreadPlanAssemblyTracer::TracingStarted ()
+{
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ if (m_register_values.size() == 0)
+ m_register_values.resize (reg_ctx->GetRegisterCount());
+}
+
+void
+ThreadPlanAssemblyTracer::TracingEnded ()
+{
+ m_register_values.clear();
+}
+
+static void
+PadOutTo (StreamString &stream, int target)
+{
+ stream.Flush();
+
+ int length = stream.GetString().length();
+
+ if (length + 1 < target)
+ stream.Printf("%*s", target - (length + 1) + 1, "");
+}
+
+void
+ThreadPlanAssemblyTracer::Log ()
+{
+ Stream *stream = GetLogStream ();
+
+ if (!stream)
+ return;
+
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ lldb::addr_t pc = reg_ctx->GetPC();
+ ProcessSP process_sp (m_thread.GetProcess());
+ Address pc_addr;
+ bool addr_valid = false;
+ uint8_t buffer[16] = {0}; // Must be big enough for any single instruction
+ addr_valid = process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress (pc, pc_addr);
+
+ pc_addr.Dump(stream, &m_thread, Address::DumpStyleResolvedDescription, Address::DumpStyleModuleWithFileAddress);
+ stream->PutCString (" ");
+
+ Disassembler *disassembler = GetDisassembler();
+ if (disassembler)
+ {
+ Error err;
+ process_sp->ReadMemory(pc, buffer, sizeof(buffer), err);
+
+ if (err.Success())
+ {
+ DataExtractor extractor(buffer, sizeof(buffer),
+ process_sp->GetByteOrder(),
+ process_sp->GetAddressByteSize());
+
+ bool data_from_file = false;
+ if (addr_valid)
+ disassembler->DecodeInstructions (pc_addr, extractor, 0, 1, false, data_from_file);
+ else
+ disassembler->DecodeInstructions (Address (pc), extractor, 0, 1, false, data_from_file);
+
+ InstructionList &instruction_list = disassembler->GetInstructionList();
+ const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize();
+
+ if (instruction_list.GetSize())
+ {
+ const bool show_bytes = true;
+ const bool show_address = true;
+ Instruction *instruction = instruction_list.GetInstructionAtIndex(0).get();
+ instruction->Dump (stream,
+ max_opcode_byte_size,
+ show_address,
+ show_bytes,
+ NULL);
+ }
+ }
+ }
+
+ const ABI *abi = process_sp->GetABI().get();
+ TypeFromUser intptr_type = GetIntPointerType();
+
+ if (abi && intptr_type.IsValid())
+ {
+ ValueList value_list;
+ const int num_args = 1;
+
+ for (int arg_index = 0; arg_index < num_args; ++arg_index)
+ {
+ Value value;
+ value.SetValueType (Value::eValueTypeScalar);
+// value.SetContext (Value::eContextTypeClangType, intptr_type.GetOpaqueQualType());
+ value.SetClangType (intptr_type);
+ value_list.PushValue (value);
+ }
+
+ if (abi->GetArgumentValues (m_thread, value_list))
+ {
+ for (int arg_index = 0; arg_index < num_args; ++arg_index)
+ {
+ stream->Printf("\n\targ[%d]=%llx", arg_index, value_list.GetValueAtIndex(arg_index)->GetScalar().ULongLong());
+
+ if (arg_index + 1 < num_args)
+ stream->PutCString (", ");
+ }
+ }
+ }
+
+
+ RegisterValue reg_value;
+ for (uint32_t reg_num = 0, num_registers = reg_ctx->GetRegisterCount();
+ reg_num < num_registers;
+ ++reg_num)
+ {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex(reg_num);
+ if (reg_ctx->ReadRegister (reg_info, reg_value))
+ {
+ assert (reg_num < m_register_values.size());
+ if (m_register_values[reg_num].GetType() == RegisterValue::eTypeInvalid ||
+ reg_value != m_register_values[reg_num])
+ {
+ if (reg_value.GetType() != RegisterValue::eTypeInvalid)
+ {
+ stream->PutCString ("\n\t");
+ reg_value.Dump(stream, reg_info, true, false, eFormatDefault);
+ }
+ }
+ m_register_values[reg_num] = reg_value;
+ }
+ }
+ stream->EOL();
+ stream->Flush();
+}
diff --git a/source/Target/ThreadSpec.cpp b/source/Target/ThreadSpec.cpp
new file mode 100644
index 000000000000..cb54469ba901
--- /dev/null
+++ b/source/Target/ThreadSpec.cpp
@@ -0,0 +1,158 @@
+//===-- ThreadSpec.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/Target/Thread.h"
+#include "lldb/Target/ThreadSpec.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+ThreadSpec::ThreadSpec() :
+ m_index (UINT32_MAX),
+ m_tid (LLDB_INVALID_THREAD_ID),
+ m_name(),
+ m_queue_name ()
+{
+}
+
+ThreadSpec::ThreadSpec (const ThreadSpec &rhs) :
+ m_index(rhs.m_index),
+ m_tid(rhs.m_tid),
+ m_name(rhs.m_name),
+ m_queue_name(rhs.m_queue_name)
+{
+}
+
+const ThreadSpec &
+ThreadSpec::operator=(const ThreadSpec &rhs)
+{
+ m_index = rhs.m_index;
+ m_tid = rhs.m_tid;
+ m_name = rhs.m_name;
+ m_queue_name = rhs.m_queue_name;
+ return *this;
+}
+
+const char *
+ThreadSpec::GetName () const
+{
+ if (m_name.empty())
+ return NULL;
+ else
+ return m_name.c_str();
+}
+
+const char *
+ThreadSpec::GetQueueName () const
+{
+ if (m_queue_name.empty())
+ return NULL;
+ else
+ return m_queue_name.c_str();
+}
+
+bool
+ThreadSpec::TIDMatches (Thread &thread) const
+{
+ if (m_tid == LLDB_INVALID_THREAD_ID)
+ return true;
+
+ lldb::tid_t thread_id = thread.GetID();
+ return TIDMatches (thread_id);
+}
+bool
+ThreadSpec::IndexMatches (Thread &thread) const
+{
+ if (m_index == UINT32_MAX)
+ return true;
+ uint32_t index = thread.GetIndexID();
+ return IndexMatches (index);
+}
+bool
+ThreadSpec::NameMatches (Thread &thread) const
+{
+ if (m_name.empty())
+ return true;
+
+ const char *name = thread.GetName();
+ return NameMatches (name);
+}
+bool
+ThreadSpec::QueueNameMatches (Thread &thread) const
+{
+ if (m_queue_name.empty())
+ return true;
+
+ const char *queue_name = thread.GetQueueName();
+ return QueueNameMatches (queue_name);
+}
+
+bool
+ThreadSpec::ThreadPassesBasicTests (Thread &thread) const
+{
+
+ if (!HasSpecification())
+ return true;
+
+ if (!TIDMatches(thread))
+ return false;
+
+ if (!IndexMatches(thread))
+ return false;
+
+ if (!NameMatches (thread))
+ return false;
+
+ if (!QueueNameMatches (thread))
+ return false;
+
+ return true;
+
+}
+
+bool
+ThreadSpec::HasSpecification() const
+{
+ return (m_index != UINT32_MAX || m_tid != LLDB_INVALID_THREAD_ID || !m_name.empty() || !m_queue_name.empty());
+}
+void
+ThreadSpec::GetDescription (Stream *s, lldb::DescriptionLevel level) const
+{
+ if (!HasSpecification())
+ {
+ if (level == eDescriptionLevelBrief)
+ {
+ s->PutCString("thread spec: no ");
+ }
+ }
+ else
+ {
+ if (level == eDescriptionLevelBrief)
+ {
+ s->PutCString("thread spec: yes ");
+ }
+ else
+ {
+ if (GetTID() != LLDB_INVALID_THREAD_ID)
+ s->Printf("tid: 0x%" PRIx64 " ", GetTID());
+
+ if (GetIndex() != UINT32_MAX)
+ s->Printf("index: %d ", GetIndex());
+
+ const char *name = GetName();
+ if (name)
+ s->Printf ("thread name: \"%s\" ", name);
+
+ const char *queue_name = GetQueueName();
+ if (queue_name)
+ s->Printf ("queue name: \"%s\" ", queue_name);
+ }
+
+ }
+}
diff --git a/source/Target/UnixSignals.cpp b/source/Target/UnixSignals.cpp
new file mode 100644
index 000000000000..7fe81f85e857
--- /dev/null
+++ b/source/Target/UnixSignals.cpp
@@ -0,0 +1,293 @@
+//===-- UnixSignals.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/Target/UnixSignals.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/Interpreter/Args.h"
+
+using namespace lldb_private;
+
+UnixSignals::Signal::Signal
+(
+ const char *name,
+ const char *short_name,
+ bool default_suppress,
+ bool default_stop,
+ bool default_notify,
+ const char *description
+) :
+ m_name (name),
+ m_short_name (short_name),
+ m_description (),
+ m_suppress (default_suppress),
+ m_stop (default_stop),
+ m_notify (default_notify)
+{
+ if (description)
+ m_description.assign (description);
+}
+
+//----------------------------------------------------------------------
+// UnixSignals constructor
+//----------------------------------------------------------------------
+UnixSignals::UnixSignals ()
+{
+ Reset ();
+}
+
+//----------------------------------------------------------------------
+// Destructor
+//----------------------------------------------------------------------
+UnixSignals::~UnixSignals ()
+{
+}
+
+void
+UnixSignals::Reset ()
+{
+ // This builds one standard set of Unix Signals. If yours aren't quite in this
+ // order, you can either subclass this class, and use Add & Remove to change them
+ // or you can subclass and build them afresh in your constructor;
+ m_signals.clear();
+ // SIGNO NAME SHORT NAME SUPPRESS STOP NOTIFY DESCRIPTION
+ // ====== ============ ========== ======== ====== ====== ===================================================
+ AddSignal (1, "SIGHUP", "HUP", false, true , true , "hangup");
+ AddSignal (2, "SIGINT", "INT", true , true , true , "interrupt");
+ AddSignal (3, "SIGQUIT", "QUIT", false, true , true , "quit");
+ AddSignal (4, "SIGILL", "ILL", false, true , true , "illegal instruction");
+ AddSignal (5, "SIGTRAP", "TRAP", true , true , true , "trace trap (not reset when caught)");
+ AddSignal (6, "SIGABRT", "ABRT", false, true , true , "abort()");
+ AddSignal (7, "SIGEMT", "EMT", false, true , true , "pollable event");
+ AddSignal (8, "SIGFPE", "FPE", false, true , true , "floating point exception");
+ AddSignal (9, "SIGKILL", "KILL", false, true , true , "kill");
+ AddSignal (10, "SIGBUS", "BUS", false, true , true , "bus error");
+ AddSignal (11, "SIGSEGV", "SEGV", false, true , true , "segmentation violation");
+ AddSignal (12, "SIGSYS", "SYS", false, true , true , "bad argument to system call");
+ AddSignal (13, "SIGPIPE", "PIPE", false, true , true , "write on a pipe with no one to read it");
+ AddSignal (14, "SIGALRM", "ALRM", false, false, true , "alarm clock");
+ AddSignal (15, "SIGTERM", "TERM", false, true , true , "software termination signal from kill");
+ AddSignal (16, "SIGURG", "URG", false, false, false, "urgent condition on IO channel");
+ AddSignal (17, "SIGSTOP", "STOP", true , true , true , "sendable stop signal not from tty");
+ AddSignal (18, "SIGTSTP", "TSTP", false, true , true , "stop signal from tty");
+ AddSignal (19, "SIGCONT", "CONT", false, true , true , "continue a stopped process");
+ AddSignal (20, "SIGCHLD", "CHLD", false, false, true , "to parent on child stop or exit");
+ AddSignal (21, "SIGTTIN", "TTIN", false, true , true , "to readers process group upon background tty read");
+ AddSignal (22, "SIGTTOU", "TTOU", false, true , true , "to readers process group upon background tty write");
+ AddSignal (23, "SIGIO", "IO", false, false, false, "input/output possible signal");
+ AddSignal (24, "SIGXCPU", "XCPU", false, true , true , "exceeded CPU time limit");
+ AddSignal (25, "SIGXFSZ", "XFSZ", false, true , true , "exceeded file size limit");
+ AddSignal (26, "SIGVTALRM", "VTALRM", false, false, false, "virtual time alarm");
+ AddSignal (27, "SIGPROF", "PROF", false, false, false, "profiling time alarm");
+ AddSignal (28, "SIGWINCH", "WINCH", false, false, false, "window size changes");
+ AddSignal (29, "SIGINFO", "INFO", false, true , true , "information request");
+ AddSignal (30, "SIGUSR1", "USR1", false, true , true , "user defined signal 1");
+ AddSignal (31, "SIGUSR2", "USR2", false, true , true , "user defined signal 2");
+}
+
+void
+UnixSignals::AddSignal
+(
+ int signo,
+ const char *name,
+ const char *short_name,
+ bool default_suppress,
+ bool default_stop,
+ bool default_notify,
+ const char *description
+)
+{
+ Signal new_signal (name, short_name, default_suppress, default_stop, default_notify, description);
+ m_signals.insert (std::make_pair(signo, new_signal));
+}
+
+void
+UnixSignals::RemoveSignal (int signo)
+{
+ collection::iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ m_signals.erase (pos);
+}
+
+const char *
+UnixSignals::GetSignalAsCString (int signo) const
+{
+ collection::const_iterator pos = m_signals.find (signo);
+ if (pos == m_signals.end())
+ return NULL;
+ else
+ return pos->second.m_name.GetCString ();
+}
+
+
+bool
+UnixSignals::SignalIsValid (int32_t signo) const
+{
+ return m_signals.find (signo) != m_signals.end();
+}
+
+
+int32_t
+UnixSignals::GetSignalNumberFromName (const char *name) const
+{
+ ConstString const_name (name);
+
+ collection::const_iterator pos, end = m_signals.end ();
+ for (pos = m_signals.begin (); pos != end; pos++)
+ {
+ if ((const_name == pos->second.m_name) || (const_name == pos->second.m_short_name))
+ return pos->first;
+ }
+
+ const int32_t signo = Args::StringToSInt32(name, LLDB_INVALID_SIGNAL_NUMBER, 0);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return signo;
+ return LLDB_INVALID_SIGNAL_NUMBER;
+}
+
+int32_t
+UnixSignals::GetFirstSignalNumber () const
+{
+ if (m_signals.empty())
+ return LLDB_INVALID_SIGNAL_NUMBER;
+
+ return (*m_signals.begin ()).first;
+}
+
+int32_t
+UnixSignals::GetNextSignalNumber (int32_t current_signal) const
+{
+ collection::const_iterator pos = m_signals.find (current_signal);
+ collection::const_iterator end = m_signals.end();
+ if (pos == end)
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ else
+ {
+ pos++;
+ if (pos == end)
+ return LLDB_INVALID_SIGNAL_NUMBER;
+ else
+ return pos->first;
+ }
+}
+
+const char *
+UnixSignals::GetSignalInfo
+(
+ int32_t signo,
+ bool &should_suppress,
+ bool &should_stop,
+ bool &should_notify
+) const
+{
+ collection::const_iterator pos = m_signals.find (signo);
+ if (pos == m_signals.end())
+ return NULL;
+ else
+ {
+ const Signal &signal = pos->second;
+ should_suppress = signal.m_suppress;
+ should_stop = signal.m_stop;
+ should_notify = signal.m_notify;
+ return signal.m_name.AsCString("");
+ }
+}
+
+bool
+UnixSignals::GetShouldSuppress (int signo) const
+{
+ collection::const_iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ return pos->second.m_suppress;
+ return false;
+}
+
+bool
+UnixSignals::SetShouldSuppress (int signo, bool value)
+{
+ collection::iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ {
+ pos->second.m_suppress = value;
+ return true;
+ }
+ return false;
+}
+
+bool
+UnixSignals::SetShouldSuppress (const char *signal_name, bool value)
+{
+ const int32_t signo = GetSignalNumberFromName (signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldSuppress (signo, value);
+ return false;
+}
+
+bool
+UnixSignals::GetShouldStop (int signo) const
+{
+ collection::const_iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ return pos->second.m_stop;
+ return false;
+}
+
+bool
+UnixSignals::SetShouldStop (int signo, bool value)
+{
+ collection::iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ {
+ pos->second.m_stop = value;
+ return true;
+ }
+ return false;
+}
+
+bool
+UnixSignals::SetShouldStop (const char *signal_name, bool value)
+{
+ const int32_t signo = GetSignalNumberFromName (signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldStop (signo, value);
+ return false;
+}
+
+bool
+UnixSignals::GetShouldNotify (int signo) const
+{
+ collection::const_iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ return pos->second.m_notify;
+ return false;
+}
+
+bool
+UnixSignals::SetShouldNotify (int signo, bool value)
+{
+ collection::iterator pos = m_signals.find (signo);
+ if (pos != m_signals.end())
+ {
+ pos->second.m_notify = value;
+ return true;
+ }
+ return false;
+}
+
+bool
+UnixSignals::SetShouldNotify (const char *signal_name, bool value)
+{
+ const int32_t signo = GetSignalNumberFromName (signal_name);
+ if (signo != LLDB_INVALID_SIGNAL_NUMBER)
+ return SetShouldNotify (signo, value);
+ return false;
+}
diff --git a/source/Target/UnwindAssembly.cpp b/source/Target/UnwindAssembly.cpp
new file mode 100644
index 000000000000..72137501f38b
--- /dev/null
+++ b/source/Target/UnwindAssembly.cpp
@@ -0,0 +1,41 @@
+//===-- UnwindAssembly.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/lldb-private.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/PluginInterface.h"
+#include "lldb/Target/UnwindAssembly.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+UnwindAssembly*
+UnwindAssembly::FindPlugin (const ArchSpec &arch)
+{
+ UnwindAssemblyCreateInstance create_callback;
+
+ for (uint32_t idx = 0;
+ (create_callback = PluginManager::GetUnwindAssemblyCreateCallbackAtIndex(idx)) != NULL;
+ ++idx)
+ {
+ std::unique_ptr<UnwindAssembly> assembly_profiler_ap (create_callback (arch));
+ if (assembly_profiler_ap.get ())
+ return assembly_profiler_ap.release ();
+ }
+ return NULL;
+}
+
+UnwindAssembly::UnwindAssembly (const ArchSpec &arch) :
+ m_arch (arch)
+{
+}
+
+UnwindAssembly::~UnwindAssembly ()
+{
+}