xref: /openbsd-src/gnu/llvm/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h (revision f6aab3d83b51b91c24247ad2c2573574de475a82)
1061da546Spatrick //===-- GDBRemoteClientBase.h -----------------------------------*- C++ -*-===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick 
9dda28197Spatrick #ifndef LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
10dda28197Spatrick #define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
11061da546Spatrick 
12061da546Spatrick #include "GDBRemoteCommunication.h"
13061da546Spatrick 
14061da546Spatrick #include <condition_variable>
15061da546Spatrick 
16061da546Spatrick namespace lldb_private {
17061da546Spatrick namespace process_gdb_remote {
18061da546Spatrick 
19*f6aab3d8Srobert class GDBRemoteClientBase : public GDBRemoteCommunication, public Broadcaster {
20061da546Spatrick public:
21*f6aab3d8Srobert   enum {
22*f6aab3d8Srobert     eBroadcastBitRunPacketSent = (1u << 0),
23*f6aab3d8Srobert   };
24*f6aab3d8Srobert 
25061da546Spatrick   struct ContinueDelegate {
26061da546Spatrick     virtual ~ContinueDelegate();
27061da546Spatrick     virtual void HandleAsyncStdout(llvm::StringRef out) = 0;
28061da546Spatrick     virtual void HandleAsyncMisc(llvm::StringRef data) = 0;
29061da546Spatrick     virtual void HandleStopReply() = 0;
30061da546Spatrick 
31061da546Spatrick     /// Process asynchronously-received structured data.
32061da546Spatrick     ///
33061da546Spatrick     /// \param[in] data
34061da546Spatrick     ///   The complete data packet, expected to start with JSON-async.
35061da546Spatrick     virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 0;
36061da546Spatrick   };
37061da546Spatrick 
38*f6aab3d8Srobert   GDBRemoteClientBase(const char *comm_name);
39061da546Spatrick 
40be691f3bSpatrick   bool SendAsyncSignal(int signo, std::chrono::seconds interrupt_timeout);
41061da546Spatrick 
42be691f3bSpatrick   bool Interrupt(std::chrono::seconds interrupt_timeout);
43061da546Spatrick 
44061da546Spatrick   lldb::StateType SendContinuePacketAndWaitForResponse(
45061da546Spatrick       ContinueDelegate &delegate, const UnixSignals &signals,
46be691f3bSpatrick       llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
47be691f3bSpatrick       StringExtractorGDBRemote &response);
48061da546Spatrick 
49be691f3bSpatrick   // If interrupt_timeout == 0 seconds, don't interrupt the target.
50be691f3bSpatrick   // Only send the packet if the target is stopped.
51be691f3bSpatrick   // If you want to use this mode, use the fact that the timeout is defaulted
52be691f3bSpatrick   // so it's clear from the call-site that you are using no-interrupt.
53be691f3bSpatrick   // If it is non-zero, interrupt the target if it is running, and
54be691f3bSpatrick   // send the packet.
55be691f3bSpatrick   // It the target doesn't respond within the given timeout, it returns
56be691f3bSpatrick   // ErrorReplyTimeout.
57be691f3bSpatrick   PacketResult SendPacketAndWaitForResponse(
58be691f3bSpatrick       llvm::StringRef payload, StringExtractorGDBRemote &response,
59be691f3bSpatrick       std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
60061da546Spatrick 
61*f6aab3d8Srobert   PacketResult ReadPacketWithOutputSupport(
62*f6aab3d8Srobert       StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
63*f6aab3d8Srobert       bool sync_on_timeout,
64*f6aab3d8Srobert       llvm::function_ref<void(llvm::StringRef)> output_callback);
65*f6aab3d8Srobert 
66061da546Spatrick   PacketResult SendPacketAndReceiveResponseWithOutputSupport(
67061da546Spatrick       llvm::StringRef payload, StringExtractorGDBRemote &response,
68be691f3bSpatrick       std::chrono::seconds interrupt_timeout,
69061da546Spatrick       llvm::function_ref<void(llvm::StringRef)> output_callback);
70061da546Spatrick 
71061da546Spatrick   class Lock {
72061da546Spatrick   public:
73be691f3bSpatrick     // If interrupt_timeout == 0 seconds, only take the lock if the target is
74be691f3bSpatrick     // not running. If using this option, use the fact that the
75be691f3bSpatrick     // interrupt_timeout is defaulted so it will be obvious at the call site
76be691f3bSpatrick     // that you are choosing this mode. If it is non-zero, interrupt the target
77be691f3bSpatrick     // if it is running, waiting for the given timeout for the interrupt to
78be691f3bSpatrick     // succeed.
79be691f3bSpatrick     Lock(GDBRemoteClientBase &comm,
80be691f3bSpatrick          std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
81061da546Spatrick     ~Lock();
82061da546Spatrick 
83061da546Spatrick     explicit operator bool() { return m_acquired; }
84061da546Spatrick 
85061da546Spatrick     // Whether we had to interrupt the continue thread to acquire the
86061da546Spatrick     // connection.
DidInterrupt()87061da546Spatrick     bool DidInterrupt() const { return m_did_interrupt; }
88061da546Spatrick 
89061da546Spatrick   private:
90061da546Spatrick     std::unique_lock<std::recursive_mutex> m_async_lock;
91061da546Spatrick     GDBRemoteClientBase &m_comm;
92be691f3bSpatrick     std::chrono::seconds m_interrupt_timeout;
93061da546Spatrick     bool m_acquired;
94061da546Spatrick     bool m_did_interrupt;
95061da546Spatrick 
96be691f3bSpatrick     void SyncWithContinueThread();
97061da546Spatrick   };
98061da546Spatrick 
99061da546Spatrick protected:
100061da546Spatrick   PacketResult
101061da546Spatrick   SendPacketAndWaitForResponseNoLock(llvm::StringRef payload,
102061da546Spatrick                                      StringExtractorGDBRemote &response);
103061da546Spatrick 
104061da546Spatrick   virtual void OnRunPacketSent(bool first);
105061da546Spatrick 
106061da546Spatrick private:
107061da546Spatrick   /// Variables handling synchronization between the Continue thread and any
108061da546Spatrick   /// other threads wishing to send packets over the connection. Either the
109061da546Spatrick   /// continue thread has control over the connection (m_is_running == true) or
110061da546Spatrick   /// the connection is free for an arbitrary number of other senders to take
111061da546Spatrick   /// which indicate their interest by incrementing m_async_count.
112061da546Spatrick   ///
113061da546Spatrick   /// Semantics of individual states:
114061da546Spatrick   ///
115061da546Spatrick   /// - m_continue_packet == false, m_async_count == 0:
116061da546Spatrick   ///   connection is free
117061da546Spatrick   /// - m_continue_packet == true, m_async_count == 0:
118061da546Spatrick   ///   only continue thread is present
119061da546Spatrick   /// - m_continue_packet == true, m_async_count > 0:
120061da546Spatrick   ///   continue thread has control, async threads should interrupt it and wait
121061da546Spatrick   ///   for it to set m_continue_packet to false
122061da546Spatrick   /// - m_continue_packet == false, m_async_count > 0:
123061da546Spatrick   ///   async threads have control, continue thread needs to wait for them to
124061da546Spatrick   ///   finish (m_async_count goes down to 0).
125061da546Spatrick   /// @{
126061da546Spatrick   std::mutex m_mutex;
127061da546Spatrick   std::condition_variable m_cv;
128061da546Spatrick 
129061da546Spatrick   /// Packet with which to resume after an async interrupt. Can be changed by
130061da546Spatrick   /// an async thread e.g. to inject a signal.
131061da546Spatrick   std::string m_continue_packet;
132061da546Spatrick 
133061da546Spatrick   /// When was the interrupt packet sent. Used to make sure we time out if the
134061da546Spatrick   /// stub does not respond to interrupt requests.
135be691f3bSpatrick   std::chrono::time_point<std::chrono::steady_clock> m_interrupt_endpoint;
136061da546Spatrick 
137061da546Spatrick   /// Number of threads interested in sending.
138061da546Spatrick   uint32_t m_async_count;
139061da546Spatrick 
140061da546Spatrick   /// Whether the continue thread has control.
141061da546Spatrick   bool m_is_running;
142061da546Spatrick 
143061da546Spatrick   /// Whether we should resume after a stop.
144061da546Spatrick   bool m_should_stop;
145061da546Spatrick   /// @}
146061da546Spatrick 
147061da546Spatrick   /// This handles the synchronization between individual async threads. For
148061da546Spatrick   /// now they just use a simple mutex.
149061da546Spatrick   std::recursive_mutex m_async_mutex;
150061da546Spatrick 
151061da546Spatrick   bool ShouldStop(const UnixSignals &signals,
152061da546Spatrick                   StringExtractorGDBRemote &response);
153061da546Spatrick 
154061da546Spatrick   class ContinueLock {
155061da546Spatrick   public:
156061da546Spatrick     enum class LockResult { Success, Cancelled, Failed };
157061da546Spatrick 
158061da546Spatrick     explicit ContinueLock(GDBRemoteClientBase &comm);
159061da546Spatrick     ~ContinueLock();
160061da546Spatrick     explicit operator bool() { return m_acquired; }
161061da546Spatrick 
162061da546Spatrick     LockResult lock();
163061da546Spatrick 
164061da546Spatrick     void unlock();
165061da546Spatrick 
166061da546Spatrick   private:
167061da546Spatrick     GDBRemoteClientBase &m_comm;
168061da546Spatrick     bool m_acquired;
169061da546Spatrick   };
170061da546Spatrick };
171061da546Spatrick 
172061da546Spatrick } // namespace process_gdb_remote
173061da546Spatrick } // namespace lldb_private
174061da546Spatrick 
175dda28197Spatrick #endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
176