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