1//===-- FuncUnwinders.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/FuncUnwinders.h"
10#include "lldb/Core/Address.h"
11#include "lldb/Core/AddressRange.h"
12#include "lldb/Symbol/ArmUnwindInfo.h"
13#include "lldb/Symbol/CallFrameInfo.h"
14#include "lldb/Symbol/CompactUnwindInfo.h"
15#include "lldb/Symbol/DWARFCallFrameInfo.h"
16#include "lldb/Symbol/ObjectFile.h"
17#include "lldb/Symbol/SymbolFile.h"
18#include "lldb/Symbol/UnwindPlan.h"
19#include "lldb/Symbol/UnwindTable.h"
20#include "lldb/Target/ABI.h"
21#include "lldb/Target/ExecutionContext.h"
22#include "lldb/Target/Process.h"
23#include "lldb/Target/RegisterContext.h"
24#include "lldb/Target/RegisterNumber.h"
25#include "lldb/Target/Target.h"
26#include "lldb/Target/Thread.h"
27#include "lldb/Target/UnwindAssembly.h"
28
29#include <memory>
30
31using namespace lldb;
32using namespace lldb_private;
33
34FuncUnwinders::FuncUnwinders(UnwindTable &unwind_table, Address addr,
35 AddressRanges ranges)
36 : m_unwind_table(unwind_table), m_addr(std::move(addr)),
37 m_ranges(std::move(ranges)), m_tried_unwind_plan_assembly(false),
38 m_tried_unwind_plan_eh_frame(false),
39 m_tried_unwind_plan_object_file(false),
40 m_tried_unwind_plan_debug_frame(false),
41 m_tried_unwind_plan_object_file_augmented(false),
42 m_tried_unwind_plan_eh_frame_augmented(false),
43 m_tried_unwind_plan_debug_frame_augmented(false),
44 m_tried_unwind_plan_compact_unwind(false),
45 m_tried_unwind_plan_arm_unwind(false),
46 m_tried_unwind_plan_symbol_file(false), m_tried_unwind_fast(false),
47 m_tried_unwind_arch_default(false),
48 m_tried_unwind_arch_default_at_func_entry(false),
49 m_first_non_prologue_insn() {}
50
51/// destructor
52
53FuncUnwinders::~FuncUnwinders() = default;
54
55std::shared_ptr<const UnwindPlan>
56FuncUnwinders::GetUnwindPlanAtCallSite(Target &target, Thread &thread) {
57 std::lock_guard<std::recursive_mutex> guard(m_mutex);
58
59 if (std::shared_ptr<const UnwindPlan> plan_sp =
60 GetObjectFileUnwindPlan(target))
61 return plan_sp;
62 if (std::shared_ptr<const UnwindPlan> plan_sp =
63 GetSymbolFileUnwindPlan(thread))
64 return plan_sp;
65 if (std::shared_ptr<const UnwindPlan> plan_sp =
66 GetDebugFrameUnwindPlan(target))
67 return plan_sp;
68 if (std::shared_ptr<const UnwindPlan> plan_sp = GetEHFrameUnwindPlan(target))
69 return plan_sp;
70 if (std::shared_ptr<const UnwindPlan> plan_sp =
71 GetCompactUnwindUnwindPlan(target))
72 return plan_sp;
73 if (std::shared_ptr<const UnwindPlan> plan_sp =
74 GetArmUnwindUnwindPlan(target))
75 return plan_sp;
76
77 return nullptr;
78}
79
80std::shared_ptr<const UnwindPlan>
81FuncUnwinders::GetCompactUnwindUnwindPlan(Target &target) {
82 std::lock_guard<std::recursive_mutex> guard(m_mutex);
83 if (m_unwind_plan_compact_unwind.size() > 0)
84 return m_unwind_plan_compact_unwind[0]; // FIXME support multiple compact
85 // unwind plans for one func
86 if (m_tried_unwind_plan_compact_unwind)
87 return nullptr;
88
89 m_tried_unwind_plan_compact_unwind = true;
90 // Only continuous functions are supported.
91 if (m_ranges.size() == 1) {
92 Address current_pc(m_ranges[0].GetBaseAddress());
93 CompactUnwindInfo *compact_unwind = m_unwind_table.GetCompactUnwindInfo();
94 if (compact_unwind) {
95 auto unwind_plan_sp =
96 std::make_shared<UnwindPlan>(args: lldb::eRegisterKindGeneric);
97 if (compact_unwind->GetUnwindPlan(target, addr: current_pc, unwind_plan&: *unwind_plan_sp)) {
98 m_unwind_plan_compact_unwind.push_back(x: unwind_plan_sp);
99 return m_unwind_plan_compact_unwind[0]; // FIXME support multiple
100 // compact unwind plans for one
101 // func
102 }
103 }
104 }
105 return nullptr;
106}
107
108std::shared_ptr<const UnwindPlan>
109FuncUnwinders::GetObjectFileUnwindPlan(Target &target) {
110 std::lock_guard<std::recursive_mutex> guard(m_mutex);
111 if (m_unwind_plan_object_file_sp.get() ||
112 m_tried_unwind_plan_object_file)
113 return m_unwind_plan_object_file_sp;
114
115 m_tried_unwind_plan_object_file = true;
116 if (CallFrameInfo *object_file_frame =
117 m_unwind_table.GetObjectFileUnwindInfo())
118 m_unwind_plan_object_file_sp =
119 object_file_frame->GetUnwindPlan(ranges: m_ranges, addr: m_addr);
120 return m_unwind_plan_object_file_sp;
121}
122
123std::shared_ptr<const UnwindPlan>
124FuncUnwinders::GetEHFrameUnwindPlan(Target &target) {
125 std::lock_guard<std::recursive_mutex> guard(m_mutex);
126 if (m_unwind_plan_eh_frame_sp.get() || m_tried_unwind_plan_eh_frame)
127 return m_unwind_plan_eh_frame_sp;
128
129 m_tried_unwind_plan_eh_frame = true;
130 if (m_addr.IsValid()) {
131 if (DWARFCallFrameInfo *eh_frame = m_unwind_table.GetEHFrameInfo())
132 m_unwind_plan_eh_frame_sp = eh_frame->GetUnwindPlan(ranges: m_ranges, addr: m_addr);
133 }
134 return m_unwind_plan_eh_frame_sp;
135}
136
137std::shared_ptr<const UnwindPlan>
138FuncUnwinders::GetDebugFrameUnwindPlan(Target &target) {
139 std::lock_guard<std::recursive_mutex> guard(m_mutex);
140 if (m_unwind_plan_debug_frame_sp || m_tried_unwind_plan_debug_frame)
141 return m_unwind_plan_debug_frame_sp;
142
143 m_tried_unwind_plan_debug_frame = true;
144 if (!m_ranges.empty()) {
145 if (DWARFCallFrameInfo *debug_frame = m_unwind_table.GetDebugFrameInfo())
146 m_unwind_plan_debug_frame_sp =
147 debug_frame->GetUnwindPlan(ranges: m_ranges, addr: m_addr);
148 }
149 return m_unwind_plan_debug_frame_sp;
150}
151
152std::shared_ptr<const UnwindPlan>
153FuncUnwinders::GetArmUnwindUnwindPlan(Target &target) {
154 std::lock_guard<std::recursive_mutex> guard(m_mutex);
155 if (m_unwind_plan_arm_unwind_sp.get() || m_tried_unwind_plan_arm_unwind)
156 return m_unwind_plan_arm_unwind_sp;
157
158 m_tried_unwind_plan_arm_unwind = true;
159 // Only continuous functions are supported.
160 if (m_ranges.size() == 1) {
161 Address current_pc = m_ranges[0].GetBaseAddress();
162 ArmUnwindInfo *arm_unwind_info = m_unwind_table.GetArmUnwindInfo();
163 if (arm_unwind_info) {
164 auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindGeneric);
165 if (arm_unwind_info->GetUnwindPlan(target, addr: current_pc, unwind_plan&: *plan_sp))
166 m_unwind_plan_arm_unwind_sp = std::move(plan_sp);
167 }
168 }
169 return m_unwind_plan_arm_unwind_sp;
170}
171
172namespace {
173class RegisterContextToInfo: public SymbolFile::RegisterInfoResolver {
174public:
175 RegisterContextToInfo(RegisterContext &ctx) : m_ctx(ctx) {}
176
177 const RegisterInfo *ResolveName(llvm::StringRef name) const override {
178 return m_ctx.GetRegisterInfoByName(reg_name: name);
179 }
180 const RegisterInfo *ResolveNumber(lldb::RegisterKind kind,
181 uint32_t number) const override {
182 return m_ctx.GetRegisterInfo(reg_kind: kind, reg_num: number);
183 }
184
185private:
186 RegisterContext &m_ctx;
187};
188} // namespace
189
190std::shared_ptr<const UnwindPlan>
191FuncUnwinders::GetSymbolFileUnwindPlan(Thread &thread) {
192 std::lock_guard<std::recursive_mutex> guard(m_mutex);
193 if (m_unwind_plan_symbol_file_sp.get() || m_tried_unwind_plan_symbol_file)
194 return m_unwind_plan_symbol_file_sp;
195
196 m_tried_unwind_plan_symbol_file = true;
197 if (SymbolFile *symfile = m_unwind_table.GetSymbolFile();
198 symfile && m_ranges.size() == 1) {
199 m_unwind_plan_symbol_file_sp = symfile->GetUnwindPlan(
200 address: m_ranges[0].GetBaseAddress(),
201 resolver: RegisterContextToInfo(*thread.GetRegisterContext()));
202 }
203 return m_unwind_plan_symbol_file_sp;
204}
205
206std::shared_ptr<const UnwindPlan>
207FuncUnwinders::GetObjectFileAugmentedUnwindPlan(Target &target,
208 Thread &thread) {
209 std::lock_guard<std::recursive_mutex> guard(m_mutex);
210 if (m_unwind_plan_object_file_augmented_sp.get() ||
211 m_tried_unwind_plan_object_file_augmented)
212 return m_unwind_plan_object_file_augmented_sp;
213
214 m_tried_unwind_plan_object_file_augmented = true;
215
216 std::shared_ptr<const UnwindPlan> object_file_unwind_plan =
217 GetObjectFileUnwindPlan(target);
218 if (!object_file_unwind_plan)
219 return m_unwind_plan_object_file_augmented_sp;
220
221 // Augment the instructions with epilogue descriptions if necessary
222 // so the UnwindPlan can be used at any instruction in the function.
223
224 UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
225 // Only continuous functions are supported.
226 if (assembly_profiler_sp && m_ranges.size() == 1) {
227 auto plan_sp = std::make_shared<UnwindPlan>(args: *object_file_unwind_plan);
228
229 if (assembly_profiler_sp->AugmentUnwindPlanFromCallSite(func&: m_ranges[0], thread,
230 unwind_plan&: *plan_sp))
231 m_unwind_plan_object_file_augmented_sp = std::move(plan_sp);
232 }
233 return m_unwind_plan_object_file_augmented_sp;
234}
235
236std::shared_ptr<const UnwindPlan>
237FuncUnwinders::GetEHFrameAugmentedUnwindPlan(Target &target, Thread &thread) {
238 std::lock_guard<std::recursive_mutex> guard(m_mutex);
239 if (m_unwind_plan_eh_frame_augmented_sp.get() ||
240 m_tried_unwind_plan_eh_frame_augmented)
241 return m_unwind_plan_eh_frame_augmented_sp;
242
243 // Only supported on x86 architectures where we get eh_frame from the
244 // compiler that describes the prologue instructions perfectly, and sometimes
245 // the epilogue instructions too.
246 if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
247 target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
248 target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
249 m_tried_unwind_plan_eh_frame_augmented = true;
250 return m_unwind_plan_eh_frame_augmented_sp;
251 }
252
253 m_tried_unwind_plan_eh_frame_augmented = true;
254
255 std::shared_ptr<const UnwindPlan> eh_frame_plan =
256 GetEHFrameUnwindPlan(target);
257 if (!eh_frame_plan)
258 return m_unwind_plan_eh_frame_augmented_sp;
259
260 // Augment the eh_frame instructions with epilogue descriptions if necessary
261 // so the UnwindPlan can be used at any instruction in the function.
262
263 UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
264 // Only continuous functions are supported.
265 if (assembly_profiler_sp && m_ranges.size() == 1) {
266 auto plan_sp = std::make_shared<UnwindPlan>(args: *eh_frame_plan);
267 if (assembly_profiler_sp->AugmentUnwindPlanFromCallSite(func&: m_ranges[0], thread,
268 unwind_plan&: *plan_sp))
269 m_unwind_plan_eh_frame_augmented_sp = std::move(plan_sp);
270 }
271 return m_unwind_plan_eh_frame_augmented_sp;
272}
273
274std::shared_ptr<const UnwindPlan>
275FuncUnwinders::GetDebugFrameAugmentedUnwindPlan(Target &target,
276 Thread &thread) {
277 std::lock_guard<std::recursive_mutex> guard(m_mutex);
278 if (m_unwind_plan_debug_frame_augmented_sp.get() ||
279 m_tried_unwind_plan_debug_frame_augmented)
280 return m_unwind_plan_debug_frame_augmented_sp;
281
282 // Only supported on x86 architectures where we get debug_frame from the
283 // compiler that describes the prologue instructions perfectly, and sometimes
284 // the epilogue instructions too.
285 if (target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_32_i386 &&
286 target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64 &&
287 target.GetArchitecture().GetCore() != ArchSpec::eCore_x86_64_x86_64h) {
288 m_tried_unwind_plan_debug_frame_augmented = true;
289 return m_unwind_plan_debug_frame_augmented_sp;
290 }
291
292 m_tried_unwind_plan_debug_frame_augmented = true;
293
294 std::shared_ptr<const UnwindPlan> debug_frame_plan =
295 GetDebugFrameUnwindPlan(target);
296 if (!debug_frame_plan)
297 return m_unwind_plan_debug_frame_augmented_sp;
298
299 // Augment the debug_frame instructions with epilogue descriptions if
300 // necessary so the UnwindPlan can be used at any instruction in the
301 // function.
302
303 UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
304 // Only continuous functions are supported.
305 if (assembly_profiler_sp && m_ranges.size() == 1) {
306 auto plan_sp = std::make_shared<UnwindPlan>(args: *debug_frame_plan);
307
308 if (assembly_profiler_sp->AugmentUnwindPlanFromCallSite(func&: m_ranges[0], thread,
309 unwind_plan&: *plan_sp))
310 m_unwind_plan_debug_frame_augmented_sp = std::move(plan_sp);
311 }
312 return m_unwind_plan_debug_frame_augmented_sp;
313}
314
315std::shared_ptr<const UnwindPlan>
316FuncUnwinders::GetAssemblyUnwindPlan(Target &target, Thread &thread) {
317 std::lock_guard<std::recursive_mutex> guard(m_mutex);
318 if (m_unwind_plan_assembly_sp.get() || m_tried_unwind_plan_assembly ||
319 !m_unwind_table.GetAllowAssemblyEmulationUnwindPlans()) {
320 return m_unwind_plan_assembly_sp;
321 }
322
323 m_tried_unwind_plan_assembly = true;
324
325 UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
326 // Only continuous functions are supported.
327 if (assembly_profiler_sp && m_ranges.size() == 1) {
328 // Don't analyze more than 10 megabytes of instructions,
329 // if a function is legitimately larger than that, we'll
330 // miss the epilogue instructions, but guard against a
331 // bogusly large function and analyzing large amounts of
332 // non-instruction data.
333 AddressRange range = m_ranges[0];
334 const addr_t func_size =
335 std::min(a: range.GetByteSize(), b: (addr_t)1024 * 10 * 10);
336 range.SetByteSize(func_size);
337
338 auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindGeneric);
339 if (assembly_profiler_sp->GetNonCallSiteUnwindPlanFromAssembly(
340 func&: range, thread, unwind_plan&: *plan_sp))
341 m_unwind_plan_assembly_sp = std::move(plan_sp);
342 }
343 return m_unwind_plan_assembly_sp;
344}
345
346// This method compares the pc unwind rule in the first row of two UnwindPlans.
347// If they have the same way of getting the pc value (e.g. "CFA - 8" + "CFA is
348// sp"), then it will return LazyBoolTrue.
349LazyBool FuncUnwinders::CompareUnwindPlansForIdenticalInitialPCLocation(
350 Thread &thread, const std::shared_ptr<const UnwindPlan> &a,
351 const std::shared_ptr<const UnwindPlan> &b) {
352 if (!a || !b)
353 return eLazyBoolCalculate;
354
355 const UnwindPlan::Row *a_first_row = a->GetRowAtIndex(idx: 0);
356 const UnwindPlan::Row *b_first_row = b->GetRowAtIndex(idx: 0);
357 if (!a_first_row || !b_first_row)
358 return eLazyBoolCalculate;
359
360 RegisterNumber pc_reg(thread, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC);
361 uint32_t a_pc_regnum = pc_reg.GetAsKind(kind: a->GetRegisterKind());
362 uint32_t b_pc_regnum = pc_reg.GetAsKind(kind: b->GetRegisterKind());
363
364 UnwindPlan::Row::AbstractRegisterLocation a_pc_regloc;
365 UnwindPlan::Row::AbstractRegisterLocation b_pc_regloc;
366
367 a_first_row->GetRegisterInfo(reg_num: a_pc_regnum, register_location&: a_pc_regloc);
368 b_first_row->GetRegisterInfo(reg_num: b_pc_regnum, register_location&: b_pc_regloc);
369
370 if (a_first_row->GetCFAValue() != b_first_row->GetCFAValue())
371 return eLazyBoolNo;
372 if (a_pc_regloc != b_pc_regloc)
373 return eLazyBoolNo;
374
375 return eLazyBoolYes;
376}
377
378std::shared_ptr<const UnwindPlan>
379FuncUnwinders::GetUnwindPlanAtNonCallSite(Target &target, Thread &thread) {
380 std::shared_ptr<const UnwindPlan> eh_frame_sp = GetEHFrameUnwindPlan(target);
381 if (!eh_frame_sp)
382 eh_frame_sp = GetDebugFrameUnwindPlan(target);
383 if (!eh_frame_sp)
384 eh_frame_sp = GetObjectFileUnwindPlan(target);
385 std::shared_ptr<const UnwindPlan> arch_default_at_entry_sp =
386 GetUnwindPlanArchitectureDefaultAtFunctionEntry(thread);
387 std::shared_ptr<const UnwindPlan> arch_default_sp =
388 GetUnwindPlanArchitectureDefault(thread);
389 std::shared_ptr<const UnwindPlan> assembly_sp =
390 GetAssemblyUnwindPlan(target, thread);
391
392 // This point of this code is to detect when a function is using a non-
393 // standard ABI, and the eh_frame correctly describes that alternate ABI.
394 // This is addressing a specific situation on x86_64 linux systems where one
395 // function in a library pushes a value on the stack and jumps to another
396 // function. So using an assembly instruction based unwind will not work
397 // when you're in the second function - the stack has been modified in a non-
398 // ABI way. But we have eh_frame that correctly describes how to unwind from
399 // this location. So we're looking to see if the initial pc register save
400 // location from the eh_frame is different from the assembly unwind, the arch
401 // default unwind, and the arch default at initial function entry.
402 //
403 // We may have eh_frame that describes the entire function -- or we may have
404 // eh_frame that only describes the unwind after the prologue has executed --
405 // so we need to check both the arch default (once the prologue has executed)
406 // and the arch default at initial function entry. And we may be running on
407 // a target where we have only some of the assembly/arch default unwind plans
408 // available.
409
410 if (CompareUnwindPlansForIdenticalInitialPCLocation(
411 thread, a: eh_frame_sp, b: arch_default_at_entry_sp) == eLazyBoolNo &&
412 CompareUnwindPlansForIdenticalInitialPCLocation(
413 thread, a: eh_frame_sp, b: arch_default_sp) == eLazyBoolNo &&
414 CompareUnwindPlansForIdenticalInitialPCLocation(
415 thread, a: assembly_sp, b: arch_default_sp) == eLazyBoolNo) {
416 return eh_frame_sp;
417 }
418
419 if (std::shared_ptr<const UnwindPlan> plan_sp =
420 GetSymbolFileUnwindPlan(thread))
421 return plan_sp;
422 if (std::shared_ptr<const UnwindPlan> plan_sp =
423 GetDebugFrameAugmentedUnwindPlan(target, thread))
424 return plan_sp;
425 if (std::shared_ptr<const UnwindPlan> plan_sp =
426 GetEHFrameAugmentedUnwindPlan(target, thread))
427 return plan_sp;
428 if (std::shared_ptr<const UnwindPlan> plan_sp =
429 GetObjectFileAugmentedUnwindPlan(target, thread))
430 return plan_sp;
431
432 return assembly_sp;
433}
434
435std::shared_ptr<const UnwindPlan>
436FuncUnwinders::GetUnwindPlanFastUnwind(Target &target, Thread &thread) {
437 std::lock_guard<std::recursive_mutex> guard(m_mutex);
438 if (m_unwind_plan_fast_sp.get() || m_tried_unwind_fast)
439 return m_unwind_plan_fast_sp;
440
441 m_tried_unwind_fast = true;
442
443 UnwindAssemblySP assembly_profiler_sp(GetUnwindAssemblyProfiler(target));
444 if (assembly_profiler_sp && m_ranges.size() == 1) {
445 auto plan_sp = std::make_shared<UnwindPlan>(args: lldb::eRegisterKindGeneric);
446 if (assembly_profiler_sp->GetFastUnwindPlan(func&: m_ranges[0], thread, unwind_plan&: *plan_sp))
447 m_unwind_plan_fast_sp = std::move(plan_sp);
448 }
449 return m_unwind_plan_fast_sp;
450}
451
452std::shared_ptr<const UnwindPlan>
453FuncUnwinders::GetUnwindPlanArchitectureDefault(Thread &thread) {
454 std::lock_guard<std::recursive_mutex> guard(m_mutex);
455 if (m_unwind_plan_arch_default_sp.get() || m_tried_unwind_arch_default)
456 return m_unwind_plan_arch_default_sp;
457
458 m_tried_unwind_arch_default = true;
459
460 ProcessSP process_sp(thread.CalculateProcess());
461 if (process_sp) {
462 if (ABI *abi = process_sp->GetABI().get())
463 m_unwind_plan_arch_default_sp = abi->CreateDefaultUnwindPlan();
464 }
465
466 return m_unwind_plan_arch_default_sp;
467}
468
469std::shared_ptr<const UnwindPlan>
470FuncUnwinders::GetUnwindPlanArchitectureDefaultAtFunctionEntry(Thread &thread) {
471 std::lock_guard<std::recursive_mutex> guard(m_mutex);
472 if (m_unwind_plan_arch_default_at_func_entry_sp.get() ||
473 m_tried_unwind_arch_default_at_func_entry)
474 return m_unwind_plan_arch_default_at_func_entry_sp;
475
476 m_tried_unwind_arch_default_at_func_entry = true;
477
478 Address current_pc;
479 ProcessSP process_sp(thread.CalculateProcess());
480 if (process_sp) {
481 if (ABI *abi = process_sp->GetABI().get()) {
482 m_unwind_plan_arch_default_at_func_entry_sp =
483 abi->CreateFunctionEntryUnwindPlan();
484 }
485 }
486
487 return m_unwind_plan_arch_default_at_func_entry_sp;
488}
489
490const Address &FuncUnwinders::GetFunctionStartAddress() const { return m_addr; }
491
492lldb::UnwindAssemblySP
493FuncUnwinders::GetUnwindAssemblyProfiler(Target &target) {
494 UnwindAssemblySP assembly_profiler_sp;
495 if (ArchSpec arch = m_unwind_table.GetArchitecture()) {
496 arch.MergeFrom(other: target.GetArchitecture());
497 assembly_profiler_sp = UnwindAssembly::FindPlugin(arch);
498 }
499 return assembly_profiler_sp;
500}
501

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of lldb/source/Symbol/FuncUnwinders.cpp