--- /dev/null
+""" Test command for checking the Python commands can run in a stop-hook """
+import lldb
+
+did_run = False
+
+class SomeCommand:
+ def __init__(self, debugger, unused):
+ self.dbg = debugger
+ def __call__(self, debugger, command, exe_ctx, result):
+ global did_run
+ did_run = True
+ result.PutCString("some output\n")
+
+ def get_short_help(self):
+ return "Test command - sets a variable."
+
+class OtherCommand:
+ def __init__(self, debugger, unused):
+ self.dbg = debugger
+ def __call__(self, debugger, command, exe_ctx, result):
+ global did_run
+ if did_run:
+ result.SetStatus(lldb.eReturnStatusSuccessFinishNoResult)
+ else:
+ result.SetStatus(lldb.eReturnStatusFailed)
+
+ def get_short_help(self):
+ return "Test command - sets a variable."
+
+def __lldb_init_module(debugger, unused):
+ print("Adding command some-cmd and report-cmd")
+ debugger.HandleCommand("command script add -c some_cmd.SomeCommand some-cmd")
+ debugger.HandleCommand("command script add -c some_cmd.OtherCommand report-cmd")
--- /dev/null
+// LLDB C++ API Test: Verify that when the Debugger stdin
+// is set to a FILE *, lldb can still successfully run a
+// python command in a stop hook.
+
+#include <errno.h>
+#include <mutex>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+%include_SB_APIs%
+
+#include "common.h"
+
+using namespace lldb;
+
+void test(SBDebugger &dbg, std::vector<std::string> args) {
+ // The problem we had was that when the thread that was
+ // waiting on input went into the call to 'read' it had
+ // the file handle lock. Then when the python interpreter
+ // Initialized itself to run the python command, it tried
+ // to flush the file channel, and that deadlocked.
+ // This only happens when Async is true, since otherwise
+ // the process event is handled on the I/O read thread,
+ // which sidestepped the problem.
+ dbg.SetAsync(true);
+
+ SBTarget target = dbg.CreateTarget(args.at(0).c_str());
+ if (!target.IsValid())
+ throw Exception("invalid target");
+
+ SBBreakpoint breakpoint = target.BreakpointCreateByName("next");
+ if (!breakpoint.IsValid())
+ throw Exception("invalid breakpoint");
+
+ SBCommandInterpreter interp = dbg.GetCommandInterpreter();
+ SBCommandReturnObject result;
+
+ // Bring in the python command. We actually add two commands,
+ // one that runs in the stop hook and sets a variable when it
+ // runs, and one that reports out the variable so we can ensure
+ // that we did indeed run the stop hook.
+ const char *source_dir = "%SOURCE_DIR%";
+ SBFileSpec script_spec(source_dir);
+ script_spec.AppendPathComponent("some_cmd.py");
+ char path[PATH_MAX];
+ script_spec.GetPath(path, PATH_MAX);
+
+ std::string import_command("command script import ");
+ import_command.append(path);
+ interp.HandleCommand(import_command.c_str(), result);
+ if (!result.Succeeded())
+ throw Exception("Couldn't import %SOURCE_DIR%/some_cmd.py");
+
+ SBProcess process = target.LaunchSimple(nullptr, nullptr, nullptr);
+ if (!process.IsValid())
+ throw Exception("Couldn't launch process.");
+ if (process.GetState() != lldb::eStateStopped)
+ throw Exception("Process was not stopped");
+
+ process.SetSelectedThreadByIndexID(0);
+
+ // Now add the stop hook:
+ interp.HandleCommand("target stop-hook add -o some-cmd", result);
+ if (!result.Succeeded())
+ throw Exception("Couldn't add a stop hook.");
+
+ // Now switch the I/O over to a pipe, which will be handled by the
+ // NativeFile class:
+ int to_lldb_des[2];
+ int pipe_result = pipe(to_lldb_des);
+ FILE *fh_lldb_in = fdopen(to_lldb_des[0], "r");
+ FILE *fh_to_lldb = fdopen(to_lldb_des[1], "w");
+
+ // We need to reset the handle before destroying the debugger
+ // or the same deadlock will stall exiting:
+ class Cleanup {
+ public:
+ Cleanup(SBDebugger dbg, int filedes[2]) : m_dbg(dbg) {
+ m_file = m_dbg.GetInputFileHandle();
+ m_filedes[0] = filedes[0];
+ m_filedes[1] = filedes[1];
+ }
+ ~Cleanup() {
+ m_dbg.SetInputFileHandle(m_file, false);
+ close(m_filedes[0]);
+ close(m_filedes[1]);
+ }
+
+ private:
+ FILE *m_file;
+ SBDebugger m_dbg;
+ int m_filedes[2];
+ };
+ Cleanup cleanup(dbg, to_lldb_des);
+
+ dbg.SetInputFileHandle(fh_lldb_in, false);
+
+ // Now run the command interpreter. You have to pass true to
+ // start thread so we will run the I/O in a separate thread.
+ dbg.RunCommandInterpreter(false, true);
+
+ // Now issue a stepi, and fetch the running and stopped events:
+ fprintf(fh_to_lldb, "thread step-inst\n");
+
+ SBEvent proc_event;
+ StateType state;
+ bool got_event;
+
+ got_event = dbg.GetListener().WaitForEventForBroadcaster(
+ 100, process.GetBroadcaster(), proc_event);
+ if (!got_event)
+ throw Exception("Didn't get running event");
+ state = SBProcess::GetStateFromEvent(proc_event);
+ if (state != eStateRunning)
+ throw Exception("Event wasn't a running event.");
+
+ got_event = dbg.GetListener().WaitForEventForBroadcaster(
+ 100, process.GetBroadcaster(), proc_event);
+ if (!got_event)
+ throw Exception("Didn't get a stopped event");
+ state = SBProcess::GetStateFromEvent(proc_event);
+ if (state != eStateStopped)
+ throw Exception("Event wasn't a stop event.");
+
+ // At this point the stop hook should have run. Check that:
+ interp.HandleCommand("report-cmd", result);
+ if (!result.Succeeded())
+ throw Exception("Didn't actually call stop hook.");
+}