Coverage Report

Created: 2023-11-11 10:31

/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Symbol/UnwindPlan.cpp
Line
Count
Source (jump to first uncovered line)
1
//===-- UnwindPlan.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/Symbol/UnwindPlan.h"
10
11
#include "lldb/Target/Process.h"
12
#include "lldb/Target/RegisterContext.h"
13
#include "lldb/Target/Target.h"
14
#include "lldb/Target/Thread.h"
15
#include "lldb/Utility/ConstString.h"
16
#include "lldb/Utility/LLDBLog.h"
17
#include "lldb/Utility/Log.h"
18
#include "llvm/DebugInfo/DIContext.h"
19
#include "llvm/DebugInfo/DWARF/DWARFExpression.h"
20
#include <optional>
21
22
using namespace lldb;
23
using namespace lldb_private;
24
25
bool UnwindPlan::Row::RegisterLocation::
26
167
operator==(const UnwindPlan::Row::RegisterLocation &rhs) const {
27
167
  if (m_type == rhs.m_type) {
28
163
    switch (m_type) {
29
6
    case unspecified:
30
6
    case undefined:
31
6
    case same:
32
6
      return true;
33
34
137
    case atCFAPlusOffset:
35
157
    case isCFAPlusOffset:
36
157
    case atAFAPlusOffset:
37
157
    case isAFAPlusOffset:
38
157
      return m_location.offset == rhs.m_location.offset;
39
40
0
    case inOtherRegister:
41
0
      return m_location.reg_num == rhs.m_location.reg_num;
42
43
0
    case atDWARFExpression:
44
0
    case isDWARFExpression:
45
0
      if (m_location.expr.length == rhs.m_location.expr.length)
46
0
        return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
47
0
                       m_location.expr.length);
48
0
      break;
49
163
    }
50
163
  }
51
4
  return false;
52
167
}
53
54
// This function doesn't copy the dwarf expression bytes; they must remain in
55
// allocated memory for the lifespan of this UnwindPlan object.
56
void UnwindPlan::Row::RegisterLocation::SetAtDWARFExpression(
57
48
    const uint8_t *opcodes, uint32_t len) {
58
48
  m_type = atDWARFExpression;
59
48
  m_location.expr.opcodes = opcodes;
60
48
  m_location.expr.length = len;
61
48
}
62
63
// This function doesn't copy the dwarf expression bytes; they must remain in
64
// allocated memory for the lifespan of this UnwindPlan object.
65
void UnwindPlan::Row::RegisterLocation::SetIsDWARFExpression(
66
47
    const uint8_t *opcodes, uint32_t len) {
67
47
  m_type = isDWARFExpression;
68
47
  m_location.expr.opcodes = opcodes;
69
47
  m_location.expr.length = len;
70
47
}
71
72
static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
73
29
GetByteOrderAndAddrSize(Thread *thread) {
74
29
  if (!thread)
75
0
    return std::nullopt;
76
29
  ProcessSP process_sp = thread->GetProcess();
77
29
  if (!process_sp)
78
0
    return std::nullopt;
79
29
  ArchSpec arch = process_sp->GetTarget().GetArchitecture();
80
29
  return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
81
29
}
82
83
29
static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
84
29
  if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
85
29
    llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle,
86
29
                             order_and_width->second);
87
29
    llvm::DWARFExpression(data, order_and_width->second, llvm::dwarf::DWARF32)
88
29
        .print(s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
89
29
  } else
90
0
    s.PutCString("dwarf-expr");
