1 | //===-- WatchpointOptions.cpp ---------------------------------------------===// |
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 | #include "lldb/Breakpoint/WatchpointOptions.h" |
10 | |
11 | #include "lldb/Breakpoint/StoppointCallbackContext.h" |
12 | #include "lldb/Core/Value.h" |
13 | #include "lldb/Target/Process.h" |
14 | #include "lldb/Target/Target.h" |
15 | #include "lldb/Target/ThreadSpec.h" |
16 | #include "lldb/Utility/Stream.h" |
17 | #include "lldb/Utility/StringList.h" |
18 | |
19 | using namespace lldb; |
20 | using namespace lldb_private; |
21 | |
22 | bool WatchpointOptions::NullCallback(void *baton, |
23 | StoppointCallbackContext *context, |
24 | lldb::user_id_t watch_id) { |
25 | return true; |
26 | } |
27 | |
28 | // WatchpointOptions constructor |
29 | WatchpointOptions::WatchpointOptions() |
30 | : m_callback(WatchpointOptions::NullCallback) {} |
31 | |
32 | // WatchpointOptions copy constructor |
33 | WatchpointOptions::WatchpointOptions(const WatchpointOptions &rhs) |
34 | : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp), |
35 | m_callback_is_synchronous(rhs.m_callback_is_synchronous) { |
36 | if (rhs.m_thread_spec_up != nullptr) |
37 | m_thread_spec_up = std::make_unique<ThreadSpec>(args&: *rhs.m_thread_spec_up); |
38 | } |
39 | |
40 | // WatchpointOptions assignment operator |
41 | const WatchpointOptions &WatchpointOptions:: |
42 | operator=(const WatchpointOptions &rhs) { |
43 | m_callback = rhs.m_callback; |
44 | m_callback_baton_sp = rhs.m_callback_baton_sp; |
45 | m_callback_is_synchronous = rhs.m_callback_is_synchronous; |
46 | if (rhs.m_thread_spec_up != nullptr) |
47 | m_thread_spec_up = std::make_unique<ThreadSpec>(args&: *rhs.m_thread_spec_up); |
48 | return *this; |
49 | } |
50 | |
51 | WatchpointOptions * |
52 | WatchpointOptions::CopyOptionsNoCallback(WatchpointOptions &orig) { |
53 | WatchpointHitCallback orig_callback = orig.m_callback; |
54 | lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp; |
55 | bool orig_is_sync = orig.m_callback_is_synchronous; |
56 | |
57 | orig.ClearCallback(); |
58 | WatchpointOptions *ret_val = new WatchpointOptions(orig); |
59 | |
60 | orig.SetCallback(callback: orig_callback, baton_sp: orig_callback_baton_sp, synchronous: orig_is_sync); |
61 | |
62 | return ret_val; |
63 | } |
64 | |
65 | // Destructor |
66 | WatchpointOptions::~WatchpointOptions() = default; |
67 | |
68 | // Callbacks |
69 | void WatchpointOptions::SetCallback(WatchpointHitCallback callback, |
70 | const BatonSP &callback_baton_sp, |
71 | bool callback_is_synchronous) { |
72 | m_callback_is_synchronous = callback_is_synchronous; |
73 | m_callback = callback; |
74 | m_callback_baton_sp = callback_baton_sp; |
75 | } |
76 | |
77 | void WatchpointOptions::ClearCallback() { |
78 | m_callback = WatchpointOptions::NullCallback; |
79 | m_callback_is_synchronous = false; |
80 | m_callback_baton_sp.reset(); |
81 | } |
82 | |
83 | Baton *WatchpointOptions::GetBaton() { return m_callback_baton_sp.get(); } |
84 | |
85 | const Baton *WatchpointOptions::GetBaton() const { |
86 | return m_callback_baton_sp.get(); |
87 | } |
88 | |
89 | bool WatchpointOptions::InvokeCallback(StoppointCallbackContext *context, |
90 | lldb::user_id_t watch_id) { |
91 | if (m_callback && context->is_synchronous == IsCallbackSynchronous()) { |
92 | return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() |
93 | : nullptr, |
94 | context, watch_id); |
95 | } else |
96 | return true; |
97 | } |
98 | |
99 | bool WatchpointOptions::HasCallback() { |
100 | return m_callback != WatchpointOptions::NullCallback; |
101 | } |
102 | |
103 | const ThreadSpec *WatchpointOptions::GetThreadSpecNoCreate() const { |
104 | return m_thread_spec_up.get(); |
105 | } |
106 | |
107 | ThreadSpec *WatchpointOptions::GetThreadSpec() { |
108 | if (m_thread_spec_up == nullptr) |
109 | m_thread_spec_up = std::make_unique<ThreadSpec>(); |
110 | |
111 | return m_thread_spec_up.get(); |
112 | } |
113 | |
114 | void WatchpointOptions::SetThreadID(lldb::tid_t thread_id) { |
115 | GetThreadSpec()->SetTID(thread_id); |
116 | } |
117 | |
118 | void WatchpointOptions::GetCallbackDescription( |
119 | Stream *s, lldb::DescriptionLevel level) const { |
120 | if (m_callback_baton_sp.get()) { |
121 | s->EOL(); |
122 | m_callback_baton_sp->GetDescription(s&: s->AsRawOstream(), level, |
123 | indentation: s->GetIndentLevel()); |
124 | } |
125 | } |
126 | |
127 | void WatchpointOptions::GetDescription(Stream *s, |
128 | lldb::DescriptionLevel level) const { |
129 | // Figure out if there are any options not at their default value, and only |
130 | // print anything if there are: |
131 | |
132 | if ((GetThreadSpecNoCreate() != nullptr && |
133 | GetThreadSpecNoCreate()->HasSpecification())) { |
134 | if (level == lldb::eDescriptionLevelVerbose) { |
135 | s->EOL(); |
136 | s->IndentMore(); |
137 | s->Indent(); |
138 | s->PutCString(cstr: "Watchpoint Options:\n" ); |
139 | s->IndentMore(); |
140 | s->Indent(); |
141 | } else |
142 | s->PutCString(cstr: " Options: " ); |
143 | |
144 | if (m_thread_spec_up) |
145 | m_thread_spec_up->GetDescription(s, level); |
146 | else if (level == eDescriptionLevelBrief) |
147 | s->PutCString(cstr: "thread spec: no " ); |
148 | if (level == lldb::eDescriptionLevelFull) { |
149 | s->IndentLess(); |
150 | s->IndentMore(); |
151 | } |
152 | } |
153 | |
154 | GetCallbackDescription(s, level); |
155 | } |
156 | |
157 | void WatchpointOptions::CommandBaton::GetDescription( |
158 | llvm::raw_ostream &s, lldb::DescriptionLevel level, |
159 | unsigned indentation) const { |
160 | const CommandData *data = getItem(); |
161 | |
162 | if (level == eDescriptionLevelBrief) { |
163 | s << ", commands = %s" |
164 | << ((data && data->user_source.GetSize() > 0) ? "yes" : "no" ); |
165 | return; |
166 | } |
167 | |
168 | indentation += 2; |
169 | s.indent(NumSpaces: indentation); |
170 | s << "watchpoint commands:\n" ; |
171 | |
172 | indentation += 2; |
173 | if (data && data->user_source.GetSize() > 0) { |
174 | for (const std::string &line : data->user_source) { |
175 | s.indent(NumSpaces: indentation); |
176 | s << line << "\n" ; |
177 | } |
178 | } else |
179 | s << "No commands.\n" ; |
180 | } |
181 | |