xref: /llvm-project/lldb/test/API/api/multithreaded/test_stop-hook.cpp.template (revision fa5a13276764a2657b3571fa3c57b07ee5d2d661)
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