91
29
}
92
93
void UnwindPlan::Row::RegisterLocation::Dump(Stream &s,
94
                                             const UnwindPlan *unwind_plan,
95
                                             const UnwindPlan::Row *row,
96
                                             Thread *thread,
97
165
                                             bool verbose) const {
98
165
  switch (m_type) {
99
0
  case unspecified:
100
0
    if (verbose)
101
0
      s.PutCString("=<unspec>");
102
0
    else
103
0
      s.PutCString("=!");
104
0
    break;
105
0
  case undefined:
106
0
    if (verbose)
107
0
      s.PutCString("=<undef>");
108
0
    else
109
0
      s.PutCString("=?");
110
0
    break;
111
2
  case same:
112
2
    s.PutCString("= <same>");
113
2
    break;
114
115
89
  case atCFAPlusOffset:
116
140
  case isCFAPlusOffset: {
117
140
    s.PutChar('=');
118
140
    if (m_type == atCFAPlusOffset)
119
89
      s.PutChar('[');
120
140
    s.Printf("CFA%+d", m_location.offset);
121
140
    if (m_type == atCFAPlusOffset)
122
89
      s.PutChar(']');
123
140
  } break;
124
125
0
  case atAFAPlusOffset:
126
0
  case isAFAPlusOffset: {
127
0
    s.PutChar('=');
128
0
    if (m_type == atAFAPlusOffset)
129
0
      s.PutChar('[');
130
0
    s.Printf("AFA%+d", m_location.offset);
131
0
    if (m_type == atAFAPlusOffset)
132
0
      s.PutChar(']');
133
0
  } break;
134
135
2
  case inOtherRegister: {
136
2
    const RegisterInfo *other_reg_info = nullptr;
137
2
    if (unwind_plan)
138
2
      other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
139
2
    if (other_reg_info)
140
2
      s.Printf("=%s", other_reg_info->name);
141
0
    else
142
0
      s.Printf("=reg(%u)", m_location.reg_num);
143
2
  } break;
144
145
0
  case atDWARFExpression:
146
21
  case isDWARFExpression: {
147
21
    s.PutChar('=');
148
21
    if (m_type == atDWARFExpression)
149
0
      s.PutChar('[');
150
21
    DumpDWARFExpr(
151
21
        s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length),
152
21
        thread);
153
21
    if (m_type == atDWARFExpression)
154
0
      s.PutChar(']');
155
21
  } break;
156
165
  }
157
165
}
158
159
static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
160
230
                             Thread *thread, uint32_t reg_num) {
161
230
  const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
162
230
  if (reg_info)
163
230
    s.PutCString(reg_info->name);
164
0
  else
165
0
    s.Printf("reg(%u)", reg_num);
166
230
}
167
168
bool UnwindPlan::Row::FAValue::
169
176
operator==(const UnwindPlan::Row::FAValue &rhs) const {
170
176
  if (m_type == rhs.m_type) {
171
176
    switch (m_type) {
172
29
    case unspecified:
173
29
    case isRaSearch:
174
29
      return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
175
176
147
    case isRegisterPlusOffset:
177
147
      return m_value.reg.offset == rhs.m_value.reg.offset;
178
179
0
    case isRegisterDereferenced:
180
0
      return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
181
182
0
    case isDWARFExpression:
183
0
      if (m_value.expr.length == rhs.m_value.expr.length)
184
0
        return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
185
0
                       m_value.expr.length);
186
0
      break;
187
176
    }
188
176
  }
189
0
  return false;
190
176
}
191
192
void UnwindPlan::Row::FAValue::Dump(Stream &s, const UnwindPlan *unwind_plan,
193
75
                                     Thread *thread) const {
194
75
  switch (m_type) {
195
65
  case isRegisterPlusOffset:
196
65
    DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
197
65
    s.Printf("%+3d", m_value.reg.offset);
198
65
    break;
199
0
  case isRegisterDereferenced:
200
0
    s.PutChar('[');
201
0
    DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
202
0
    s.PutChar(']');
203
0
    break;
204
8
  case isDWARFExpression:
205
8
    DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),
206
8
                  thread);
207
8
    break;
208
0
  case unspecified:
209
0
    s.PutCString("unspecified");
210
0
    break;
211
2
  case isRaSearch:
212
2
    s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
213
2
    break;
214
75
  }
