xref: /freebsd-src/contrib/llvm-project/lldb/source/Plugins/Process/gdb-remote/GDBRemoteClientBase.h (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
10b57cec5SDimitry Andric //===-- GDBRemoteClientBase.h -----------------------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
95ffd83dbSDimitry Andric #ifndef LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
105ffd83dbSDimitry Andric #define LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
110b57cec5SDimitry Andric 
120b57cec5SDimitry Andric #include "GDBRemoteCommunication.h"
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include <condition_variable>
150b57cec5SDimitry Andric 
160b57cec5SDimitry Andric namespace lldb_private {
170b57cec5SDimitry Andric namespace process_gdb_remote {
180b57cec5SDimitry Andric 
19*bdd1243dSDimitry Andric class GDBRemoteClientBase : public GDBRemoteCommunication, public Broadcaster {
200b57cec5SDimitry Andric public:
21*bdd1243dSDimitry Andric   enum {
22*bdd1243dSDimitry Andric     eBroadcastBitRunPacketSent = (1u << 0),
23*bdd1243dSDimitry Andric   };
24*bdd1243dSDimitry Andric 
250b57cec5SDimitry Andric   struct ContinueDelegate {
260b57cec5SDimitry Andric     virtual ~ContinueDelegate();
270b57cec5SDimitry Andric     virtual void HandleAsyncStdout(llvm::StringRef out) = 0;
280b57cec5SDimitry Andric     virtual void HandleAsyncMisc(llvm::StringRef data) = 0;
290b57cec5SDimitry Andric     virtual void HandleStopReply() = 0;
300b57cec5SDimitry Andric 
310b57cec5SDimitry Andric     /// Process asynchronously-received structured data.
320b57cec5SDimitry Andric     ///
330b57cec5SDimitry Andric     /// \param[in] data
340b57cec5SDimitry Andric     ///   The complete data packet, expected to start with JSON-async.
350b57cec5SDimitry Andric     virtual void HandleAsyncStructuredDataPacket(llvm::StringRef data) = 0;
360b57cec5SDimitry Andric   };
370b57cec5SDimitry Andric 
38*bdd1243dSDimitry Andric   GDBRemoteClientBase(const char *comm_name);
390b57cec5SDimitry Andric 
40fe6060f1SDimitry Andric   bool SendAsyncSignal(int signo, std::chrono::seconds interrupt_timeout);
410b57cec5SDimitry Andric 
42fe6060f1SDimitry Andric   bool Interrupt(std::chrono::seconds interrupt_timeout);
430b57cec5SDimitry Andric 
440b57cec5SDimitry Andric   lldb::StateType SendContinuePacketAndWaitForResponse(
450b57cec5SDimitry Andric       ContinueDelegate &delegate, const UnixSignals &signals,
46fe6060f1SDimitry Andric       llvm::StringRef payload, std::chrono::seconds interrupt_timeout,
47fe6060f1SDimitry Andric       StringExtractorGDBRemote &response);
480b57cec5SDimitry Andric 
49fe6060f1SDimitry Andric   // If interrupt_timeout == 0 seconds, don't interrupt the target.
50fe6060f1SDimitry Andric   // Only send the packet if the target is stopped.
51fe6060f1SDimitry Andric   // If you want to use this mode, use the fact that the timeout is defaulted
52fe6060f1SDimitry Andric   // so it's clear from the call-site that you are using no-interrupt.
53fe6060f1SDimitry Andric   // If it is non-zero, interrupt the target if it is running, and
54fe6060f1SDimitry Andric   // send the packet.
55fe6060f1SDimitry Andric   // It the target doesn't respond within the given timeout, it returns
56fe6060f1SDimitry Andric   // ErrorReplyTimeout.
57fe6060f1SDimitry Andric   PacketResult SendPacketAndWaitForResponse(
58fe6060f1SDimitry Andric       llvm::StringRef payload, StringExtractorGDBRemote &response,
59fe6060f1SDimitry Andric       std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
600b57cec5SDimitry Andric 
61*bdd1243dSDimitry Andric   PacketResult ReadPacketWithOutputSupport(
62*bdd1243dSDimitry Andric       StringExtractorGDBRemote &response, Timeout<std::micro> timeout,
63*bdd1243dSDimitry Andric       bool sync_on_timeout,
64*bdd1243dSDimitry Andric       llvm::function_ref<void(llvm::StringRef)> output_callback);
65*bdd1243dSDimitry Andric 
660b57cec5SDimitry Andric   PacketResult SendPacketAndReceiveResponseWithOutputSupport(
670b57cec5SDimitry Andric       llvm::StringRef payload, StringExtractorGDBRemote &response,
68fe6060f1SDimitry Andric       std::chrono::seconds interrupt_timeout,
690b57cec5SDimitry Andric       llvm::function_ref<void(llvm::StringRef)> output_callback);
700b57cec5SDimitry Andric 
710b57cec5SDimitry Andric   class Lock {
720b57cec5SDimitry Andric   public:
73fe6060f1SDimitry Andric     // If interrupt_timeout == 0 seconds, only take the lock if the target is
74fe6060f1SDimitry Andric     // not running. If using this option, use the fact that the
75fe6060f1SDimitry Andric     // interrupt_timeout is defaulted so it will be obvious at the call site
76fe6060f1SDimitry Andric     // that you are choosing this mode. If it is non-zero, interrupt the target
77fe6060f1SDimitry Andric     // if it is running, waiting for the given timeout for the interrupt to
78fe6060f1SDimitry Andric     // succeed.
79fe6060f1SDimitry Andric     Lock(GDBRemoteClientBase &comm,
80fe6060f1SDimitry Andric          std::chrono::seconds interrupt_timeout = std::chrono::seconds(0));
810b57cec5SDimitry Andric     ~Lock();
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric     explicit operator bool() { return m_acquired; }
840b57cec5SDimitry Andric 
850b57cec5SDimitry Andric     // Whether we had to interrupt the continue thread to acquire the
860b57cec5SDimitry Andric     // connection.
DidInterrupt()870b57cec5SDimitry Andric     bool DidInterrupt() const { return m_did_interrupt; }
880b57cec5SDimitry Andric 
890b57cec5SDimitry Andric   private:
900b57cec5SDimitry Andric     std::unique_lock<std::recursive_mutex> m_async_lock;
910b57cec5SDimitry Andric     GDBRemoteClientBase &m_comm;
92fe6060f1SDimitry Andric     std::chrono::seconds m_interrupt_timeout;
930b57cec5SDimitry Andric     bool m_acquired;
940b57cec5SDimitry Andric     bool m_did_interrupt;
950b57cec5SDimitry Andric 
96fe6060f1SDimitry Andric     void SyncWithContinueThread();
970b57cec5SDimitry Andric   };
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric protected:
1000b57cec5SDimitry Andric   PacketResult
1010b57cec5SDimitry Andric   SendPacketAndWaitForResponseNoLock(llvm::StringRef payload,
1020b57cec5SDimitry Andric                                      StringExtractorGDBRemote &response);
1030b57cec5SDimitry Andric 
1040b57cec5SDimitry Andric   virtual void OnRunPacketSent(bool first);
1050b57cec5SDimitry Andric 
1060b57cec5SDimitry Andric private:
1079dba64beSDimitry Andric   /// Variables handling synchronization between the Continue thread and any
1089dba64beSDimitry Andric   /// other threads wishing to send packets over the connection. Either the
1099dba64beSDimitry Andric   /// continue thread has control over the connection (m_is_running == true) or
1109dba64beSDimitry Andric   /// the connection is free for an arbitrary number of other senders to take
1119dba64beSDimitry Andric   /// which indicate their interest by incrementing m_async_count.
1129dba64beSDimitry Andric   ///
1139dba64beSDimitry Andric   /// Semantics of individual states:
1149dba64beSDimitry Andric   ///
1159dba64beSDimitry Andric   /// - m_continue_packet == false, m_async_count == 0:
1169dba64beSDimitry Andric   ///   connection is free
1179dba64beSDimitry Andric   /// - m_continue_packet == true, m_async_count == 0:
1189dba64beSDimitry Andric   ///   only continue thread is present
1199dba64beSDimitry Andric   /// - m_continue_packet == true, m_async_count > 0:
1209dba64beSDimitry Andric   ///   continue thread has control, async threads should interrupt it and wait
1219dba64beSDimitry Andric   ///   for it to set m_continue_packet to false
1229dba64beSDimitry Andric   /// - m_continue_packet == false, m_async_count > 0:
1239dba64beSDimitry Andric   ///   async threads have control, continue thread needs to wait for them to
1249dba64beSDimitry Andric   ///   finish (m_async_count goes down to 0).
1259dba64beSDimitry Andric   /// @{
1260b57cec5SDimitry Andric   std::mutex m_mutex;
1270b57cec5SDimitry Andric   std::condition_variable m_cv;
1280b57cec5SDimitry Andric 
1299dba64beSDimitry Andric   /// Packet with which to resume after an async interrupt. Can be changed by
1309dba64beSDimitry Andric   /// an async thread e.g. to inject a signal.
1319dba64beSDimitry Andric   std::string m_continue_packet;
1329dba64beSDimitry Andric 
1339dba64beSDimitry Andric   /// When was the interrupt packet sent. Used to make sure we time out if the
1349dba64beSDimitry Andric   /// stub does not respond to interrupt requests.
135fe6060f1SDimitry Andric   std::chrono::time_point<std::chrono::steady_clock> m_interrupt_endpoint;
1369dba64beSDimitry Andric 
1379dba64beSDimitry Andric   /// Number of threads interested in sending.
1389dba64beSDimitry Andric   uint32_t m_async_count;
1399dba64beSDimitry Andric 
1409dba64beSDimitry Andric   /// Whether the continue thread has control.
1419dba64beSDimitry Andric   bool m_is_running;
1429dba64beSDimitry Andric 
1439dba64beSDimitry Andric   /// Whether we should resume after a stop.
1449dba64beSDimitry Andric   bool m_should_stop;
1459dba64beSDimitry Andric   /// @}
1469dba64beSDimitry Andric 
1479dba64beSDimitry Andric   /// This handles the synchronization between individual async threads. For
1489dba64beSDimitry Andric   /// now they just use a simple mutex.
1490b57cec5SDimitry Andric   std::recursive_mutex m_async_mutex;
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric   bool ShouldStop(const UnixSignals &signals,
1520b57cec5SDimitry Andric                   StringExtractorGDBRemote &response);
1530b57cec5SDimitry Andric 
1540b57cec5SDimitry Andric   class ContinueLock {
1550b57cec5SDimitry Andric   public:
1560b57cec5SDimitry Andric     enum class LockResult { Success, Cancelled, Failed };
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric     explicit ContinueLock(GDBRemoteClientBase &comm);
1590b57cec5SDimitry Andric     ~ContinueLock();
1600b57cec5SDimitry Andric     explicit operator bool() { return m_acquired; }
1610b57cec5SDimitry Andric 
1620b57cec5SDimitry Andric     LockResult lock();
1630b57cec5SDimitry Andric 
1640b57cec5SDimitry Andric     void unlock();
1650b57cec5SDimitry Andric 
1660b57cec5SDimitry Andric   private:
1670b57cec5SDimitry Andric     GDBRemoteClientBase &m_comm;
1680b57cec5SDimitry Andric     bool m_acquired;
1690b57cec5SDimitry Andric   };
1700b57cec5SDimitry Andric };
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric } // namespace process_gdb_remote
1730b57cec5SDimitry Andric } // namespace lldb_private
1740b57cec5SDimitry Andric 
1755ffd83dbSDimitry Andric #endif // LLDB_SOURCE_PLUGINS_PROCESS_GDB_REMOTE_GDBREMOTECLIENTBASE_H
176