1*a37672e2SJim Ingham// LLDB C++ API Test: Verify that when the Debugger stdin 2*a37672e2SJim Ingham// is set to a FILE *, lldb can still successfully run a 3*a37672e2SJim Ingham// python command in a stop hook. 4*a37672e2SJim Ingham 5*a37672e2SJim Ingham#include <errno.h> 6*a37672e2SJim Ingham#include <mutex> 7*a37672e2SJim Ingham#include <stdio.h> 8*a37672e2SJim Ingham#include <string> 9*a37672e2SJim Ingham#include <vector> 10*a37672e2SJim Ingham 11*a37672e2SJim Ingham%include_SB_APIs% 12*a37672e2SJim Ingham 13*a37672e2SJim Ingham#include "common.h" 14*a37672e2SJim Ingham 15*a37672e2SJim Inghamusing namespace lldb; 16*a37672e2SJim Ingham 17*a37672e2SJim Inghamvoid test(SBDebugger &dbg, std::vector<std::string> args) { 18*a37672e2SJim Ingham // The problem we had was that when the thread that was 19*a37672e2SJim Ingham // waiting on input went into the call to 'read' it had 20*a37672e2SJim Ingham // the file handle lock. Then when the python interpreter 21*a37672e2SJim Ingham // Initialized itself to run the python command, it tried 22*a37672e2SJim Ingham // to flush the file channel, and that deadlocked. 23*a37672e2SJim Ingham // This only happens when Async is true, since otherwise 24*a37672e2SJim Ingham // the process event is handled on the I/O read thread, 25*a37672e2SJim Ingham // which sidestepped the problem. 26*a37672e2SJim Ingham dbg.SetAsync(true); 27*a37672e2SJim Ingham 28*a37672e2SJim Ingham SBTarget target = dbg.CreateTarget(args.at(0).c_str()); 29*a37672e2SJim Ingham if (!target.IsValid()) 30*a37672e2SJim Ingham throw Exception("invalid target"); 31*a37672e2SJim Ingham 32*a37672e2SJim Ingham SBBreakpoint breakpoint = target.BreakpointCreateByName("next"); 33*a37672e2SJim Ingham if (!breakpoint.IsValid()) 34*a37672e2SJim Ingham throw Exception("invalid breakpoint"); 35*a37672e2SJim Ingham 36*a37672e2SJim Ingham SBCommandInterpreter interp = dbg.GetCommandInterpreter(); 37*a37672e2SJim Ingham SBCommandReturnObject result; 38*a37672e2SJim Ingham 39*a37672e2SJim Ingham // Bring in the python command. We actually add two commands, 40*a37672e2SJim Ingham // one that runs in the stop hook and sets a variable when it 41*a37672e2SJim Ingham // runs, and one that reports out the variable so we can ensure 42*a37672e2SJim Ingham // that we did indeed run the stop hook. 43*a37672e2SJim Ingham const char *source_dir = "%SOURCE_DIR%"; 44*a37672e2SJim Ingham SBFileSpec script_spec(source_dir); 45*a37672e2SJim Ingham script_spec.AppendPathComponent("some_cmd.py"); 46*a37672e2SJim Ingham char path[PATH_MAX]; 47*a37672e2SJim Ingham script_spec.GetPath(path, PATH_MAX); 48*a37672e2SJim Ingham 49*a37672e2SJim Ingham std::string import_command("command script import "); 50*a37672e2SJim Ingham import_command.append(path); 51*a37672e2SJim Ingham interp.HandleCommand(import_command.c_str(), result); 52*a37672e2SJim Ingham if (!result.Succeeded()) 53*a37672e2SJim Ingham throw Exception("Couldn't import %SOURCE_DIR%/some_cmd.py"); 54*a37672e2SJim Ingham 55*a37672e2SJim Ingham SBProcess process = target.LaunchSimple(nullptr, nullptr, nullptr); 56*a37672e2SJim Ingham if (!process.IsValid()) 57*a37672e2SJim Ingham throw Exception("Couldn't launch process."); 58*a37672e2SJim Ingham if (process.GetState() != lldb::eStateStopped) 59*a37672e2SJim Ingham throw Exception("Process was not stopped"); 60*a37672e2SJim Ingham 61*a37672e2SJim Ingham process.SetSelectedThreadByIndexID(0); 62*a37672e2SJim Ingham 63*a37672e2SJim Ingham // Now add the stop hook: 64*a37672e2SJim Ingham interp.HandleCommand("target stop-hook add -o some-cmd", result); 65*a37672e2SJim Ingham if (!result.Succeeded()) 66*a37672e2SJim Ingham throw Exception("Couldn't add a stop hook."); 67*a37672e2SJim Ingham 68*a37672e2SJim Ingham // Now switch the I/O over to a pipe, which will be handled by the 69*a37672e2SJim Ingham // NativeFile class: 70*a37672e2SJim Ingham int to_lldb_des[2]; 71*a37672e2SJim Ingham int pipe_result = pipe(to_lldb_des); 72*a37672e2SJim Ingham FILE *fh_lldb_in = fdopen(to_lldb_des[0], "r"); 73*a37672e2SJim Ingham FILE *fh_to_lldb = fdopen(to_lldb_des[1], "w"); 74*a37672e2SJim Ingham 75*a37672e2SJim Ingham // We need to reset the handle before destroying the debugger 76*a37672e2SJim Ingham // or the same deadlock will stall exiting: 77*a37672e2SJim Ingham class Cleanup { 78*a37672e2SJim Ingham public: 79*a37672e2SJim Ingham Cleanup(SBDebugger dbg, int filedes[2]) : m_dbg(dbg) { 80*a37672e2SJim Ingham m_file = m_dbg.GetInputFileHandle(); 81*a37672e2SJim Ingham m_filedes[0] = filedes[0]; 82*a37672e2SJim Ingham m_filedes[1] = filedes[1]; 83*a37672e2SJim Ingham } 84*a37672e2SJim Ingham ~Cleanup() { 85*a37672e2SJim Ingham m_dbg.SetInputFileHandle(m_file, false); 86*a37672e2SJim Ingham close(m_filedes[0]); 87*a37672e2SJim Ingham close(m_filedes[1]); 88*a37672e2SJim Ingham } 89*a37672e2SJim Ingham 90*a37672e2SJim Ingham private: 91*a37672e2SJim Ingham FILE *m_file; 92*a37672e2SJim Ingham SBDebugger m_dbg; 93*a37672e2SJim Ingham int m_filedes[2]; 94*a37672e2SJim Ingham }; 95*a37672e2SJim Ingham Cleanup cleanup(dbg, to_lldb_des); 96*a37672e2SJim Ingham 97*a37672e2SJim Ingham dbg.SetInputFileHandle(fh_lldb_in, false); 98*a37672e2SJim Ingham 99*a37672e2SJim Ingham // Now run the command interpreter. You have to pass true to 100*a37672e2SJim Ingham // start thread so we will run the I/O in a separate thread. 101*a37672e2SJim Ingham dbg.RunCommandInterpreter(false, true); 102*a37672e2SJim Ingham 103*a37672e2SJim Ingham // Now issue a stepi, and fetch the running and stopped events: 104*a37672e2SJim Ingham fprintf(fh_to_lldb, "thread step-inst\n"); 105*a37672e2SJim Ingham 106*a37672e2SJim Ingham SBEvent proc_event; 107*a37672e2SJim Ingham StateType state; 108*a37672e2SJim Ingham bool got_event; 109*a37672e2SJim Ingham 110*a37672e2SJim Ingham got_event = dbg.GetListener().WaitForEventForBroadcaster( 111*a37672e2SJim Ingham 100, process.GetBroadcaster(), proc_event); 112*a37672e2SJim Ingham if (!got_event) 113*a37672e2SJim Ingham throw Exception("Didn't get running event"); 114*a37672e2SJim Ingham state = SBProcess::GetStateFromEvent(proc_event); 115*a37672e2SJim Ingham if (state != eStateRunning) 116*a37672e2SJim Ingham throw Exception("Event wasn't a running event."); 117*a37672e2SJim Ingham 118*a37672e2SJim Ingham got_event = dbg.GetListener().WaitForEventForBroadcaster( 119*a37672e2SJim Ingham 100, process.GetBroadcaster(), proc_event); 120*a37672e2SJim Ingham if (!got_event) 121*a37672e2SJim Ingham throw Exception("Didn't get a stopped event"); 122*a37672e2SJim Ingham state = SBProcess::GetStateFromEvent(proc_event); 123*a37672e2SJim Ingham if (state != eStateStopped) 124*a37672e2SJim Ingham throw Exception("Event wasn't a stop event."); 125*a37672e2SJim Ingham 126*a37672e2SJim Ingham // At this point the stop hook should have run. Check that: 127*a37672e2SJim Ingham interp.HandleCommand("report-cmd", result); 128*a37672e2SJim Ingham if (!result.Succeeded()) 129*a37672e2SJim Ingham throw Exception("Didn't actually call stop hook."); 130*a37672e2SJim Ingham} 131