215
75
}
216
217
0
void UnwindPlan::Row::Clear() {
218
0
  m_cfa_value.SetUnspecified();
219
0
  m_afa_value.SetUnspecified();
220
0
  m_offset = 0;
221
0
  m_unspecified_registers_are_undefined = false;
222
0
  m_register_locations.clear();
223
0
}
224
225
void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
226
75
                           Thread *thread, addr_t base_addr) const {
227
75
  if (base_addr != LLDB_INVALID_ADDRESS)
228
0
    s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
229
75
  else
230
75
    s.Printf("%4" PRId64 ": CFA=", GetOffset());
231
232
75
  m_cfa_value.Dump(s, unwind_plan, thread);
233
234
75
  if (!m_afa_value.IsUnspecified()) {
235
0
    s.Printf(" AFA=");
236
0
    m_afa_value.Dump(s, unwind_plan, thread);
237
0
  }
238
239
75
  s.Printf(" => ");
240
75
  for (collection::const_iterator idx = m_register_locations.begin();
241
240
       idx != m_register_locations.end(); 
++idx165
) {
242
165
    DumpRegisterName(s, unwind_plan, thread, idx->first);
243
165
    const bool verbose = false;
244
165
    idx->second.Dump(s, unwind_plan, this, thread, verbose);
245
165
    s.PutChar(' ');
246
165
  }
247
75
}
248
249
228k
UnwindPlan::Row::Row() : m_cfa_value(), m_afa_value(), m_register_locations() {}
250
251
bool UnwindPlan::Row::GetRegisterInfo(
252
    uint32_t reg_num,
253
471k
    UnwindPlan::Row::RegisterLocation &register_location) const {
254
471k
  collection::const_iterator pos = m_register_locations.find(reg_num);
255
471k
  if (pos != m_register_locations.end()) {
256
462k
    register_location = pos->second;
257
462k
    return true;
258
462k
  }
259
9.37k
  if (m_unspecified_registers_are_undefined) {
260
17
    register_location.SetUndefined();
261
17
    return true;
262
17
  }
263
9.35k
  return false;
264
9.37k
}
265
266
12.5k
void UnwindPlan::Row::RemoveRegisterInfo(uint32_t reg_num) {
267
12.5k
  collection::const_iterator pos = m_register_locations.find(reg_num);
268
12.5k
  if (pos != m_register_locations.end()) {
269
12.5k
    m_register_locations.erase(pos);
270
12.5k
  }
271
12.5k
}
272
273
void UnwindPlan::Row::SetRegisterInfo(
274
    uint32_t reg_num,
275
78.3k
    const UnwindPlan::Row::RegisterLocation register_location) {
276
78.3k
  m_register_locations[reg_num] = register_location;
277
78.3k
}
278
279
bool UnwindPlan::Row::SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num,
280
                                                           int32_t offset,
281
140k
                                                           bool can_replace) {
282
140k
  if (!can_replace &&
283
140k
      
m_register_locations.find(reg_num) != m_register_locations.end()9.07k
)
284
0
    return false;
285
140k
  RegisterLocation reg_loc;
286
140k
  reg_loc.SetAtCFAPlusOffset(offset);
287
140k
  m_register_locations[reg_num] = reg_loc;
288
140k
  return true;
289
140k
}
290
291
bool UnwindPlan::Row::SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num,
292
                                                           int32_t offset,
