Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/Language/CPlusPlus/LibCxxList.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- LibCxxList.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 "LibCxx.h"
10
11
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
12
#include "lldb/Core/ValueObject.h"
13
#include "lldb/Core/ValueObjectConstResult.h"
14
#include "lldb/DataFormatters/FormattersHelpers.h"
15
#include "lldb/Target/Target.h"
16
#include "lldb/Utility/DataBufferHeap.h"
17
#include "lldb/Utility/Endian.h"
18
#include "lldb/Utility/Status.h"
19
#include "lldb/Utility/Stream.h"
20
21
using namespace lldb;
22
using namespace lldb_private;
23
using namespace lldb_private::formatters;
24
25
namespace {
26
27
class ListEntry {
28
public:
29
1.82k
  ListEntry() = default;
30
8.18k
  ListEntry(ValueObjectSP entry_sp) : m_entry_sp(std::move(entry_sp)) {}
31
  ListEntry(ValueObject *entry)
32
1.76k
      : m_entry_sp(entry ? entry->GetSP() : 
ValueObjectSP()0
) {}
33
34
8.18k
  ListEntry next() {
35
8.18k
    if (!m_entry_sp)
36
0
      return ListEntry();
37
8.18k
    return ListEntry(m_entry_sp->GetChildMemberWithName("__next_"));
38
8.18k
  }
39
40
0
  ListEntry prev() {
41
0
    if (!m_entry_sp)
42
0
      return ListEntry();
43
0
    return ListEntry(m_entry_sp->GetChildMemberWithName("__prev_"));
44
0
  }
45
46
8.17k
  uint64_t value() const {
47
8.17k
    if (!m_entry_sp)
48
0
      return 0;
49
8.17k
    return m_entry_sp->GetValueAsUnsigned(0);
50
8.17k
  }
51
52
4.91k
  bool null() { return (value() == 0); }
53
54
4.91k
  explicit operator bool() { return GetEntry() && 
!null()4.91k
; }
55
56
6.61k
  ValueObjectSP GetEntry() { return m_entry_sp; }
57
58
256
  void SetEntry(ValueObjectSP entry) { m_entry_sp = entry; }
59
60
1.63k
  bool operator==(const ListEntry &rhs) const { return value() == rhs.value(); }
61
62
1.63k
  bool operator!=(const ListEntry &rhs) const { return !(*this == rhs); }
63
64
private:
65
  ValueObjectSP m_entry_sp;
66
};
67
68
class ListIterator {
69
public:
70
1.69k
  ListIterator() = default;
71
0
  ListIterator(ListEntry entry) : m_entry(std::move(entry)) {}
72
0
  ListIterator(ValueObjectSP entry) : m_entry(std::move(entry)) {}
73
1.69k
  ListIterator(ValueObject *entry) : m_entry(entry) {}
74
75
0
  ValueObjectSP value() { return m_entry.GetEntry(); }
76
77
1.69k
  ValueObjectSP advance(size_t count) {
78
1.69k
    if (count == 0)
79
54
      return m_entry.GetEntry();
80
1.64k
    if (count == 1) {
81
1.64k
      next();
82
1.64k
      return m_entry.GetEntry();
83
1.64k
    }
84
0
    while (count > 0) {
85
0
      next();
86
0
      count--;
87
0
      if (m_entry.null())
88
0
        return lldb::ValueObjectSP();
89
0
    }
90
0
    return m_entry.GetEntry();
91
0
  }
92
93
0
  bool operator==(const ListIterator &rhs) const {
94
0
    return (rhs.m_entry == m_entry);
95
0
  }
96
97
protected:
98
1.64k
  void next() { m_entry = m_entry.next(); }
99
100
0
  void prev() { m_entry = m_entry.prev(); }
101
102
private:
103
  ListEntry m_entry;
104
};
105
106
class AbstractListFrontEnd : public SyntheticChildrenFrontEnd {
107
public:
108
0
  size_t GetIndexOfChildWithName(ConstString name) override {
109
0
    return ExtractIndexFromString(name.GetCString());
110
0
  }
111
22
  bool MightHaveChildren() override { return true; }
112
  bool Update() override;
113
114
protected:
115
  AbstractListFrontEnd(ValueObject &valobj)
116
64
      : SyntheticChildrenFrontEnd(valobj) {}
117
118
  size_t m_count = 0;
119
  ValueObject *m_head = nullptr;
120
121
  static constexpr bool g_use_loop_detect = true;
122
  size_t m_loop_detected = 0; // The number of elements that have had loop
123
                              // detection run over them.
124
  ListEntry m_slow_runner; // Used for loop detection
125
  ListEntry m_fast_runner; // Used for loop detection
126
127
  size_t m_list_capping_size = 0;
128
  CompilerType m_element_type;
129
  std::map<size_t, ListIterator> m_iterators;
130
131
  bool HasLoop(size_t count);
132
  ValueObjectSP GetItem(size_t idx);
133
};
134
135
class ForwardListFrontEnd : public AbstractListFrontEnd {
136
public:
137
  ForwardListFrontEnd(ValueObject &valobj);
138
139
  size_t CalculateNumChildren() override;
140
  ValueObjectSP GetChildAtIndex(size_t idx) override;
141
  bool Update() override;
142
};
143
144
class ListFrontEnd : public AbstractListFrontEnd {
145
public:
146
  ListFrontEnd(lldb::ValueObjectSP valobj_sp);
147
148
8
  ~ListFrontEnd() override = default;
149
150
  size_t CalculateNumChildren() override;
151
152
  lldb::ValueObjectSP GetChildAtIndex(size_t idx) override;
153
154
  bool Update() override;
155
156
private:
157
  lldb::addr_t m_node_address = 0;
158
  ValueObject *m_tail = nullptr;
159
};
160
161
} // end anonymous namespace
162
163
128
bool AbstractListFrontEnd::Update() {
164
128
  m_loop_detected = 0;
165
128
  m_count = UINT32_MAX;
166
128
  m_head = nullptr;
167
128
  m_list_capping_size = 0;
168
128
  m_slow_runner.SetEntry(nullptr);
169
128
  m_fast_runner.SetEntry(nullptr);
170
128
  m_iterators.clear();
171
172
128
  if (m_backend.GetTargetSP())
173
128
    m_list_capping_size =
174
128
        m_backend.GetTargetSP()->GetMaximumNumberOfChildrenToDisplay();
175
128
  if (m_list_capping_size == 0)
176
0
    m_list_capping_size = 255;
177
178
128
  CompilerType list_type = m_backend.GetCompilerType();
179
128
  if (list_type.IsReferenceType())
180
16
    list_type = list_type.GetNonReferenceType();
181
182
128
  if (list_type.GetNumTemplateArguments() == 0)
183
0
    return false;
184
128
  m_element_type = list_type.GetTypeTemplateArgument(0);
185
186
128
  return false;
187
128
}
188
189
1.69k
bool AbstractListFrontEnd::HasLoop(size_t count) {
190
1.69k
  if (!g_use_loop_detect)
191
0
    return false;
192
  // don't bother checking for a loop if we won't actually need to jump nodes
193
1.69k
  if (m_count < 2)
194
18
    return false;
195
196
1.68k
  if (m_loop_detected == 0) {
197
    // This is the first time we are being run (after the last update). Set up
198
    // the loop invariant for the first element.
199
36
    m_slow_runner = ListEntry(m_head).next();
200
36
    m_fast_runner = m_slow_runner.next();
201
36
    m_loop_detected = 1;
202
36
  }
203
204
  // Loop invariant:
205
  // Loop detection has been run over the first m_loop_detected elements. If
206
  // m_slow_runner == m_fast_runner then the loop has been detected after
207
  // m_loop_detected elements.
208
1.68k
  const size_t steps_to_run = std::min(count, m_count);
209
3.31k
  while (m_loop_detected < steps_to_run && 
m_slow_runner1.64k
&&
m_fast_runner1.64k
&&
210
3.31k
         
m_slow_runner != m_fast_runner1.63k
) {
211
212
1.63k
    m_slow_runner = m_slow_runner.next();
213
1.63k
    m_fast_runner = m_fast_runner.next().next();
214
1.63k
    m_loop_detected++;
215
1.63k
  }
216
1.68k
  if (count <= m_loop_detected)
217
1.66k
    return false; // No loop in the first m_loop_detected elements.
218
12
  if (!m_slow_runner || !m_fast_runner)
219
12
    return false; // Reached the end of the list. Definitely no loops.
220
0
  return m_slow_runner == m_fast_runner;
221
12
}
222
223
1.69k
ValueObjectSP AbstractListFrontEnd::GetItem(size_t idx) {
224
1.69k
  size_t advance = idx;
225
1.69k
  ListIterator current(m_head);
226
1.69k
  if (idx > 0) {
227
1.64k
    auto cached_iterator = m_iterators.find(idx - 1);
228
1.64k
    if (cached_iterator != m_iterators.end()) {
229
1.64k
      current = cached_iterator->second;
230
1.64k
      advance = 1;
231
1.64k
    }
232
1.64k
  }
233
1.69k
  ValueObjectSP value_sp = current.advance(advance);
234
1.69k
  m_iterators[idx] = current;
235
1.69k
  return value_sp;
236
1.69k
}
237
238
ForwardListFrontEnd::ForwardListFrontEnd(ValueObject &valobj)
239
28
    : AbstractListFrontEnd(valobj) {
240
28
  Update();
241
28
}
242
243
1.60k
size_t ForwardListFrontEnd::CalculateNumChildren() {
244
1.60k
  if (m_count != UINT32_MAX)
245
1.58k
    return m_count;
246
247
28
  ListEntry current(m_head);
248
28
  m_count = 0;
249
1.60k
  while (current && 
m_count < m_list_capping_size1.58k
) {
250
1.57k
    ++m_count;
251
1.57k
    current = current.next();
252
1.57k
  }
253
28
  return m_count;
254
1.60k
}
255
256
1.58k
ValueObjectSP ForwardListFrontEnd::GetChildAtIndex(size_t idx) {
257
1.58k
  if (idx >= CalculateNumChildren())
258
0
    return nullptr;
259
260
1.58k
  if (!m_head)
261
0
    return nullptr;
262
263
1.58k
  if (HasLoop(idx + 1))
264
0
    return nullptr;
265
266
1.58k
  ValueObjectSP current_sp = GetItem(idx);
267
1.58k
  if (!current_sp)
268
0
    return nullptr;
269
270
1.58k
  current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child
271
1.58k
  if (!current_sp)
272
8
    return nullptr;
273
274
  // we need to copy current_sp into a new object otherwise we will end up with
275
  // all items named __value_
276
1.57k
  DataExtractor data;
277
1.57k
  Status error;
278
1.57k
  current_sp->GetData(data, error);
279
1.57k
  if (error.Fail())
280
0
    return nullptr;
281
282
1.57k
  return CreateValueObjectFromData(llvm::formatv("[{0}]", idx).str(), data,
283
1.57k
                                   m_backend.GetExecutionContextRef(),
284
1.57k
                                   m_element_type);
285
1.57k
}
286
287
56
bool ForwardListFrontEnd::Update() {
288
56
  AbstractListFrontEnd::Update();
289
290
56
  Status err;
291
56
  ValueObjectSP backend_addr(m_backend.AddressOf(err));
292
56
  if (err.Fail() || !backend_addr)
293
0
    return false;
294
295
56
  ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__before_begin_"));
296
56
  if (!impl_sp)
297
0
    return false;
298
56
  impl_sp = GetFirstValueOfLibCXXCompressedPair(*impl_sp);
299
56
  if (!impl_sp)
300
0
    return false;
301
56
  m_head = impl_sp->GetChildMemberWithName("__next_").get();
302
56
  return false;
303
56
}
304
305
ListFrontEnd::ListFrontEnd(lldb::ValueObjectSP valobj_sp)
306
36
    : AbstractListFrontEnd(*valobj_sp) {
307
36
  if (valobj_sp)
308
36
    Update();
309
36
}
310
311
154
size_t ListFrontEnd::CalculateNumChildren() {
312
154
  if (m_count != UINT32_MAX)
313
118
    return m_count;
314
36
  if (!m_head || !m_tail || m_node_address == 0)
315
0
    return 0;
316
36
  ValueObjectSP size_alloc(m_backend.GetChildMemberWithName("__size_alloc_"));
317
36
  if (size_alloc) {
318
36
    ValueObjectSP value = GetFirstValueOfLibCXXCompressedPair(*size_alloc);
319
36
    if (value) {
320
36
      m_count = value->GetValueAsUnsigned(UINT32_MAX);
321
36
    }
322
36
  }
323
36
  if (m_count != UINT32_MAX) {
324
36
    return m_count;
325
36
  } else {
326
0
    uint64_t next_val = m_head->GetValueAsUnsigned(0);
327
0
    uint64_t prev_val = m_tail->GetValueAsUnsigned(0);
328
0
    if (next_val == 0 || prev_val == 0)
329
0
      return 0;
330
0
    if (next_val == m_node_address)
331
0
      return 0;
332
0
    if (next_val == prev_val)
333
0
      return 1;
334
0
    uint64_t size = 2;
335
0
    ListEntry current(m_head);
336
0
    while (current.next() && current.next().value() != m_node_address) {
337
0
      size++;
338
0
      current = current.next();
339
0
      if (size > m_list_capping_size)
340
0
        break;
341
0
    }
342
0
    return m_count = (size - 1);
343
0
  }
344
36
}
345
346
118
lldb::ValueObjectSP ListFrontEnd::GetChildAtIndex(size_t idx) {
347
118
  static ConstString g_value("__value_");
348
118
  static ConstString g_next("__next_");
349
350
118
  if (idx >= CalculateNumChildren())
351
0
    return lldb::ValueObjectSP();
352
353
118
  if (!m_head || !m_tail || m_node_address == 0)
354
0
    return lldb::ValueObjectSP();
355
356
118
  if (HasLoop(idx + 1))
357
0
    return lldb::ValueObjectSP();
358
359
118
  ValueObjectSP current_sp = GetItem(idx);
360
118
  if (!current_sp)
361
0
    return lldb::ValueObjectSP();
362
363
118
  current_sp = current_sp->GetChildAtIndex(1); // get the __value_ child
364
118
  if (!current_sp)
365
0
    return lldb::ValueObjectSP();
366
367
118
  if (current_sp->GetName() == g_next) {
368
118
    ProcessSP process_sp(current_sp->GetProcessSP());
369
118
    if (!process_sp)
370
0
      return lldb::ValueObjectSP();
371
372
    // if we grabbed the __next_ pointer, then the child is one pointer deep-er
373
118
    lldb::addr_t addr = current_sp->GetParent()->GetPointerValue();
374
118
    addr = addr + 2 * process_sp->GetAddressByteSize();
375
118
    ExecutionContext exe_ctx(process_sp);
376
118
    current_sp =
377
118
        CreateValueObjectFromAddress("__value_", addr, exe_ctx, m_element_type);
378
118
    if (!current_sp)
379
0
      return lldb::ValueObjectSP();
380
118
  }
381
382
  // we need to copy current_sp into a new object otherwise we will end up with
383
  // all items named __value_
384
118
  DataExtractor data;
385
118
  Status error;
386
118
  current_sp->GetData(data, error);
387
118
  if (error.Fail())
388
0
    return lldb::ValueObjectSP();
389
390
118
  StreamString name;
391
118
  name.Printf("[%" PRIu64 "]", (uint64_t)idx);
392
118
  return CreateValueObjectFromData(name.GetString(), data,
393
118
                                   m_backend.GetExecutionContextRef(),
394
118
                                   m_element_type);
395
118
}
396
397
72
bool ListFrontEnd::Update() {
398
72
  AbstractListFrontEnd::Update();
399
72
  m_tail = nullptr;
400
72
  m_node_address = 0;
401
402
72
  Status err;
403
72
  ValueObjectSP backend_addr(m_backend.AddressOf(err));
404
72
  if (err.Fail() || !backend_addr)
405
0
    return false;
406
72
  m_node_address = backend_addr->GetValueAsUnsigned(0);
407
72
  if (!m_node_address || m_node_address == LLDB_INVALID_ADDRESS)
408
0
    return false;
409
72
  ValueObjectSP impl_sp(m_backend.GetChildMemberWithName("__end_"));
410
72
  if (!impl_sp)
411
0
    return false;
412
72
  m_head = impl_sp->GetChildMemberWithName("__next_").get();
413
72
  m_tail = impl_sp->GetChildMemberWithName("__prev_").get();
414
72
  return false;
415
72
}
416
417
SyntheticChildrenFrontEnd *formatters::LibcxxStdListSyntheticFrontEndCreator(
418
36
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
419
36
  return (valobj_sp ? new ListFrontEnd(valobj_sp) : 
nullptr0
);
420
36
}
421
422
SyntheticChildrenFrontEnd *
423
formatters::LibcxxStdForwardListSyntheticFrontEndCreator(
424
28
    CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
425
28
  return valobj_sp ? new ForwardListFrontEnd(*valobj_sp) : 
nullptr0
;
426
28
}