LLDB mainline
UnwindPlan.cpp
Go to the documentation of this file.
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
10
11#include "lldb/Target/Process.h"
13#include "lldb/Target/Target.h"
14#include "lldb/Target/Thread.h"
17#include "lldb/Utility/Log.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/DebugInfo/DIContext.h"
20#include "llvm/DebugInfo/DWARF/DWARFExpressionPrinter.h"
21#include "llvm/DebugInfo/DWARF/LowLevel/DWARFExpression.h"
22#include <optional>
23
24using namespace lldb;
25using namespace lldb_private;
26
29 if (m_type == rhs.m_type) {
30 switch (m_type) {
31 case unspecified:
32 case undefined:
33 case same:
34 return true;
35
36 case atCFAPlusOffset:
37 case isCFAPlusOffset:
38 case atAFAPlusOffset:
39 case isAFAPlusOffset:
40 return m_location.offset == rhs.m_location.offset;
41
42 case inOtherRegister:
43 return m_location.reg_num == rhs.m_location.reg_num;
44
47 if (m_location.expr.length == rhs.m_location.expr.length)
48 return !memcmp(m_location.expr.opcodes, rhs.m_location.expr.opcodes,
49 m_location.expr.length);
50 break;
51 case isConstant:
52 return m_location.constant_value == rhs.m_location.constant_value;
53 }
54 }
55 return false;
56}
57
58// This function doesn't copy the dwarf expression bytes; they must remain in
59// allocated memory for the lifespan of this UnwindPlan object.
61 const uint8_t *opcodes, uint32_t len) {
63 m_location.expr.opcodes = opcodes;
64 m_location.expr.length = len;
65}
66
67// This function doesn't copy the dwarf expression bytes; they must remain in
68// allocated memory for the lifespan of this UnwindPlan object.
70 const uint8_t *opcodes, uint32_t len) {
72 m_location.expr.opcodes = opcodes;
73 m_location.expr.length = len;
74}
75
76static std::optional<std::pair<lldb::ByteOrder, uint32_t>>
78 if (!thread)
79 return std::nullopt;
80 ProcessSP process_sp = thread->GetProcess();
81 if (!process_sp)
82 return std::nullopt;
83 ArchSpec arch = process_sp->GetTarget().GetArchitecture();
84 return std::make_pair(arch.GetByteOrder(), arch.GetAddressByteSize());
85}
86
87static void DumpDWARFExpr(Stream &s, llvm::ArrayRef<uint8_t> expr, Thread *thread) {
88 if (auto order_and_width = GetByteOrderAndAddrSize(thread)) {
89 llvm::DataExtractor data(expr, order_and_width->first == eByteOrderLittle);
90 llvm::DWARFExpression E(data, order_and_width->second,
91 llvm::dwarf::DWARF32);
92 printDwarfExpression(&E, s.AsRawOstream(), llvm::DIDumpOptions(), nullptr);
93 } else
94 s.PutCString("dwarf-expr");
95}
96
98 Stream &s, const UnwindPlan *unwind_plan, const UnwindPlan::Row *row,
99 Thread *thread, bool verbose) const {
100 switch (m_type) {
101 case unspecified:
102 if (verbose)
103 s.PutCString("=<unspec>");
104 else
105 s.PutCString("=!");
106 break;
107 case undefined:
108 if (verbose)
109 s.PutCString("=<undef>");
110 else
111 s.PutCString("=?");
112 break;
113 case same:
114 s.PutCString("= <same>");
115 break;
116
117 case atCFAPlusOffset:
118 case isCFAPlusOffset: {
119 s.PutChar('=');
120 if (m_type == atCFAPlusOffset)
121 s.PutChar('[');
122 s.Printf("CFA%+d", m_location.offset);
123 if (m_type == atCFAPlusOffset)
124 s.PutChar(']');
125 } break;
126
127 case atAFAPlusOffset:
128 case isAFAPlusOffset: {
129 s.PutChar('=');
130 if (m_type == atAFAPlusOffset)
131 s.PutChar('[');
132 s.Printf("AFA%+d", m_location.offset);
133 if (m_type == atAFAPlusOffset)
134 s.PutChar(']');
135 } break;
136
137 case inOtherRegister: {
138 const RegisterInfo *other_reg_info = nullptr;
139 if (unwind_plan)
140 other_reg_info = unwind_plan->GetRegisterInfo(thread, m_location.reg_num);
141 if (other_reg_info)
142 s.Printf("=%s", other_reg_info->name);
143 else
144 s.Printf("=reg(%u)", m_location.reg_num);
145 } break;
146
148 case isDWARFExpression: {
149 s.PutChar('=');
151 s.PutChar('[');
153 s, llvm::ArrayRef(m_location.expr.opcodes, m_location.expr.length),
154 thread);
156 s.PutChar(']');
157 } break;
158 case isConstant:
159 s.Printf("=0x%" PRIx64, m_location.constant_value);
160 break;
161 }
162}
163
164static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan,
165 Thread *thread, uint32_t reg_num) {
166 const RegisterInfo *reg_info = unwind_plan->GetRegisterInfo(thread, reg_num);
167 if (reg_info)
168 s.PutCString(reg_info->name);
169 else
170 s.Printf("reg(%u)", reg_num);
171}
172
174operator==(const UnwindPlan::Row::FAValue &rhs) const {
175 if (m_type == rhs.m_type) {
176 switch (m_type) {
177 case unspecified:
178 case isRaSearch:
179 return m_value.ra_search_offset == rhs.m_value.ra_search_offset;
180
182 return m_value.reg.offset == rhs.m_value.reg.offset;
183
185 return m_value.reg.reg_num == rhs.m_value.reg.reg_num;
186
188 if (m_value.expr.length == rhs.m_value.expr.length)
189 return !memcmp(m_value.expr.opcodes, rhs.m_value.expr.opcodes,
190 m_value.expr.length);
191 break;
192 case isConstant:
193 return m_value.constant == rhs.m_value.constant;
194 }
195 }
196 return false;
197}
198
200 Thread *thread) const {
201 switch (m_type) {
203 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
204 s.Printf("%+3d", m_value.reg.offset);
205 break;
207 s.PutChar('[');
208 DumpRegisterName(s, unwind_plan, thread, m_value.reg.reg_num);
209 s.PutChar(']');
210 break;
212 DumpDWARFExpr(s, llvm::ArrayRef(m_value.expr.opcodes, m_value.expr.length),
213 thread);
214 break;
215 case unspecified:
216 s.PutCString("unspecified");
217 break;
218 case isRaSearch:
219 s.Printf("RaSearch@SP%+d", m_value.ra_search_offset);
220 break;
221 case isConstant:
222 s.Printf("0x%" PRIx64, m_value.constant);
223 }
224}
225
227 m_cfa_value.SetUnspecified();
228 m_afa_value.SetUnspecified();
229 m_offset = 0;
231 m_register_locations.clear();
232}
233
234void UnwindPlan::Row::Dump(Stream &s, const UnwindPlan *unwind_plan,
235 Thread *thread, addr_t base_addr) const {
236 if (base_addr != LLDB_INVALID_ADDRESS)
237 s.Printf("0x%16.16" PRIx64 ": CFA=", base_addr + GetOffset());
238 else
239 s.Printf("%4" PRId64 ": CFA=", GetOffset());
240
241 m_cfa_value.Dump(s, unwind_plan, thread);
242
243 if (!m_afa_value.IsUnspecified()) {
244 s.Printf(" AFA=");
245 m_afa_value.Dump(s, unwind_plan, thread);
246 }
247
248 s.Printf(" => ");
249 for (collection::const_iterator idx = m_register_locations.begin();
250 idx != m_register_locations.end(); ++idx) {
251 DumpRegisterName(s, unwind_plan, thread, idx->first);
252 const bool verbose = false;
253 idx->second.Dump(s, unwind_plan, this, thread, verbose);
254 s.PutChar(' ');
255 }
256}
257
259
261 uint32_t reg_num,
262 UnwindPlan::Row::AbstractRegisterLocation &register_location) const {
263 collection::const_iterator pos = m_register_locations.find(reg_num);
264 if (pos != m_register_locations.end()) {
265 register_location = pos->second;
266 return true;
267 }
269 register_location.SetUndefined();
270 return true;
271 }
272 return false;
273}
274
276 collection::const_iterator pos = m_register_locations.find(reg_num);
277 if (pos != m_register_locations.end()) {
278 m_register_locations.erase(pos);
279 }
280}
281
283 uint32_t reg_num,
284 const UnwindPlan::Row::AbstractRegisterLocation register_location) {
285 m_register_locations[reg_num] = register_location;
286}
287
289 int32_t offset,
290 bool can_replace) {
291 if (!can_replace &&
292 m_register_locations.find(reg_num) != m_register_locations.end())
293 return false;
295 reg_loc.SetAtCFAPlusOffset(offset);
296 m_register_locations[reg_num] = reg_loc;
297 return true;
298}
299
301 int32_t offset,
302 bool can_replace) {
303 if (!can_replace &&
304 m_register_locations.find(reg_num) != m_register_locations.end())
305 return false;
307 reg_loc.SetIsCFAPlusOffset(offset);
308 m_register_locations[reg_num] = reg_loc;
309 return true;
310}
311
313 uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified) {
314 collection::iterator pos = m_register_locations.find(reg_num);
315 collection::iterator end = m_register_locations.end();
316
317 if (pos != end) {
318 if (!can_replace)
319 return false;
320 if (can_replace_only_if_unspecified && !pos->second.IsUnspecified())
321 return false;
322 }
324 reg_loc.SetUndefined();
325 m_register_locations[reg_num] = reg_loc;
326 return true;
327}
328
330 bool can_replace) {
331 if (!can_replace &&
332 m_register_locations.find(reg_num) != m_register_locations.end())
333 return false;
335 reg_loc.SetUnspecified();
336 m_register_locations[reg_num] = reg_loc;
337 return true;
338}
339
341 uint32_t other_reg_num,
342 bool can_replace) {
343 if (!can_replace &&
344 m_register_locations.find(reg_num) != m_register_locations.end())
345 return false;
347 reg_loc.SetInRegister(other_reg_num);
348 m_register_locations[reg_num] = reg_loc;
349 return true;
350}
351
353 bool must_replace) {
354 if (must_replace &&
355 m_register_locations.find(reg_num) == m_register_locations.end())
356 return false;
358 reg_loc.SetSame();
359 m_register_locations[reg_num] = reg_loc;
360 return true;
361}
362
364 uint32_t reg_num, const uint8_t *opcodes, uint32_t len, bool can_replace) {
365 if (!can_replace &&
366 m_register_locations.find(reg_num) != m_register_locations.end())
367 return false;
369 reg_loc.SetIsDWARFExpression(opcodes, len);
370 m_register_locations[reg_num] = reg_loc;
371 return true;
372}
373
375 uint64_t constant,
376 bool can_replace) {
377 if (!can_replace &&
378 m_register_locations.find(reg_num) != m_register_locations.end())
379 return false;
381 reg_loc.SetIsConstant(constant);
382 m_register_locations[reg_num] = reg_loc;
383 return true;
384}
385
393
395 if (m_row_list.empty() || m_row_list.back().GetOffset() != row.GetOffset())
396 m_row_list.push_back(std::move(row));
397 else
398 m_row_list.back() = std::move(row);
399}
400
401struct RowLess {
402 bool operator()(int64_t a, const UnwindPlan::Row &b) const {
403 return a < b.GetOffset();
404 }
405 bool operator()(const UnwindPlan::Row &a, int64_t b) const {
406 return a.GetOffset() < b;
407 }
408};
409
410void UnwindPlan::InsertRow(Row row, bool replace_existing) {
411 auto it = llvm::lower_bound(m_row_list, row.GetOffset(), RowLess());
412 if (it == m_row_list.end() || it->GetOffset() > row.GetOffset())
413 m_row_list.insert(it, std::move(row));
414 else {
415 assert(it->GetOffset() == row.GetOffset());
416 if (replace_existing)
417 *it = std::move(row);
418 }
419}
420
421const UnwindPlan::Row *
422UnwindPlan::GetRowForFunctionOffset(std::optional<int64_t> offset) const {
423 auto it = offset ? llvm::upper_bound(m_row_list, *offset, RowLess())
424 : m_row_list.end();
425 if (it == m_row_list.begin())
426 return nullptr;
427 // upper_bound returns the row strictly greater than our desired offset, which
428 // means that the row before it is a match.
429 return &*std::prev(it);
430}
431
432bool UnwindPlan::IsValidRowIndex(uint32_t idx) const {
433 return idx < m_row_list.size();
434}
435
436const UnwindPlan::Row *UnwindPlan::GetRowAtIndex(uint32_t idx) const {
437 if (idx < m_row_list.size())
438 return &m_row_list[idx];
440 "error: UnwindPlan::GetRowAtIndex(idx = {0}) invalid index "
441 "(number rows is {1})",
442 idx, m_row_list.size());
443 return nullptr;
444}
445
447 if (m_row_list.empty()) {
449 "UnwindPlan::GetLastRow() when rows are empty");
450 return nullptr;
451 }
452 return &m_row_list.back();
453}
454
456 // If this UnwindPlan has no rows, it is an invalid UnwindPlan.
457 if (GetRowCount() == 0) {
458 Log *log = GetLog(LLDBLog::Unwind);
459 if (log) {
460 StreamString s;
461 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
462 LLDB_LOGF(log,
463 "UnwindPlan is invalid -- no unwind rows for UnwindPlan "
464 "'%s' at address %s",
465 m_source_name.GetCString(), s.GetData());
466 } else {
467 LLDB_LOGF(log,
468 "UnwindPlan is invalid -- no unwind rows for UnwindPlan '%s'",
469 m_source_name.GetCString());
470 }
471 }
472 return false;
473 }
474
475 // If the 0th Row of unwind instructions is missing, or if it doesn't provide
476 // a register to use to find the Canonical Frame Address, this is not a valid
477 // UnwindPlan.
478 const Row *row0 = GetRowAtIndex(0);
479 if (!row0 ||
481 Log *log = GetLog(LLDBLog::Unwind);
482 if (log) {
483 StreamString s;
484 if (addr.Dump(&s, nullptr, Address::DumpStyleSectionNameOffset)) {
485 LLDB_LOGF(log,
486 "UnwindPlan is invalid -- no CFA register defined in row 0 "
487 "for UnwindPlan '%s' at address %s",
488 m_source_name.GetCString(), s.GetData());
489 } else {
490 LLDB_LOGF(log,
491 "UnwindPlan is invalid -- no CFA register defined in row 0 "
492 "for UnwindPlan '%s'",
493 m_source_name.GetCString());
494 }
495 }
496 return false;
497 }
498
499 if (m_plan_valid_ranges.empty())
500 return true;
501
502 if (!addr.IsValid())
503 return true;
504
505 return llvm::any_of(m_plan_valid_ranges, [&](const AddressRange &range) {
506 return range.ContainsFileAddress(addr);
507 });
508}
509
510void UnwindPlan::Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const {
511 if (!m_source_name.IsEmpty()) {
512 s.Printf("This UnwindPlan originally sourced from %s\n",
513 m_source_name.GetCString());
514 }
515 s.Printf("This UnwindPlan is sourced from the compiler: ");
517 case eLazyBoolYes:
518 s.Printf("yes.\n");
519 break;
520 case eLazyBoolNo:
521 s.Printf("no.\n");
522 break;
524 s.Printf("not specified.\n");
525 break;
526 }
527 s.Printf("This UnwindPlan is valid at all instruction locations: ");
529 case eLazyBoolYes:
530 s.Printf("yes.\n");
531 break;
532 case eLazyBoolNo:
533 s.Printf("no.\n");
534 break;
536 s.Printf("not specified.\n");
537 break;
538 }
539 s.Printf("This UnwindPlan is for a trap handler function: ");
541 case eLazyBoolYes:
542 s.Printf("yes.\n");
543 break;
544 case eLazyBoolNo:
545 s.Printf("no.\n");
546 break;
548 s.Printf("not specified.\n");
549 break;
550 }
551 if (!m_plan_valid_ranges.empty()) {
552 s.PutCString("Address range of this UnwindPlan: ");
553 TargetSP target_sp(thread->CalculateTarget());
554 for (const AddressRange &range : m_plan_valid_ranges)
555 range.Dump(&s, target_sp.get(), Address::DumpStyleSectionNameOffset);
556 s.EOL();
557 }
558 for (const auto &[index, row] : llvm::enumerate(m_row_list)) {
559 s.Format("row[{0}]: ", index);
560 row.Dump(s, this, thread, base_addr);
561 s << "\n";
562 }
563}
564
565void UnwindPlan::SetSourceName(const char *source) {
566 m_source_name = ConstString(source);
567}
568
570
572 uint32_t unwind_reg) const {
573 if (thread) {
574 RegisterContext *reg_ctx = thread->GetRegisterContext().get();
575 if (reg_ctx) {
576 uint32_t reg;
578 reg = unwind_reg;
579 else
581 unwind_reg);
582 if (reg != LLDB_INVALID_REGNUM)
583 return reg_ctx->GetRegisterInfoAtIndex(reg);
584 }
585 }
586 return nullptr;
587}
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:364
#define LLDB_LOGF(log,...)
Definition Log.h:378
static std::optional< std::pair< lldb::ByteOrder, uint32_t > > GetByteOrderAndAddrSize(Thread *thread)
static void DumpRegisterName(Stream &s, const UnwindPlan *unwind_plan, Thread *thread, uint32_t reg_num)
static void DumpDWARFExpr(Stream &s, llvm::ArrayRef< uint8_t > expr, Thread *thread)
A section + offset based address range class.
bool ContainsFileAddress(const Address &so_addr) const
Check if a section offset address is contained in this range.
A section + offset based address class.
Definition Address.h:62
@ DumpStyleSectionNameOffset
Display as the section name + offset.
Definition Address.h:74
bool Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, DumpStyle fallback_style=DumpStyleInvalid, uint32_t addr_byte_size=UINT32_MAX, bool all_ranges=false, std::optional< Stream::HighlightSettings > settings=std::nullopt) const
Dump a description of this object to a Stream.
Definition Address.cpp:396
bool IsValid() const
Check if the object state is valid.
Definition Address.h:355
An architecture specification class.
Definition ArchSpec.h:32
uint32_t GetAddressByteSize() const
Returns the size in bytes of an address of the current architecture.
Definition ArchSpec.cpp:681
lldb::ByteOrder GetByteOrder() const
Returns the byte order for the architecture specification.
Definition ArchSpec.cpp:730
A uniqued constant string class.
Definition ConstString.h:40
virtual uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, uint32_t num)
Convert from a given register numbering scheme to the lldb register numbering scheme.
virtual const RegisterInfo * GetRegisterInfoAtIndex(size_t reg)=0
const char * GetData() const
A stream class that can stream formatted output to a file.
Definition Stream.h:28
void Format(const char *format, Args &&... args)
Forwards the arguments to llvm::formatv and writes to the stream.
Definition Stream.h:367
llvm::raw_ostream & AsRawOstream()
Returns a raw_ostream that forwards the data to this Stream object.
Definition Stream.h:402
size_t Printf(const char *format,...) __attribute__((format(printf
Output printf formatted output to the stream.
Definition Stream.cpp:132
size_t PutCString(llvm::StringRef cstr)
Output a C string to the stream.
Definition Stream.cpp:63
size_t PutChar(char ch)
Definition Stream.cpp:129
size_t EOL()
Output and End of Line character to the stream.
Definition Stream.cpp:153
union lldb_private::UnwindPlan::Row::AbstractRegisterLocation::@247123167177001062106372012325121034343026205311 m_location
void SetIsDWARFExpression(const uint8_t *opcodes, uint32_t len)
void Dump(Stream &s, const UnwindPlan *unwind_plan, const UnwindPlan::Row *row, Thread *thread, bool verbose) const
struct lldb_private::UnwindPlan::Row::AbstractRegisterLocation::@247123167177001062106372012325121034343026205311::@345126345073156072262163204234160210215141241152 expr
bool operator==(const AbstractRegisterLocation &rhs) const
void SetAtDWARFExpression(const uint8_t *opcodes, uint32_t len)
void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread) const
struct lldb_private::UnwindPlan::Row::FAValue::@237005217134263076317055000055340116042170060244::@061007216234210042211152150212272247344332146356 reg
struct lldb_private::UnwindPlan::Row::FAValue::@237005217134263076317055000055340116042170060244::@253003143140122340134355243175033354327034135274 expr
bool operator==(const FAValue &rhs) const
union lldb_private::UnwindPlan::Row::FAValue::@237005217134263076317055000055340116042170060244 m_value
bool SetRegisterLocationToIsConstant(uint32_t reg_num, uint64_t constant, bool can_replace)
bool SetRegisterLocationToIsCFAPlusOffset(uint32_t reg_num, int32_t offset, bool can_replace)
bool SetRegisterLocationToSame(uint32_t reg_num, bool must_replace)
bool SetRegisterLocationToIsDWARFExpression(uint32_t reg_num, const uint8_t *opcodes, uint32_t len, bool can_replace)
This method does not make a copy of the opcodes memory, it is assumed to have the same lifetime as th...
bool SetRegisterLocationToAtCFAPlusOffset(uint32_t reg_num, int32_t offset, bool can_replace)
const FAValue & GetCFAValue() const
Definition UnwindPlan.h:365
bool GetRegisterInfo(uint32_t reg_num, AbstractRegisterLocation &register_location) const
bool SetRegisterLocationToRegister(uint32_t reg_num, uint32_t other_reg_num, bool can_replace)
void Dump(Stream &s, const UnwindPlan *unwind_plan, Thread *thread, lldb::addr_t base_addr) const
bool SetRegisterLocationToUnspecified(uint32_t reg_num, bool can_replace)
void RemoveRegisterInfo(uint32_t reg_num)
bool operator==(const Row &rhs) const
bool SetRegisterLocationToUndefined(uint32_t reg_num, bool can_replace, bool can_replace_only_if_unspecified)
void SetRegisterInfo(uint32_t reg_num, const AbstractRegisterLocation register_location)
std::vector< Row > m_row_list
Definition UnwindPlan.h:544
lldb::RegisterKind m_register_kind
Definition UnwindPlan.h:546
UnwindPlan(lldb::RegisterKind reg_kind)
Definition UnwindPlan.h:431
const RegisterInfo * GetRegisterInfo(Thread *thread, uint32_t reg_num) const
lldb_private::LazyBool m_plan_is_for_signal_trap
Definition UnwindPlan.h:556
bool IsValidRowIndex(uint32_t idx) const
void InsertRow(Row row, bool replace_existing=false)
lldb_private::LazyBool m_plan_is_valid_at_all_instruction_locations
Definition UnwindPlan.h:555
std::vector< AddressRange > m_plan_valid_ranges
Definition UnwindPlan.h:545
const UnwindPlan::Row * GetRowForFunctionOffset(std::optional< int64_t > offset) const
bool PlanValidAtAddress(Address addr) const
lldb_private::ConstString m_source_name
Definition UnwindPlan.h:553
const UnwindPlan::Row * GetLastRow() const
void SetSourceName(const char *)
void Dump(Stream &s, Thread *thread, lldb::addr_t base_addr) const
lldb_private::ConstString GetSourceName() const
const UnwindPlan::Row * GetRowAtIndex(uint32_t idx) const
lldb_private::LazyBool m_plan_is_sourced_from_compiler
Definition UnwindPlan.h:554
#define LLDB_INVALID_ADDRESS
#define LLDB_INVALID_REGNUM
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:327
std::shared_ptr< lldb_private::Process > ProcessSP
uint64_t addr_t
Definition lldb-types.h:80
std::shared_ptr< lldb_private::Target > TargetSP
@ eRegisterKindLLDB
lldb's internal register numbers
bool operator()(const UnwindPlan::Row &a, int64_t b) const
bool operator()(int64_t a, const UnwindPlan::Row &b) const
Every register is described in detail including its name, alternate name (optional),...
const char * name
Name of this register, can't be NULL.