293
70.4k
                                                           bool can_replace) {
294
70.4k
  if (!can_replace &&
295
70.4k
      
m_register_locations.find(reg_num) != m_register_locations.end()20
)
296
0
    return false;
297
70.4k
  RegisterLocation reg_loc;
298
70.4k
  reg_loc.SetIsCFAPlusOffset(offset);
299
70.4k
  m_register_locations[reg_num] = reg_loc;
300
70.4k
  return true;
301
70.4k
}
302
303
bool UnwindPlan::Row::SetRegisterLocationToUndefined(
304
0
    uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
305
0
  collection::iterator pos = m_register_locations.find(reg_num);
306
0
  collection::iterator end = m_register_locations.end();
307
308
0
  if (pos != end) {
309
0
    if (!can_replace)
310
0
      return false;
311
0
    if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
312
0
      return false;
313
0
  }
314
0
  RegisterLocation reg_loc;
315
0
  reg_loc.SetUndefined();
316
0
  m_register_locations[reg_num] = reg_loc;
317
0
  return true;
318
0
}
319
320
bool UnwindPlan::Row::SetRegisterLocationToUnspecified(uint32_t reg_num,
321
0
                                                       bool can_replace) {
322
0
  if (!can_replace &&
323
0
      m_register_locations.find(reg_num) != m_register_locations.end())
324
0
    return false;
325
0
  RegisterLocation reg_loc;
326
0
  reg_loc.SetUnspecified();
327
0
  m_register_locations[reg_num] = reg_loc;
328
0
  return true;
329
0
}
330
331
bool UnwindPlan::Row::SetRegisterLocationToRegister(uint32_t reg_num,
332
                                                    uint32_t other_reg_num,
333
13
                                                    bool can_replace) {
334
13
  if (!can_replace &&
335
13
      
m_register_locations.find(reg_num) != m_register_locations.end()0
)
336
0
    return false;
337
13
  RegisterLocation reg_loc;
338
13
  reg_loc.SetInRegister(other_reg_num);
339
13
  m_register_locations[reg_num] = reg_loc;
340
13
  return true;
341
13
}
342
343
bool UnwindPlan::Row::SetRegisterLocationToSame(uint32_t reg_num,
344
47
                                                bool must_replace) {
345
47
  if (must_replace &&
346
47
      
m_register_locations.find(reg_num) == m_register_locations.end()0
)
347
0
    return false;
348
47
  RegisterLocation reg_loc;
349
47
  reg_loc.SetSame();
350
47
  m_register_locations[reg_num] = reg_loc;
351
47
  return true;
352
47
}
353
354
29
bool UnwindPlan::Row::operator==(const UnwindPlan::Row &rhs) const {
355
29
  return m_offset == rhs.m_offset && m_cfa_value == rhs.m_cfa_value &&
356
29
         m_afa_value == rhs.m_afa_value &&
357
29
         m_unspecified_registers_are_undefined ==
358
29
             rhs.m_unspecified_registers_are_undefined &&
359
29
         m_register_locations == rhs.m_register_locations;
360
29
}
361
362
129k
void UnwindPlan::AppendRow(const UnwindPlan::RowSP &row_sp) {
363
129k
  if (m_row_list.empty() ||
364
129k
      
m_row_list.back()->GetOffset() != row_sp->GetOffset()50.3k
)
365
129k
    m_row_list.push_back(row_sp);
366
0
  else
367
0
    m_row_list.back() = row_sp;
368
129k
}
369
370
void UnwindPlan::InsertRow(const UnwindPlan::RowSP &row_sp,
371
84
                           bool replace_existing) {
372
84
  collection::iterator it = m_row_list.begin();
373
482
  while (it != m_row_list.end()) {
374
401
    RowSP row = *it;
375
401
    if (row->GetOffset() >= row_sp->GetOffset())
376
3
      break;
377
398
    it++;
378
398
  }
379
84
  if (it == m_row_list.end() || 
(*it)->GetOffset() != row_sp->GetOffset()3
)
380
84
    m_row_list.insert(it, row_sp);
381
0
  else if (replace_existing)
382
0
    *it = row_sp;
383
84
}
384
385
686k
UnwindPlan::RowSP UnwindPlan::GetRowForFunctionOffset(int offset) const {
386
686k
  RowSP row;
387
686k
  if (!m_row_list.empty()) {
388
686k
    if (offset == -1)
389
6.88k
      row = m_row_list.back();
390
679k
    else {
391
679k
      collection::const_iterator pos, end = m_row_list.end();
392
1.45M
      for (pos = m_row_list.begin(); pos != end; 
++pos774k
) {
393
832k
        if ((*pos)->GetOffset() <= static_cast<lldb::offset_t>(offset))
394
774k
          row = *pos;
395
57.9k
        else
396
57.9k
          break;
397
832k
      }
398
679k
    }
399
686k
  }
400
686k
  return row;
401
686k
}
402
403
18
bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
404
18
  return idx < m_row_list.size();
