| // 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" | |
| #if !defined(PATH_MAX) | |
| #define PATH_MAX 4096 | |
| #endif | |
| 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."); | |
| } |