1 | //===-- Debug.h -------------------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLDB_HOST_DEBUG_H |
10 | #define LLDB_HOST_DEBUG_H |
11 | |
12 | #include <vector> |
13 | |
14 | #include "lldb/lldb-private.h" |
15 | |
16 | namespace lldb_private { |
17 | |
18 | // Tells a thread what it needs to do when the process is resumed. |
19 | struct ResumeAction { |
20 | lldb::tid_t tid; // The thread ID that this action applies to, |
21 | // LLDB_INVALID_THREAD_ID for the default thread |
22 | // action |
23 | lldb::StateType state; // Valid values are eStateStopped/eStateSuspended, |
24 | // eStateRunning, and eStateStepping. |
25 | int signal; // When resuming this thread, resume it with this signal if this |
26 | // value is > 0 |
27 | }; |
28 | |
29 | // A class that contains instructions for all threads for |
30 | // NativeProcessProtocol::Resume(). Each thread can either run, stay suspended, |
31 | // or step when the process is resumed. We optionally have the ability to also |
32 | // send a signal to the thread when the action is run or step. |
33 | class ResumeActionList { |
34 | public: |
35 | ResumeActionList() = default; |
36 | |
37 | ResumeActionList(lldb::StateType default_action, int signal) { |
38 | SetDefaultThreadActionIfNeeded(action: default_action, signal); |
39 | } |
40 | |
41 | ResumeActionList(const ResumeAction *actions, size_t num_actions) { |
42 | if (actions && num_actions) { |
43 | m_actions.assign(first: actions, last: actions + num_actions); |
44 | m_signal_handled.assign(n: num_actions, x: false); |
45 | } |
46 | } |
47 | |
48 | ~ResumeActionList() = default; |
49 | |
50 | bool IsEmpty() const { return m_actions.empty(); } |
51 | |
52 | void Append(const ResumeAction &action) { |
53 | m_actions.push_back(x: action); |
54 | m_signal_handled.push_back(x: false); |
55 | } |
56 | |
57 | void AppendAction(lldb::tid_t tid, lldb::StateType state, int signal = 0) { |
58 | ResumeAction action = {.tid: tid, .state: state, .signal: signal}; |
59 | Append(action); |
60 | } |
61 | |
62 | void AppendResumeAll() { |
63 | AppendAction(LLDB_INVALID_THREAD_ID, state: lldb::eStateRunning); |
64 | } |
65 | |
66 | void AppendSuspendAll() { |
67 | AppendAction(LLDB_INVALID_THREAD_ID, state: lldb::eStateStopped); |
68 | } |
69 | |
70 | void AppendStepAll() { |
71 | AppendAction(LLDB_INVALID_THREAD_ID, state: lldb::eStateStepping); |
72 | } |
73 | |
74 | const ResumeAction *GetActionForThread(lldb::tid_t tid, |
75 | bool default_ok) const { |
76 | const size_t num_actions = m_actions.size(); |
77 | for (size_t i = 0; i < num_actions; ++i) { |
78 | if (m_actions[i].tid == tid) |
79 | return &m_actions[i]; |
80 | } |
81 | if (default_ok && tid != LLDB_INVALID_THREAD_ID) |
82 | return GetActionForThread(LLDB_INVALID_THREAD_ID, default_ok: false); |
83 | return nullptr; |
84 | } |
85 | |
86 | size_t NumActionsWithState(lldb::StateType state) const { |
87 | size_t count = 0; |
88 | const size_t num_actions = m_actions.size(); |
89 | for (size_t i = 0; i < num_actions; ++i) { |
90 | if (m_actions[i].state == state) |
91 | ++count; |
92 | } |
93 | return count; |
94 | } |
95 | |
96 | bool SetDefaultThreadActionIfNeeded(lldb::StateType action, int signal) { |
97 | if (GetActionForThread(LLDB_INVALID_THREAD_ID, default_ok: true) == nullptr) { |
98 | // There isn't a default action so we do need to set it. |
99 | ResumeAction default_action = {LLDB_INVALID_THREAD_ID, .state: action, .signal: signal}; |
100 | m_actions.push_back(x: default_action); |
101 | m_signal_handled.push_back(x: false); |
102 | return true; // Return true as we did add the default action |
103 | } |
104 | return false; |
105 | } |
106 | |
107 | void SetSignalHandledForThread(lldb::tid_t tid) const { |
108 | if (tid != LLDB_INVALID_THREAD_ID) { |
109 | const size_t num_actions = m_actions.size(); |
110 | for (size_t i = 0; i < num_actions; ++i) { |
111 | if (m_actions[i].tid == tid) |
112 | m_signal_handled[i] = true; |
113 | } |
114 | } |
115 | } |
116 | |
117 | const ResumeAction *GetFirst() const { return m_actions.data(); } |
118 | |
119 | size_t GetSize() const { return m_actions.size(); } |
120 | |
121 | void Clear() { |
122 | m_actions.clear(); |
123 | m_signal_handled.clear(); |
124 | } |
125 | |
126 | protected: |
127 | std::vector<ResumeAction> m_actions; |
128 | mutable std::vector<bool> m_signal_handled; |
129 | }; |
130 | |
131 | struct ThreadStopInfo { |
132 | lldb::StopReason reason; |
133 | uint32_t signo; |
134 | union { |
135 | // eStopReasonException |
136 | struct { |
137 | uint64_t type; |
138 | uint32_t data_count; |
139 | lldb::addr_t data[8]; |
140 | } exception; |
141 | |
142 | // eStopReasonFork / eStopReasonVFork |
143 | struct { |
144 | lldb::pid_t child_pid; |
145 | lldb::tid_t child_tid; |
146 | } fork; |
147 | } details; |
148 | }; |
149 | } |
150 | |
151 | #endif // LLDB_HOST_DEBUG_H |
152 | |