405
18
}
406
407
939k
const UnwindPlan::RowSP UnwindPlan::GetRowAtIndex(uint32_t idx) const {
408
939k
  if (idx < m_row_list.size())
409
939k
    return m_row_list[idx];
410
0
  else {
411
0
    Log *log = GetLog(LLDBLog::Unwind);
412
0
    LLDB_LOGF(log,
413
0
              "error: UnwindPlan::GetRowAtIndex(idx = %u) invalid index "
414
0
              "(number rows is %u)",
415
0
              idx, (uint32_t)m_row_list.size());
416
0
    return UnwindPlan::RowSP();
417
0
  }
418
939k
}
419
420
14
const UnwindPlan::RowSP UnwindPlan::GetLastRow() const {
421
14
  if (m_row_list.empty()) {
422
0
    Log *log = GetLog(LLDBLog::Unwind);
423
0
    LLDB_LOGF(log, "UnwindPlan::GetLastRow() when rows are empty");
424
0
    return UnwindPlan::RowSP();
425
0
  }
426
14
  return m_row_list.back();
427
14
}
428
429
469k
int UnwindPlan::GetRowCount() const { return m_row_list.size(); }
430
431
13.7k
void UnwindPlan::SetPlanValidAddressRange(const AddressRange &range) {
432
13.7k
  if (range.GetBaseAddress().IsValid() && range.GetByteSize() != 0)
433
13.7k
    m_plan_valid_address_range = range;
434
13.7k
}
435
436
469k
bool UnwindPlan::PlanValidAtAddress(Address addr) {
437
  // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
438
469k
  if (GetRowCount() == 0) {
439
0
    Log *log = GetLog(LLDBLog::Unwind);
440
0
    if (log) {
441
0
      StreamString s;
442
0
      if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
443
0
        LLDB_LOGF(log,
444
0
                  "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
445
0
                  "'%s' at address %s",
446
0
                  m_source_name.GetCString(), s.GetData());
447
0
      } else {
448
0
        LLDB_LOGF(log,
449
0
                  "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
450
0
                  m_source_name.GetCString());
451
0
      }
452
0
    }
453
0
    return false;
454
0
  }
455
456
  // If the 0th Row of unwind instructions is missing, or if it doesn't provide
457
  // a register to use to find the Canonical Frame Address, this is not a valid
458
  // UnwindPlan.
459
469k
  if (GetRowAtIndex(0).get() == nullptr ||
460
469k
      GetRowAtIndex(0)->GetCFAValue().GetValueType() ==
461
469k
          Row::FAValue::unspecified) {
462
0
    Log *log = GetLog(LLDBLog::Unwind);
463
0
    if (log) {
464
0
      StreamString s;
465
0
      if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
466
0
        LLDB_LOGF(log,
467
0
                  "UnwindPlan is invalid -- no CFA register defined in row 0 "
468
0
                  "for UnwindPlan '%s' at address %s",
469
0
                  m_source_name.GetCString(), s.GetData());
470
0
      } else {
471
0
        LLDB_LOGF(log,
472
0
                  "UnwindPlan is invalid -- no CFA register defined in row 0 "
473
0
                  "for UnwindPlan '%s'",
474
0
                  m_source_name.GetCString());
475
0
      }
476
0
    }
477
0
    return false;
478
0
  }
479
480
469k
  if (!m_plan_valid_address_range.GetBaseAddress().IsValid() ||
481
469k
      
m_plan_valid_address_range.GetByteSize() == 075.0k
)
482
394k
    return true;
483
484
75.0k
  if (!addr.IsValid())
485
0
    return true;
486
487
75.0k
  if (m_plan_valid_address_range.ContainsFileAddress(addr))
488
75.0k
    return true;
489
490
0
  return false;
491
75.0k
}
492
493
66
void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
494
66
  if (!m_source_name.IsEmpty()) {
495
66
    s.Printf("This UnwindPlan originally sourced from %s\n",
496
66
             m_source_name.GetCString());
497
66
  }
