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