aboutsummaryrefslogtreecommitdiffstats
path: root/source/Target
diff options
context:
space:
mode:
authorEd Maste <emaste@FreeBSD.org>2013-08-23 17:46:38 +0000
committerEd Maste <emaste@FreeBSD.org>2013-08-23 17:46:38 +0000
commitf034231a6a1fd5d6395206c1651de8cd9402cca3 (patch)
treef561dabc721ad515599172c16da3a4400b7f4aec /source/Target
downloadsrc-f034231a6a1fd5d6395206c1651de8cd9402cca3.tar.gz
src-f034231a6a1fd5d6395206c1651de8cd9402cca3.zip
Import lldb as of SVN r188801
(A number of files not required for the FreeBSD build have been removed.) Sponsored by: DARPA, AFRL
Notes
Notes: svn path=/vendor/lldb/dist/; revision=254721
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_mu