498
66
  if (m_lsda_address.IsValid() && 
m_personality_func_addr.IsValid()0
) {
499
0
    TargetSP target_sp(thread->CalculateTarget());
500
0
    addr_t lsda_load_addr = m_lsda_address.GetLoadAddress(target_sp.get());
501
0
    addr_t personality_func_load_addr =
502
0
        m_personality_func_addr.GetLoadAddress(target_sp.get());
503
504
0
    if (lsda_load_addr != LLDB_INVALID_ADDRESS &&
505
0
        personality_func_load_addr != LLDB_INVALID_ADDRESS) {
506
0
      s.Printf("LSDA address 0x%" PRIx64
507
0
               ", personality routine is at address 0x%" PRIx64 "\n",
508
0
               lsda_load_addr, personality_func_load_addr);
509
0
    }
510
0
  }
511
66
  s.Printf("This UnwindPlan is sourced from the compiler: ");
512
66
  switch (m_plan_is_sourced_from_compiler) {
513
12
  case eLazyBoolYes:
514
12
    s.Printf("yes.\n");
515
12
    break;
516
54
  case eLazyBoolNo:
517
54
    s.Printf("no.\n");
518
54
    break;
519
0
  case eLazyBoolCalculate:
520
0
    s.Printf("not specified.\n");
521
0
    break;
522
66
  }
523
66
  s.Printf("This UnwindPlan is valid at all instruction locations: ");
524
66
  switch (m_plan_is_valid_at_all_instruction_locations) {
525
5
  case eLazyBoolYes:
526
5
    s.Printf("yes.\n");
527
5
    break;
528
38
  case eLazyBoolNo:
529
38
    s.Printf("no.\n");
530
38
    break;
531
23
  case eLazyBoolCalculate:
532
23
    s.Printf("not specified.\n");
533
23
    break;
534
66
  }
535
66
  s.Printf("This UnwindPlan is for a trap handler function: ");
536
66
  switch (m_plan_is_for_signal_trap) {
537
0
  case eLazyBoolYes:
538
0
    s.Printf("yes.\n");
539
0
    break;
540
43
  case eLazyBoolNo:
541
43
    s.Printf("no.\n");
542
43
    break;
543
23
  case eLazyBoolCalculate:
544
23
    s.Printf("not specified.\n");
545
23
    break;
546
66
  }
547
66
  if (m_plan_valid_address_range.GetBaseAddress().IsValid() &&
548
66
      
m_plan_valid_address_range.GetByteSize() > 015
) {
549
15
    s.PutCString("Address range of this UnwindPlan: ");
550
15
    TargetSP target_sp(thread->CalculateTarget());
551
15
    m_plan_valid_address_range.Dump(&s, target_sp.get(),
552
15
                                    Address::DumpStyleSectionNameOffset);
553
15
    s.EOL();
554
15
  }
555
66
  collection::const_iterator pos, begin = m_row_list.begin(),
556
66
                                  end = m_row_list.end();
557
141
  for (pos = begin; pos != end; 
++pos75
) {
558
75
    s.Printf("row[%u]: ", (uint32_t)std::distance(begin, pos));
559
75
    (*pos)->Dump(s, this, thread, base_addr);
560
75
    s.Printf("\n");
561
75
  }
562
66
}
563
564
79.6k
void UnwindPlan::SetSourceName(const char *source) {
565
79.6k
  m_source_name = ConstString(source);
566
79.6k
}
567
568
164k
ConstString UnwindPlan::GetSourceName() const { return m_source_name; }
569
570
const RegisterInfo *UnwindPlan::GetRegisterInfo(Thread *thread,
571
232
                                                uint32_t unwind_reg) const {
572
232
  if (thread) {
573
232
    RegisterContext *reg_ctx = thread->GetRegisterContext().get();
574
232
    if (reg_ctx) {
575
232
      uint32_t reg;
576
232
      if (m_register_kind == eRegisterKindLLDB)
577
44
        reg = unwind_reg;
578
188
      else
579
188
        reg = reg_ctx->ConvertRegisterKindToRegisterNumber(m_register_kind,
580
188
                                                           unwind_reg);
581
232
      if (reg != LLDB_INVALID_REGNUM)
582
232
        return reg_ctx->GetRegisterInfoAtIndex(reg);
583
232
    }
584
232
  }
585
0
  return nullptr;
586
232
}