/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Core/ValueObjectSyntheticFilter.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- ValueObjectSyntheticFilter.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/Core/ValueObjectSyntheticFilter.h" |
10 | | |
11 | | #include "lldb/Core/Value.h" |
12 | | #include "lldb/Core/ValueObject.h" |
13 | | #include "lldb/DataFormatters/TypeSynthetic.h" |
14 | | #include "lldb/Target/ExecutionContext.h" |
15 | | #include "lldb/Utility/ConstString.h" |
16 | | #include "lldb/Utility/LLDBLog.h" |
17 | | #include "lldb/Utility/Log.h" |
18 | | #include "lldb/Utility/Status.h" |
19 | | |
20 | | #include "llvm/ADT/STLExtras.h" |
21 | | #include <optional> |
22 | | |
23 | | namespace lldb_private { |
24 | | class Declaration; |
25 | | } |
26 | | |
27 | | using namespace lldb_private; |
28 | | |
29 | | class DummySyntheticFrontEnd : public SyntheticChildrenFrontEnd { |
30 | | public: |
31 | | DummySyntheticFrontEnd(ValueObject &backend) |
32 | 44 | : SyntheticChildrenFrontEnd(backend) {} |
33 | | |
34 | 9 | size_t CalculateNumChildren() override { return m_backend.GetNumChildren(); } |
35 | | |
36 | 36 | lldb::ValueObjectSP GetChildAtIndex(size_t idx) override { |
37 | 36 | return m_backend.GetChildAtIndex(idx); |
38 | 36 | } |
39 | | |
40 | 0 | size_t GetIndexOfChildWithName(ConstString name) override { |
41 | 0 | return m_backend.GetIndexOfChildWithName(name); |
42 | 0 | } |
43 | | |
44 | 2 | bool MightHaveChildren() override { return true; } |
45 | | |
46 | 42 | bool Update() override { return false; } |
47 | | }; |
48 | | |
49 | | ValueObjectSynthetic::ValueObjectSynthetic(ValueObject &parent, |
50 | | lldb::SyntheticChildrenSP filter) |
51 | 1.84k | : ValueObject(parent), m_synth_sp(std::move(filter)), m_children_byindex(), |
52 | 1.84k | m_name_toindex(), m_synthetic_children_cache(), |
53 | | m_synthetic_children_count(UINT32_MAX), |
54 | 1.84k | m_parent_type_name(parent.GetTypeName()), |
55 | 1.84k | m_might_have_children(eLazyBoolCalculate), |
56 | 1.84k | m_provides_value(eLazyBoolCalculate) { |
57 | 1.84k | SetName(parent.GetName()); |
58 | | // Copying the data of an incomplete type won't work as it has no byte size. |
59 | 1.84k | if (m_parent->GetCompilerType().IsCompleteType()) |
60 | 1.84k | CopyValueData(m_parent); |
61 | 1.84k | CreateSynthFilter(); |
62 | 1.84k | } |
63 | | |
64 | 1.55k | ValueObjectSynthetic::~ValueObjectSynthetic() = default; |
65 | | |
66 | 33.8k | CompilerType ValueObjectSynthetic::GetCompilerTypeImpl() { |
67 | 33.8k | return m_parent->GetCompilerType(); |
68 | 33.8k | } |
69 | | |
70 | 0 | ConstString ValueObjectSynthetic::GetTypeName() { |
71 | 0 | return m_parent->GetTypeName(); |
72 | 0 | } |
73 | | |
74 | 4.88k | ConstString ValueObjectSynthetic::GetQualifiedTypeName() { |
75 | 4.88k | return m_parent->GetQualifiedTypeName(); |
76 | 4.88k | } |
77 | | |
78 | 1.67k | ConstString ValueObjectSynthetic::GetDisplayTypeName() { |
79 | 1.67k | if (ConstString synth_name = m_synth_filter_up->GetSyntheticTypeName()) |
80 | 6 | return synth_name; |
81 | | |
82 | 1.67k | return m_parent->GetDisplayTypeName(); |
83 | 1.67k | } |
84 | | |
85 | 863 | size_t ValueObjectSynthetic::CalculateNumChildren(uint32_t max) { |
86 | 863 | Log *log = GetLog(LLDBLog::DataFormatters); |
87 | | |
88 | 863 | UpdateValueIfNeeded(); |
89 | 863 | if (m_synthetic_children_count < UINT32_MAX) |
90 | 0 | return m_synthetic_children_count <= max ? m_synthetic_children_count : max; |
91 | | |
92 | 863 | if (max < UINT32_MAX) { |
93 | 6 | size_t num_children = m_synth_filter_up->CalculateNumChildren(max); |
94 | 6 | LLDB_LOGF(log, |
95 | 6 | "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
96 | 6 | "%s and type %s, the filter returned %zu child values", |
97 | 6 | GetName().AsCString(), GetTypeName().AsCString(), num_children); |
98 | 6 | return num_children; |
99 | 857 | } else { |
100 | 857 | size_t num_children = (m_synthetic_children_count = |
101 | 857 | m_synth_filter_up->CalculateNumChildren(max)); |
102 | 857 | LLDB_LOGF(log, |
103 | 857 | "[ValueObjectSynthetic::CalculateNumChildren] for VO of name " |
104 | 857 | "%s and type %s, the filter returned %zu child values", |
105 | 857 | GetName().AsCString(), GetTypeName().AsCString(), num_children); |
106 | 857 | return num_children; |
107 | 857 | } |
108 | 863 | } |
109 | | |
110 | | lldb::ValueObjectSP |
111 | 243 | ValueObjectSynthetic::GetDynamicValue(lldb::DynamicValueType valueType) { |
112 | 243 | if (!m_parent) |
113 | 0 | return lldb::ValueObjectSP(); |
114 | 243 | if (IsDynamic() && GetDynamicValueType() == valueType0 ) |
115 | 0 | return GetSP(); |
116 | 243 | return m_parent->GetDynamicValue(valueType); |
117 | 243 | } |
118 | | |
119 | 447 | bool ValueObjectSynthetic::MightHaveChildren() { |
120 | 447 | if (m_might_have_children == eLazyBoolCalculate) |
121 | 328 | m_might_have_children = |
122 | 328 | (m_synth_filter_up->MightHaveChildren() ? eLazyBoolYes199 : eLazyBoolNo129 ); |
123 | 447 | return (m_might_have_children != eLazyBoolNo); |
124 | 447 | } |
125 | | |
126 | 2 | std::optional<uint64_t> ValueObjectSynthetic::GetByteSize() { |
127 | 2 | return m_parent->GetByteSize(); |
128 | 2 | } |
129 | | |
130 | 2 | lldb::ValueType ValueObjectSynthetic::GetValueType() const { |
131 | 2 | return m_parent->GetValueType(); |
132 | 2 | } |
133 | | |
134 | 1.84k | void ValueObjectSynthetic::CreateSynthFilter() { |
135 | 1.84k | ValueObject *valobj_for_frontend = m_parent; |
136 | 1.84k | if (m_synth_sp->WantsDereference()) |
137 | 274 | { |
138 | 274 | CompilerType type = m_parent->GetCompilerType(); |
139 | 274 | if (type.IsValid() && type.IsPointerOrReferenceType()) |
140 | 48 | { |
141 | 48 | Status error; |
142 | 48 | lldb::ValueObjectSP deref_sp = m_parent->Dereference(error); |
143 | 48 | if (error.Success()) |
144 | 46 | valobj_for_frontend = deref_sp.get(); |
145 | 48 | } |
146 | 274 | } |
147 | 1.84k | m_synth_filter_up = (m_synth_sp->GetFrontEnd(*valobj_for_frontend)); |
148 | 1.84k | if (!m_synth_filter_up) |
149 | 44 | m_synth_filter_up = std::make_unique<DummySyntheticFrontEnd>(*m_parent); |
150 | 1.84k | } |
151 | | |
152 | 1.66k | bool ValueObjectSynthetic::UpdateValue() { |
153 | 1.66k | Log *log = GetLog(LLDBLog::DataFormatters); |
154 | | |
155 | 1.66k | SetValueIsValid(false); |
156 | 1.66k | m_error.Clear(); |
157 | | |
158 | 1.66k | if (!m_parent->UpdateValueIfNeeded(false)) { |
159 | | // our parent could not update.. as we are meaningless without a parent, |
160 | | // just stop |
161 | 17 | if (m_parent->GetError().Fail()) |
162 | 17 | m_error = m_parent->GetError(); |
163 | 17 | return false; |
164 | 17 | } |
165 | | |
166 | | // Regenerate the synthetic filter if our typename changes. When the (dynamic) |
167 | | // type of an object changes, so does their synthetic filter of choice. |
168 | 1.64k | ConstString new_parent_type_name = m_parent->GetTypeName(); |
169 | 1.64k | if (new_parent_type_name != m_parent_type_name) { |
170 | 0 | LLDB_LOGF(log, |
171 | 0 | "[ValueObjectSynthetic::UpdateValue] name=%s, type changed " |
172 | 0 | "from %s to %s, recomputing synthetic filter", |
173 | 0 | GetName().AsCString(), m_parent_type_name.AsCString(), |
174 | 0 | new_parent_type_name.AsCString()); |
175 | 0 | m_parent_type_name = new_parent_type_name; |
176 | 0 | CreateSynthFilter(); |
177 | 0 | } |
178 | | |
179 | | // let our backend do its update |
180 | 1.64k | if (!m_synth_filter_up->Update()) { |
181 | 1.24k | LLDB_LOGF(log, |
182 | 1.24k | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
183 | 1.24k | "filter said caches are stale - clearing", |
184 | 1.24k | GetName().AsCString()); |
185 | | // filter said that cached values are stale |
186 | 1.24k | { |
187 | 1.24k | std::lock_guard<std::mutex> guard(m_child_mutex); |
188 | 1.24k | m_children_byindex.clear(); |
189 | 1.24k | m_name_toindex.clear(); |
190 | 1.24k | } |
191 | | // usually, an object's value can change but this does not alter its |
192 | | // children count for a synthetic VO that might indeed happen, so we need |
193 | | // to tell the upper echelons that they need to come back to us asking for |
194 | | // children |
195 | 1.24k | m_flags.m_children_count_valid = false; |
196 | 1.24k | { |
197 | 1.24k | std::lock_guard<std::mutex> guard(m_child_mutex); |
198 | 1.24k | m_synthetic_children_cache.clear(); |
199 | 1.24k | } |
200 | 1.24k | m_synthetic_children_count = UINT32_MAX; |
201 | 1.24k | m_might_have_children = eLazyBoolCalculate; |
202 | 1.24k | } else { |
203 | 403 | LLDB_LOGF(log, |
204 | 403 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
205 | 403 | "filter said caches are still valid", |
206 | 403 | GetName().AsCString()); |
207 | 403 | } |
208 | | |
209 | 1.64k | m_provides_value = eLazyBoolCalculate; |
210 | | |
211 | 1.64k | lldb::ValueObjectSP synth_val(m_synth_filter_up->GetSyntheticValue()); |
212 | | |
213 | 1.64k | if (synth_val && synth_val->CanProvideValue()12 ) { |
214 | 12 | LLDB_LOGF(log, |
215 | 12 | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
216 | 12 | "filter said it can provide a value", |
217 | 12 | GetName().AsCString()); |
218 | | |
219 | 12 | m_provides_value = eLazyBoolYes; |
220 | 12 | CopyValueData(synth_val.get()); |
221 | 1.63k | } else { |
222 | 1.63k | LLDB_LOGF(log, |
223 | 1.63k | "[ValueObjectSynthetic::UpdateValue] name=%s, synthetic " |
224 | 1.63k | "filter said it will not provide a value", |
225 | 1.63k | GetName().AsCString()); |
226 | | |
227 | 1.63k | m_provides_value = eLazyBoolNo; |
228 | | // Copying the data of an incomplete type won't work as it has no byte size. |
229 | 1.63k | if (m_parent->GetCompilerType().IsCompleteType()) |
230 | 1.62k | CopyValueData(m_parent); |
231 | 1.63k | } |
232 | | |
233 | 1.64k | SetValueIsValid(true); |
234 | 1.64k | return true; |
235 | 1.66k | } |
236 | | |
237 | | lldb::ValueObjectSP ValueObjectSynthetic::GetChildAtIndex(size_t idx, |
238 | 24.1k | bool can_create) { |
239 | 24.1k | Log *log = GetLog(LLDBLog::DataFormatters); |
240 | | |
241 | 24.1k | LLDB_LOGF(log, |
242 | 24.1k | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, retrieving " |
243 | 24.1k | "child at index %zu", |
244 | 24.1k | GetName().AsCString(), idx); |
245 | | |
246 | 24.1k | UpdateValueIfNeeded(); |
247 | | |
248 | 24.1k | ValueObject *valobj; |
249 | 24.1k | bool child_is_cached; |
250 | 24.1k | { |
251 | 24.1k | std::lock_guard<std::mutex> guard(m_child_mutex); |
252 | 24.1k | auto cached_child_it = m_children_byindex.find(idx); |
253 | 24.1k | child_is_cached = cached_child_it != m_children_byindex.end(); |
254 | 24.1k | if (child_is_cached) |
255 | 12.9k | valobj = cached_child_it->second; |
256 | 24.1k | } |
257 | | |
258 | 24.1k | if (!child_is_cached) { |
259 | 11.1k | if (can_create && m_synth_filter_up != nullptr) { |
260 | 11.1k | LLDB_LOGF(log, |
261 | 11.1k | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
262 | 11.1k | "index %zu not cached and will be created", |
263 | 11.1k | GetName().AsCString(), idx); |
264 | | |
265 | 11.1k | lldb::ValueObjectSP synth_guy = m_synth_filter_up->GetChildAtIndex(idx); |
266 | | |
267 | 11.1k | LLDB_LOGF( |
268 | 11.1k | log, |
269 | 11.1k | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at index " |
270 | 11.1k | "%zu created as %p (is " |
271 | 11.1k | "synthetic: %s)", |
272 | 11.1k | GetName().AsCString(), idx, static_cast<void *>(synth_guy.get()), |
273 | 11.1k | synth_guy.get() |
274 | 11.1k | ? (synth_guy->IsSyntheticChildrenGenerated() ? "yes" : "no") |
275 | 11.1k | : "no"); |
276 | | |
277 | 11.1k | if (!synth_guy) |
278 | 45 | return synth_guy; |
279 | | |
280 | 11.1k | { |
281 | 11.1k | std::lock_guard<std::mutex> guard(m_child_mutex); |
282 | 11.1k | if (synth_guy->IsSyntheticChildrenGenerated()) |
283 | 9.23k | m_synthetic_children_cache.push_back(synth_guy); |
284 | 11.1k | m_children_byindex[idx] = synth_guy.get(); |
285 | 11.1k | } |
286 | 11.1k | synth_guy->SetPreferredDisplayLanguageIfNeeded( |
287 | 11.1k | GetPreferredDisplayLanguage()); |
288 | 11.1k | return synth_guy; |
289 | 11.1k | } else { |
290 | 0 | LLDB_LOGF(log, |
291 | 0 | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
292 | 0 | "index %zu not cached and cannot " |
293 | 0 | "be created (can_create = %s, synth_filter = %p)", |
294 | 0 | GetName().AsCString(), idx, can_create ? "yes" : "no", |
295 | 0 | static_cast<void *>(m_synth_filter_up.get())); |
296 | |
|
297 | 0 | return lldb::ValueObjectSP(); |
298 | 0 | } |
299 | 12.9k | } else { |
300 | 12.9k | LLDB_LOGF(log, |
301 | 12.9k | "[ValueObjectSynthetic::GetChildAtIndex] name=%s, child at " |
302 | 12.9k | "index %zu cached as %p", |
303 | 12.9k | GetName().AsCString(), idx, static_cast<void *>(valobj)); |
304 | | |
305 | 12.9k | return valobj->GetSP(); |
306 | 12.9k | } |
307 | 24.1k | } |
308 | | |
309 | | lldb::ValueObjectSP |
310 | | ValueObjectSynthetic::GetChildMemberWithName(llvm::StringRef name, |
311 | 57 | bool can_create) { |
312 | 57 | UpdateValueIfNeeded(); |
313 | | |
314 | 57 | uint32_t index = GetIndexOfChildWithName(name); |
315 | | |
316 | 57 | if (index == UINT32_MAX) |
317 | 6 | return lldb::ValueObjectSP(); |
318 | | |
319 | 51 | return GetChildAtIndex(index, can_create); |
320 | 57 | } |
321 | | |
322 | 57 | size_t ValueObjectSynthetic::GetIndexOfChildWithName(llvm::StringRef name_ref) { |
323 | 57 | UpdateValueIfNeeded(); |
324 | | |
325 | 57 | ConstString name(name_ref); |
326 | | |
327 | 57 | uint32_t found_index = UINT32_MAX; |
328 | 57 | bool did_find; |
329 | 57 | { |
330 | 57 | std::lock_guard<std::mutex> guard(m_child_mutex); |
331 | 57 | auto name_to_index = m_name_toindex.find(name.GetCString()); |
332 | 57 | did_find = name_to_index != m_name_toindex.end(); |
333 | 57 | if (did_find) |
334 | 10 | found_index = name_to_index->second; |
335 | 57 | } |
336 | | |
337 | 57 | if (!did_find && m_synth_filter_up != nullptr47 ) { |
338 | 47 | uint32_t index = m_synth_filter_up->GetIndexOfChildWithName(name); |
339 | 47 | if (index == UINT32_MAX) |
340 | 6 | return index; |
341 | 41 | std::lock_guard<std::mutex> guard(m_child_mutex); |
342 | 41 | m_name_toindex[name.GetCString()] = index; |
343 | 41 | return index; |
344 | 47 | } else if (10 !did_find10 && m_synth_filter_up == nullptr0 ) |
345 | 0 | return UINT32_MAX; |
346 | 10 | else /*if (iter != m_name_toindex.end())*/ |
347 | 10 | return found_index; |
348 | 57 | } |
349 | | |
350 | 3.19k | bool ValueObjectSynthetic::IsInScope() { return m_parent->IsInScope(); } |
351 | | |
352 | 270 | lldb::ValueObjectSP ValueObjectSynthetic::GetNonSyntheticValue() { |
353 | 270 | return m_parent->GetSP(); |
354 | 270 | } |
355 | | |
356 | 3.48k | void ValueObjectSynthetic::CopyValueData(ValueObject *source) { |
357 | 3.48k | m_value = (source->UpdateValueIfNeeded(), source->GetValue()); |
358 | 3.48k | ExecutionContext exe_ctx(GetExecutionContextRef()); |
359 | 3.48k | m_error = m_value.GetValueAsData(&exe_ctx, m_data, GetModule().get()); |
360 | 3.48k | } |
361 | | |
362 | 3.32k | bool ValueObjectSynthetic::CanProvideValue() { |
363 | 3.32k | if (!UpdateValueIfNeeded()) |
364 | 0 | return false; |
365 | 3.32k | if (m_provides_value == eLazyBoolYes) |
366 | 28 | return true; |
367 | 3.29k | return m_parent->CanProvideValue(); |
368 | 3.32k | } |
369 | | |
370 | | bool ValueObjectSynthetic::SetValueFromCString(const char *value_str, |
371 | 2 | Status &error) { |
372 | 2 | return m_parent->SetValueFromCString(value_str, error); |
373 | 2 | } |
374 | | |
375 | 6 | void ValueObjectSynthetic::SetFormat(lldb::Format format) { |
376 | 6 | if (m_parent) { |
377 | 6 | m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
378 | 6 | m_parent->SetFormat(format); |
379 | 6 | } |
380 | 6 | this->ValueObject::SetFormat(format); |
381 | 6 | this->ClearUserVisibleData(eClearUserVisibleDataItemsAll); |
382 | 6 | } |
383 | | |
384 | | void ValueObjectSynthetic::SetPreferredDisplayLanguage( |
385 | 0 | lldb::LanguageType lang) { |
386 | 0 | this->ValueObject::SetPreferredDisplayLanguage(lang); |
387 | 0 | if (m_parent) |
388 | 0 | m_parent->SetPreferredDisplayLanguage(lang); |
389 | 0 | } |
390 | | |
391 | 11.9k | lldb::LanguageType ValueObjectSynthetic::GetPreferredDisplayLanguage() { |
392 | 11.9k | if (m_preferred_display_language == lldb::eLanguageTypeUnknown) { |
393 | 11.9k | if (m_parent) |
394 | 11.9k | return m_parent->GetPreferredDisplayLanguage(); |
395 | 0 | return lldb::eLanguageTypeUnknown; |
396 | 11.9k | } else |
397 | 6 | return m_preferred_display_language; |
398 | 11.9k | } |
399 | | |
400 | 2 | bool ValueObjectSynthetic::IsSyntheticChildrenGenerated() { |
401 | 2 | if (m_parent) |
402 | 2 | return m_parent->IsSyntheticChildrenGenerated(); |
403 | 0 | return false; |
404 | 2 | } |
405 | | |
406 | 0 | void ValueObjectSynthetic::SetSyntheticChildrenGenerated(bool b) { |
407 | 0 | if (m_parent) |
408 | 0 | m_parent->SetSyntheticChildrenGenerated(b); |
409 | 0 | this->ValueObject::SetSyntheticChildrenGenerated(b); |
410 | 0 | } |
411 | | |
412 | 1 | bool ValueObjectSynthetic::GetDeclaration(Declaration &decl) { |
413 | 1 | if (m_parent) |
414 | 1 | return m_parent->GetDeclaration(decl); |
415 | | |
416 | 0 | return ValueObject::GetDeclaration(decl); |
417 | 1 | } |
418 | | |
419 | 0 | uint64_t ValueObjectSynthetic::GetLanguageFlags() { |
420 | 0 | if (m_parent) |
421 | 0 | return m_parent->GetLanguageFlags(); |
422 | 0 | return this->ValueObject::GetLanguageFlags(); |
423 | 0 | } |
424 | | |
425 | 0 | void ValueObjectSynthetic::SetLanguageFlags(uint64_t flags) { |
426 | 0 | if (m_parent) |
427 | 0 | m_parent->SetLanguageFlags(flags); |
428 | 0 | else |
429 | 0 | this->ValueObject::SetLanguageFlags(flags); |
430 | 0 | } |