Moved the code in ClangUserExpression that set up & ran the thread plan with timeouts, and restarting with all threads into a utility function in Process. This required a bunch of renaming.
Added a ThreadPlanCallUserExpression that differs from ThreadPlanCallFunction in that it holds onto a shared pointer to its ClangUserExpression so that can't go away before the thread plan is done using it.
Fixed the stop message when you hit a breakpoint while running a user expression so it is more obvious what has happened.
git-svn-id: https://llvm.org/svn/llvm-project/llvdb/trunk@120386 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/source/Expression/ClangFunction.cpp b/source/Expression/ClangFunction.cpp
index 1eeeb00..3dc31ec 100644
--- a/source/Expression/ClangFunction.cpp
+++ b/source/Expression/ClangFunction.cpp
@@ -275,7 +275,7 @@
Error error;
using namespace clang;
- ExecutionResults return_value = eExecutionSetupError;
+ Process::ExecutionResults return_value = Process::eExecutionSetupError;
Process *process = exe_ctx.process;
@@ -439,13 +439,13 @@
exe_ctx.process->DeallocateMemory(args_addr);
}
-ClangFunction::ExecutionResults
+Process::ExecutionResults
ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, Value &results)
{
return ExecuteFunction (exe_ctx, errors, 1000, true, results);
}
-ClangFunction::ExecutionResults
+Process::ExecutionResults
ClangFunction::ExecuteFunction(ExecutionContext &exe_ctx, Stream &errors, bool stop_others, Value &results)
{
const bool try_all_threads = false;
@@ -453,7 +453,7 @@
return ExecuteFunction (exe_ctx, NULL, errors, stop_others, NULL, try_all_threads, discard_on_error, results);
}
-ClangFunction::ExecutionResults
+Process::ExecutionResults
ClangFunction::ExecuteFunction(
ExecutionContext &exe_ctx,
Stream &errors,
@@ -468,7 +468,7 @@
}
// This is the static function
-ClangFunction::ExecutionResults
+Process::ExecutionResults
ClangFunction::ExecuteFunction (
ExecutionContext &exe_ctx,
lldb::addr_t function_address,
@@ -480,259 +480,19 @@
Stream &errors,
lldb::addr_t *this_arg)
{
- // Save this value for restoration of the execution context after we run
- uint32_t tid = exe_ctx.thread->GetIndexID();
-
- // 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 = exe_ctx.process->GetThreadList().GetSelectedThread();
- lldb::StackFrameSP selected_frame_sp;
-
- uint32_t selected_tid;
- if (selected_thread_sp != NULL)
- {
- selected_tid = selected_thread_sp->GetIndexID();
- selected_frame_sp = selected_thread_sp->GetSelectedFrame();
- }
- else
- {
- selected_tid = LLDB_INVALID_THREAD_ID;
- }
-
- ClangFunction::ExecutionResults return_value = eExecutionSetupError;
lldb::ThreadPlanSP call_plan_sp(ClangFunction::GetThreadPlanToCallFunction(exe_ctx, function_address, void_arg,
errors, stop_others, discard_on_error,
this_arg));
-
- ThreadPlanCallFunction *call_plan_ptr = static_cast<ThreadPlanCallFunction *> (call_plan_sp.get());
-
if (call_plan_sp == NULL)
- return eExecutionSetupError;
+ return Process::eExecutionSetupError;
call_plan_sp->SetPrivate(true);
- exe_ctx.thread->QueueThreadPlan(call_plan_sp, true);
- Listener listener("ClangFunction temporary listener");
- exe_ctx.process->HijackProcessEvents(&listener);
-
- Error resume_error = exe_ctx.process->Resume ();
- if (!resume_error.Success())
- {
- errors.Printf("Error resuming inferior: \"%s\".\n", resume_error.AsCString());
- exe_ctx.process->RestoreProcessEvents();
- return eExecutionSetupError;
- }
-
- // 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.
-
- TimeValue* timeout_ptr = NULL;
- TimeValue real_timeout;
-
- if (single_thread_timeout_usec != 0)
- {
- real_timeout = TimeValue::Now();
- real_timeout.OffsetWithMicroSeconds(single_thread_timeout_usec);
- timeout_ptr = &real_timeout;
- }
-
- lldb::LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
- while (1)
- {
- lldb::EventSP event_sp;
- lldb::StateType stop_state = lldb::eStateInvalid;
- // Now wait for the process to stop again:
- bool got_event = listener.WaitForEvent (timeout_ptr, event_sp);
-
- if (!got_event)
- {
- // Right now this is the only way to tell we've timed out...
- // We should interrupt the process here...
- // Not really sure what to do if Halt fails here...
- if (log)
- if (try_all_threads)
- log->Printf ("Running function with timeout: %d timed out, trying with all threads enabled.",
- single_thread_timeout_usec);
- else
- log->Printf ("Running function with timeout: %d timed out, abandoning execution.",
- single_thread_timeout_usec);
-
- if (exe_ctx.process->Halt().Success())
- {
- timeout_ptr = NULL;
- if (log)
- log->Printf ("Halt succeeded.");
-
- // Between the time that we got the timeout and the time we halted, but target
- // might have actually completed the plan. If so, we're done. Note, I call WFE here with a short
- // timeout to
- got_event = listener.WaitForEvent(NULL, event_sp);
-
- if (got_event)
- {
- stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
- if (log)
- {
- log->Printf ("Stopped with event: %s", StateAsCString(stop_state));
- if (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent(event_sp.get()))
- log->Printf (" Event was the Halt interruption event.");
- }
-
- if (exe_ctx.thread->IsThreadPlanDone (call_plan_sp.get()))
- {
- if (log)
- log->Printf ("Even though we timed out, the call plan was done. Exiting wait loop.");
- return_value = eExecutionCompleted;
- break;
- }
-
- if (try_all_threads
- && (stop_state == lldb::eStateStopped && Process::ProcessEventData::GetInterruptedFromEvent (event_sp.get())))
- {
-
- call_plan_ptr->SetStopOthers (false);
- if (log)
- log->Printf ("About to resume.");
-
- exe_ctx.process->Resume();
- continue;
- }
- else
- {
- exe_ctx.process->RestoreProcessEvents ();
- return eExecutionInterrupted;
- }
- }
- }
- }
-
- stop_state = Process::ProcessEventData::GetStateFromEvent(event_sp.get());
- if (log)
- log->Printf("Got event: %s.", StateAsCString(stop_state));
-
- if (stop_state == lldb::eStateRunning || stop_state == lldb::eStateStepping)
- continue;
-
- if (exe_ctx.thread->IsThreadPlanDone (call_plan_sp.get()))
- {
- return_value = eExecutionCompleted;
- break;
- }
- else if (exe_ctx.thread->WasThreadPlanDiscarded (call_plan_sp.get()))
- {
- return_value = eExecutionDiscarded;
- break;
- }
- else
- {
- if (log)
- {
- StreamString s;
- event_sp->Dump (&s);
- StreamString ts;
-
- const char *event_explanation;
-
- do
- {
- 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("<");
- RegisterContext *register_context = thread->GetRegisterContext();
-
- if (register_context)
- ts.Printf("[ip 0x%llx] ", 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 (log)
- log->Printf("Execution interrupted: %s %s", s.GetData(), event_explanation);
- }
-
- if (discard_on_error && call_plan_sp)
- {
- exe_ctx.thread->DiscardThreadPlansUpToPlan (call_plan_sp);
- }
- return_value = eExecutionInterrupted;
- break;
- }
- }
-
- if (exe_ctx.process)
- exe_ctx.process->RestoreProcessEvents ();
-
- // Thread we ran the function in may have gone away because we ran the target
- // Check that it's still there.
- exe_ctx.thread = exe_ctx.process->GetThreadList().FindThreadByIndexID(tid, true).get();
- if (exe_ctx.thread)
- exe_ctx.frame = exe_ctx.thread->GetStackFrameAtIndex(0).get();
-
- // 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 (exe_ctx.process->GetThreadList().SetSelectedThreadByIndexID (selected_tid))
- {
- // We were able to restore the selected thread, now restore the frame:
- exe_ctx.process->GetThreadList().GetSelectedThread()->SetSelectedFrame(selected_frame_sp.get());
- }
- }
-
- return return_value;
+ return exe_ctx.process->RunThreadPlan (exe_ctx, call_plan_sp, stop_others, try_all_threads, discard_on_error,
+ single_thread_timeout_usec, errors);
}
-ClangFunction::ExecutionResults
+Process::ExecutionResults
ClangFunction::ExecuteFunction(
ExecutionContext &exe_ctx,
lldb::addr_t *args_addr_ptr,
@@ -744,7 +504,7 @@
Value &results)
{
using namespace clang;
- ExecutionResults return_value = eExecutionSetupError;
+ Process::ExecutionResults return_value = Process::eExecutionSetupError;
lldb::addr_t args_addr;
@@ -754,12 +514,12 @@
args_addr = LLDB_INVALID_ADDRESS;
if (CompileFunction(errors) != 0)
- return eExecutionSetupError;
+ return Process::eExecutionSetupError;
if (args_addr == LLDB_INVALID_ADDRESS)
{
if (!InsertFunction(exe_ctx, args_addr, errors))
- return eExecutionSetupError;
+ return Process::eExecutionSetupError;
}
return_value = ClangFunction::ExecuteFunction(exe_ctx, m_wrapper_function_addr, args_addr, stop_others,
@@ -768,7 +528,7 @@
if (args_addr_ptr != NULL)
*args_addr_ptr = args_addr;
- if (return_value != eExecutionCompleted)
+ if (return_value != Process::eExecutionCompleted)
return return_value;
FetchFunctionResults(exe_ctx, args_addr, results);
@@ -776,7 +536,7 @@
if (args_addr_ptr == NULL)
DeallocateFunctionResults(exe_ctx, args_addr);
- return eExecutionCompleted;
+ return Process::eExecutionCompleted;
}
clang::ASTConsumer *
diff --git a/source/Expression/ClangUserExpression.cpp b/source/Expression/ClangUserExpression.cpp
index 3e4827b..993b780 100644
--- a/source/Expression/ClangUserExpression.cpp
+++ b/source/Expression/ClangUserExpression.cpp
@@ -33,6 +33,8 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallUserExpression.h"
using namespace lldb_private;
@@ -318,6 +320,9 @@
PrepareToExecuteJITExpression (error_stream, exe_ctx, struct_address, object_ptr);
+ // FIXME: This should really return a ThreadPlanCallUserExpression, in order to make sure that we don't release the
+ // ClangUserExpression resources before the thread plan finishes execution in the target. But because we are
+ // forcing unwind_on_error to be true here, in practical terms that can't happen.
return ClangFunction::GetThreadPlanToCallFunction (exe_ctx,
m_jit_addr,
struct_address,
@@ -342,10 +347,11 @@
return true;
}
-bool
+Process::ExecutionResults
ClangUserExpression::Execute (Stream &error_stream,
ExecutionContext &exe_ctx,
bool discard_on_error,
+ ClangUserExpression::ClangUserExpressionSP &shared_ptr_to_me,
ClangExpressionVariable *&result)
{
if (m_dwarf_opcodes.get())
@@ -354,7 +360,7 @@
error_stream.Printf("We don't currently support executing DWARF expressions");
- return false;
+ return Process::eExecutionSetupError;
}
else if (m_jit_addr != LLDB_INVALID_ADDRESS)
{
@@ -366,50 +372,46 @@
const bool stop_others = true;
const bool try_all_threads = true;
- ClangFunction::ExecutionResults execution_result =
- ClangFunction::ExecuteFunction (exe_ctx,
- m_jit_addr,
- struct_address,
- stop_others,
- try_all_threads,
- discard_on_error,
- 10000000,
- error_stream,
- (m_needs_object_ptr ? &object_ptr : NULL));
- if (execution_result != ClangFunction::eExecutionCompleted)
+ Address wrapper_address (NULL, m_jit_addr);
+ lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression (*(exe_ctx.thread), wrapper_address, struct_address,
+ stop_others, discard_on_error,
+ (m_needs_object_ptr ? &object_ptr : NULL),
+ shared_ptr_to_me));
+ if (call_plan_sp == NULL || !call_plan_sp->ValidatePlan (NULL))
+ return Process::eExecutionSetupError;
+
+ call_plan_sp->SetPrivate(true);
+
+ uint32_t single_thread_timeout_usec = 10000000;
+ Process::ExecutionResults execution_result =
+ exe_ctx.process->RunThreadPlan (exe_ctx, call_plan_sp, stop_others, try_all_threads, discard_on_error,
+ single_thread_timeout_usec, error_stream);
+
+ if (execution_result == Process::eExecutionInterrupted)
{
- const char *result_name;
-
- switch (execution_result)
- {
- case ClangFunction::eExecutionCompleted:
- result_name = "eExecutionCompleted";
- break;
- case ClangFunction::eExecutionDiscarded:
- result_name = "eExecutionDiscarded";
- break;
- case ClangFunction::eExecutionInterrupted:
- result_name = "eExecutionInterrupted";
- break;
- case ClangFunction::eExecutionSetupError:
- result_name = "eExecutionSetupError";
- break;
- case ClangFunction::eExecutionTimedOut:
- result_name = "eExecutionTimedOut";
- break;
- }
-
- error_stream.Printf ("Couldn't execute function; result was %s\n", result_name);
- return false;
+ if (discard_on_error)
+ error_stream.Printf ("Expression execution was interrupted. The process has been returned to the state before execution.");
+ else
+ error_stream.Printf ("Expression execution was interrupted. The process has been left at the point where it was interrupted.");
+
+ return execution_result;
+ }
+ else if (execution_result != Process::eExecutionCompleted)
+ {
+ error_stream.Printf ("Couldn't execute function; result was %s\n", Process::ExecutionResultAsCString (execution_result));
+ return execution_result;
}
- return FinalizeJITExecution (error_stream, exe_ctx, result);
+ if (FinalizeJITExecution (error_stream, exe_ctx, result))
+ return Process::eExecutionCompleted;
+ else
+ return Process::eExecutionSetupError;
}
else
{
error_stream.Printf("Expression can't be run; neither DWARF nor a JIT compiled function is present");
- return false;
+ return Process::eExecutionSetupError;
}
}
@@ -422,18 +424,24 @@
return *m_dwarf_opcodes.get();
}
-lldb::ValueObjectSP
+Process::ExecutionResults
ClangUserExpression::Evaluate (ExecutionContext &exe_ctx,
bool discard_on_error,
const char *expr_cstr,
- const char *expr_prefix)
+ const char *expr_prefix,
+ lldb::ValueObjectSP &result_valobj_sp)
{
Error error;
- lldb::ValueObjectSP result_valobj_sp;
+ Process::ExecutionResults execution_results = Process::eExecutionSetupError;
if (exe_ctx.process == NULL)
- return result_valobj_sp;
-
+ {
+ error.SetErrorString ("Must have a process to evaluate expressions.");
+
+ result_valobj_sp.reset (new ValueObjectConstResult (error));
+ return Process::eExecutionSetupError;
+ }
+
if (!exe_ctx.process->GetDynamicCheckers())
{
DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions();
@@ -448,17 +456,17 @@
error.SetErrorString (install_errors.GetString().c_str());
result_valobj_sp.reset (new ValueObjectConstResult (error));
- return result_valobj_sp;
+ return Process::eExecutionSetupError;
}
exe_ctx.process->SetDynamicCheckers(dynamic_checkers);
}
- ClangUserExpression user_expression (expr_cstr, expr_prefix);
-
+ ClangUserExpressionSP user_expression_sp (new ClangUserExpression (expr_cstr, expr_prefix));
+
StreamString error_stream;
- if (!user_expression.Parse (error_stream, exe_ctx, TypeFromUser(NULL, NULL)))
+ if (!user_expression_sp->Parse (error_stream, exe_ctx, TypeFromUser(NULL, NULL)))
{
if (error_stream.GetString().empty())
error.SetErrorString ("expression failed to parse, unknown error");
@@ -471,7 +479,12 @@
error_stream.GetString().clear();
- if (!user_expression.Execute (error_stream, exe_ctx, discard_on_error, expr_result))
+ execution_results = user_expression_sp->Execute (error_stream,
+ exe_ctx,
+ discard_on_error,
+ user_expression_sp,
+ expr_result);
+ if (execution_results != Process::eExecutionCompleted)
{
if (error_stream.GetString().empty())
error.SetErrorString ("expression failed to execute, unknown error");
@@ -499,5 +512,5 @@
if (result_valobj_sp.get() == NULL)
result_valobj_sp.reset (new ValueObjectConstResult (error));
- return result_valobj_sp;
+ return execution_results;
}