/Users/buildslave/jenkins/workspace/coverage/llvm-project/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- AppleObjCRuntime.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 "AppleObjCRuntime.h" |
10 | | #include "AppleObjCRuntimeV1.h" |
11 | | #include "AppleObjCRuntimeV2.h" |
12 | | #include "AppleObjCTrampolineHandler.h" |
13 | | #include "Plugins/Language/ObjC/NSString.h" |
14 | | #include "Plugins/LanguageRuntime/CPlusPlus/CPPLanguageRuntime.h" |
15 | | #include "Plugins/Process/Utility/HistoryThread.h" |
16 | | #include "lldb/Breakpoint/BreakpointLocation.h" |
17 | | #include "lldb/Core/Module.h" |
18 | | #include "lldb/Core/ModuleList.h" |
19 | | #include "lldb/Core/PluginManager.h" |
20 | | #include "lldb/Core/Section.h" |
21 | | #include "lldb/Core/ValueObject.h" |
22 | | #include "lldb/Core/ValueObjectConstResult.h" |
23 | | #include "lldb/DataFormatters/FormattersHelpers.h" |
24 | | #include "lldb/Expression/DiagnosticManager.h" |
25 | | #include "lldb/Expression/FunctionCaller.h" |
26 | | #include "lldb/Symbol/ObjectFile.h" |
27 | | #include "lldb/Target/ExecutionContext.h" |
28 | | #include "lldb/Target/Process.h" |
29 | | #include "lldb/Target/RegisterContext.h" |
30 | | #include "lldb/Target/StopInfo.h" |
31 | | #include "lldb/Target/Target.h" |
32 | | #include "lldb/Target/Thread.h" |
33 | | #include "lldb/Utility/ConstString.h" |
34 | | #include "lldb/Utility/LLDBLog.h" |
35 | | #include "lldb/Utility/Log.h" |
36 | | #include "lldb/Utility/Scalar.h" |
37 | | #include "lldb/Utility/Status.h" |
38 | | #include "lldb/Utility/StreamString.h" |
39 | | #include "clang/AST/Type.h" |
40 | | |
41 | | #include "Plugins/TypeSystem/Clang/TypeSystemClang.h" |
42 | | |
43 | | #include <vector> |
44 | | |
45 | | using namespace lldb; |
46 | | using namespace lldb_private; |
47 | | |
48 | | LLDB_PLUGIN_DEFINE(AppleObjCRuntime) |
49 | | |
50 | | char AppleObjCRuntime::ID = 0; |
51 | | |
52 | 2.11k | AppleObjCRuntime::~AppleObjCRuntime() = default; |
53 | | |
54 | | AppleObjCRuntime::AppleObjCRuntime(Process *process) |
55 | 2.11k | : ObjCLanguageRuntime(process), m_read_objc_library(false), |
56 | 2.11k | m_objc_trampoline_handler_up(), m_Foundation_major() { |
57 | 2.11k | ReadObjCLibraryIfNeeded(process->GetTarget().GetImages()); |
58 | 2.11k | } |
59 | | |
60 | 3.92k | void AppleObjCRuntime::Initialize() { |
61 | 3.92k | AppleObjCRuntimeV2::Initialize(); |
62 | 3.92k | AppleObjCRuntimeV1::Initialize(); |
63 | 3.92k | } |
64 | | |
65 | 3.92k | void AppleObjCRuntime::Terminate() { |
66 | 3.92k | AppleObjCRuntimeV2::Terminate(); |
67 | 3.92k | AppleObjCRuntimeV1::Terminate(); |
68 | 3.92k | } |
69 | | |
70 | 162 | bool AppleObjCRuntime::GetObjectDescription(Stream &str, ValueObject &valobj) { |
71 | 162 | CompilerType compiler_type(valobj.GetCompilerType()); |
72 | 162 | bool is_signed; |
73 | | // ObjC objects can only be pointers (or numbers that actually represents |
74 | | // pointers but haven't been typecast, because reasons..) |
75 | 162 | if (!compiler_type.IsIntegerType(is_signed) && !compiler_type.IsPointerType()154 ) |
76 | 4 | return false; |
77 | | |
78 | | // Make the argument list: we pass one arg, the address of our pointer, to |
79 | | // the print function. |
80 | 158 | Value val; |
81 | | |
82 | 158 | if (!valobj.ResolveValue(val.GetScalar())) |
83 | 0 | return false; |
84 | | |
85 | | // Value Objects may not have a process in their ExecutionContextRef. But we |
86 | | // need to have one in the ref we pass down to eventually call description. |
87 | | // Get it from the target if it isn't present. |
88 | 158 | ExecutionContext exe_ctx; |
89 | 158 | if (valobj.GetProcessSP()) { |
90 | 156 | exe_ctx = ExecutionContext(valobj.GetExecutionContextRef()); |
91 | 156 | } else { |
92 | 2 | exe_ctx.SetContext(valobj.GetTargetSP(), true); |
93 | 2 | if (!exe_ctx.HasProcessScope()) |
94 | 0 | return false; |
95 | 2 | } |
96 | 158 | return GetObjectDescription(str, val, exe_ctx.GetBestExecutionContextScope()); |
97 | 158 | } |
98 | | bool AppleObjCRuntime::GetObjectDescription(Stream &strm, Value &value, |
99 | 158 | ExecutionContextScope *exe_scope) { |
100 | 158 | if (!m_read_objc_library) |
101 | 0 | return false; |
102 | | |
103 | 158 | ExecutionContext exe_ctx; |
104 | 158 | exe_scope->CalculateExecutionContext(exe_ctx); |
105 | 158 | Process *process = exe_ctx.GetProcessPtr(); |
106 | 158 | if (!process) |
107 | 0 | return false; |
108 | | |
109 | | // We need other parts of the exe_ctx, but the processes have to match. |
110 | 158 | assert(m_process == process); |
111 | | |
112 | | // Get the function address for the print function. |
113 | 158 | const Address *function_address = GetPrintForDebuggerAddr(); |
114 | 158 | if (!function_address) |
115 | 12 | return false; |
116 | | |
117 | 146 | Target *target = exe_ctx.GetTargetPtr(); |
118 | 146 | CompilerType compiler_type = value.GetCompilerType(); |
119 | 146 | if (compiler_type) { |
120 | 0 | if (!TypeSystemClang::IsObjCObjectPointerType(compiler_type)) { |
121 | 0 | strm.Printf("Value doesn't point to an ObjC object.\n"); |
122 | 0 | return false; |
123 | 0 | } |
124 | 146 | } else { |
125 | | // If it is not a pointer, see if we can make it into a pointer. |
126 | 146 | TypeSystemClangSP scratch_ts_sp = |
127 | 146 | ScratchTypeSystemClang::GetForTarget(*target); |
128 | 146 | if (!scratch_ts_sp) |
129 | 0 | return false; |
130 | | |
131 | 146 | CompilerType opaque_type = scratch_ts_sp->GetBasicType(eBasicTypeObjCID); |
132 | 146 | if (!opaque_type) |
133 | 0 | opaque_type = scratch_ts_sp->GetBasicType(eBasicTypeVoid).GetPointerType(); |
134 | | // value.SetContext(Value::eContextTypeClangType, opaque_type_ptr); |
135 | 146 | value.SetCompilerType(opaque_type); |
136 | 146 | } |
137 | | |
138 | 146 | ValueList arg_value_list; |
139 | 146 | arg_value_list.PushValue(value); |
140 | | |
141 | | // This is the return value: |
142 | 146 | TypeSystemClangSP scratch_ts_sp = |
143 | 146 | ScratchTypeSystemClang::GetForTarget(*target); |
144 | 146 | if (!scratch_ts_sp) |
145 | 0 | return false; |
146 | | |
147 | 146 | CompilerType return_compiler_type = scratch_ts_sp->GetCStringType(true); |
148 | 146 | Value ret; |
149 | | // ret.SetContext(Value::eContextTypeClangType, return_compiler_type); |
150 | 146 | ret.SetCompilerType(return_compiler_type); |
151 | | |
152 | 146 | if (exe_ctx.GetFramePtr() == nullptr) { |
153 | 6 | Thread *thread = exe_ctx.GetThreadPtr(); |
154 | 6 | if (thread == nullptr) { |
155 | 6 | exe_ctx.SetThreadSP(process->GetThreadList().GetSelectedThread()); |
156 | 6 | thread = exe_ctx.GetThreadPtr(); |
157 | 6 | } |
158 | 6 | if (thread) { |
159 | 6 | exe_ctx.SetFrameSP(thread->GetSelectedFrame(DoNoSelectMostRelevantFrame)); |
160 | 6 | } |
161 | 6 | } |
162 | | |
163 | | // Now we're ready to call the function: |
164 | | |
165 | 146 | DiagnosticManager diagnostics; |
166 | 146 | lldb::addr_t wrapper_struct_addr = LLDB_INVALID_ADDRESS; |
167 | | |
168 | 146 | if (!m_print_object_caller_up) { |
169 | 52 | Status error; |
170 | 52 | m_print_object_caller_up.reset( |
171 | 52 | exe_scope->CalculateTarget()->GetFunctionCallerForLanguage( |
172 | 52 | eLanguageTypeObjC, return_compiler_type, *function_address, |
173 | 52 | arg_value_list, "objc-object-description", error)); |
174 | 52 | if (error.Fail()) { |
175 | 0 | m_print_object_caller_up.reset(); |
176 | 0 | strm.Printf("Could not get function runner to call print for debugger " |
177 | 0 | "function: %s.", |
178 | 0 | error.AsCString()); |
179 | 0 | return false; |
180 | 0 | } |
181 | 52 | m_print_object_caller_up->InsertFunction(exe_ctx, wrapper_struct_addr, |
182 | 52 | diagnostics); |
183 | 94 | } else { |
184 | 94 | m_print_object_caller_up->WriteFunctionArguments( |
185 | 94 | exe_ctx, wrapper_struct_addr, arg_value_list, diagnostics); |
186 | 94 | } |
187 | | |
188 | 146 | EvaluateExpressionOptions options; |
189 | 146 | options.SetUnwindOnError(true); |
190 | 146 | options.SetTryAllThreads(true); |
191 | 146 | options.SetStopOthers(true); |
192 | 146 | options.SetIgnoreBreakpoints(true); |
193 | 146 | options.SetTimeout(process->GetUtilityExpressionTimeout()); |
194 | 146 | options.SetIsForUtilityExpr(true); |
195 | | |
196 | 146 | ExpressionResults results = m_print_object_caller_up->ExecuteFunction( |
197 | 146 | exe_ctx, &wrapper_struct_addr, options, diagnostics, ret); |
198 | 146 | if (results != eExpressionCompleted) { |
199 | 10 | strm.Printf("Error evaluating Print Object function: %d.\n", results); |
200 | 10 | return false; |
201 | 10 | } |
202 | | |
203 | 136 | addr_t result_ptr = ret.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); |
204 | | |
205 | 136 | char buf[512]; |
206 | 136 | size_t cstr_len = 0; |
207 | 136 | size_t full_buffer_len = sizeof(buf) - 1; |
208 | 136 | size_t curr_len = full_buffer_len; |
209 | 272 | while (curr_len == full_buffer_len) { |
210 | 136 | Status error; |
211 | 136 | curr_len = process->ReadCStringFromMemory(result_ptr + cstr_len, buf, |
212 | 136 | sizeof(buf), error); |
213 | 136 | strm.Write(buf, curr_len); |
214 | 136 | cstr_len += curr_len; |
215 | 136 | } |
216 | 136 | return cstr_len > 0; |
217 | 146 | } |
218 | | |
219 | 4.69k | lldb::ModuleSP AppleObjCRuntime::GetObjCModule() { |
220 | 4.69k | ModuleSP module_sp(m_objc_module_wp.lock()); |
221 | 4.69k | if (module_sp) |
222 | 3.73k | return module_sp; |
223 | | |
224 | 957 | Process *process = GetProcess(); |
225 | 957 | if (process) { |
226 | 957 | const ModuleList &modules = process->GetTarget().GetImages(); |
227 | 28.7k | for (uint32_t idx = 0; idx < modules.GetSize(); idx++27.7k ) { |
228 | 28.7k | module_sp = modules.GetModuleAtIndex(idx); |
229 | 28.7k | if (AppleObjCRuntime::AppleIsModuleObjCLibrary(module_sp)) { |
230 | 957 | m_objc_module_wp = module_sp; |
231 | 957 | return module_sp; |
232 | 957 | } |
233 | 28.7k | } |
234 | 957 | } |
235 | 0 | return ModuleSP(); |
236 | 957 | } |
237 | | |
238 | 158 | Address *AppleObjCRuntime::GetPrintForDebuggerAddr() { |
239 | 158 | if (!m_PrintForDebugger_addr) { |
240 | 64 | const ModuleList &modules = m_process->GetTarget().GetImages(); |
241 | | |
242 | 64 | SymbolContextList contexts; |
243 | 64 | SymbolContext context; |
244 | | |
245 | 64 | modules.FindSymbolsWithNameAndType(ConstString("_NSPrintForDebugger"), |
246 | 64 | eSymbolTypeCode, contexts); |
247 | 64 | if (contexts.IsEmpty()) { |
248 | 12 | modules.FindSymbolsWithNameAndType(ConstString("_CFPrintForDebugger"), |
249 | 12 | eSymbolTypeCode, contexts); |
250 | 12 | if (contexts.IsEmpty()) |
251 | 12 | return nullptr; |
252 | 12 | } |
253 | | |
254 | 52 | contexts.GetContextAtIndex(0, context); |
255 | | |
256 | 52 | m_PrintForDebugger_addr = |
257 | 52 | std::make_unique<Address>(context.symbol->GetAddress()); |
258 | 52 | } |
259 | | |
260 | 146 | return m_PrintForDebugger_addr.get(); |
261 | 158 | } |
262 | | |
263 | 85.9k | bool AppleObjCRuntime::CouldHaveDynamicValue(ValueObject &in_value) { |
264 | 85.9k | return in_value.GetCompilerType().IsPossibleDynamicType( |
265 | 85.9k | nullptr, |
266 | 85.9k | false, // do not check C++ |
267 | 85.9k | true); // check ObjC |
268 | 85.9k | } |
269 | | |
270 | | bool AppleObjCRuntime::GetDynamicTypeAndAddress( |
271 | | ValueObject &in_value, lldb::DynamicValueType use_dynamic, |
272 | | TypeAndOrName &class_type_or_name, Address &address, |
273 | 0 | Value::ValueType &value_type) { |
274 | 0 | return false; |
275 | 0 | } |
276 | | |
277 | | TypeAndOrName |
278 | | AppleObjCRuntime::FixUpDynamicType(const TypeAndOrName &type_and_or_name, |
279 | 2.13k | ValueObject &static_value) { |
280 | 2.13k | CompilerType static_type(static_value.GetCompilerType()); |
281 | 2.13k | Flags static_type_flags(static_type.GetTypeInfo()); |
282 | | |
283 | 2.13k | TypeAndOrName ret(type_and_or_name); |
284 | 2.13k | if (type_and_or_name.HasType()) { |
285 | | // The type will always be the type of the dynamic object. If our parent's |
286 | | // type was a pointer, then our type should be a pointer to the type of the |
287 | | // dynamic object. If a reference, then the original type should be |
288 | | // okay... |
289 | 2.13k | CompilerType orig_type = type_and_or_name.GetCompilerType(); |
290 | 2.13k | CompilerType corrected_type = orig_type; |
291 | 2.13k | if (static_type_flags.AllSet(eTypeIsPointer)) |
292 | 2.13k | corrected_type = orig_type.GetPointerType(); |
293 | 2.13k | ret.SetCompilerType(corrected_type); |
294 | 2.13k | } else { |
295 | | // If we are here we need to adjust our dynamic type name to include the |
296 | | // correct & or * symbol |
297 | 0 | std::string corrected_name(type_and_or_name.GetName().GetCString()); |
298 | 0 | if (static_type_flags.AllSet(eTypeIsPointer)) |
299 | 0 | corrected_name.append(" *"); |
300 | | // the parent type should be a correctly pointer'ed or referenc'ed type |
301 | 0 | ret.SetCompilerType(static_type); |
302 | 0 | ret.SetName(corrected_name.c_str()); |
303 | 0 | } |
304 | 2.13k | return ret; |
305 | 2.13k | } |
306 | | |
307 | 279k | bool AppleObjCRuntime::AppleIsModuleObjCLibrary(const ModuleSP &module_sp) { |
308 | 279k | if (module_sp) { |
309 | 279k | const FileSpec &module_file_spec = module_sp->GetFileSpec(); |
310 | 279k | static ConstString ObjCName("libobjc.A.dylib"); |
311 | | |
312 | 279k | if (module_file_spec) { |
313 | 279k | if (module_file_spec.GetFilename() == ObjCName) |
314 | 5.43k | return true; |
315 | 279k | } |
316 | 279k | } |
317 | 274k | return false; |
318 | 279k | } |
319 | | |
320 | | // we use the version of Foundation to make assumptions about the ObjC runtime |
321 | | // on a target |
322 | 622 | uint32_t AppleObjCRuntime::GetFoundationVersion() { |
323 | 622 | if (!m_Foundation_major) { |
324 | 54 | const ModuleList &modules = m_process->GetTarget().GetImages(); |
325 | 170 | for (uint32_t idx = 0; idx < modules.GetSize(); idx++116 ) { |
326 | 170 | lldb::ModuleSP module_sp = modules.GetModuleAtIndex(idx); |
327 | 170 | if (!module_sp) |
328 | 0 | continue; |
329 | 170 | if (strcmp(module_sp->GetFileSpec().GetFilename().AsCString(""), |
330 | 170 | "Foundation") == 0) { |
331 | 54 | m_Foundation_major = module_sp->GetVersion().getMajor(); |
332 | 54 | return *m_Foundation_major; |
333 | 54 | } |
334 | 170 | } |
335 | 0 | return LLDB_INVALID_MODULE_VERSION; |
336 | 54 | } else |
337 | 568 | return *m_Foundation_major; |
338 | 622 | } |
339 | | |
340 | | void AppleObjCRuntime::GetValuesForGlobalCFBooleans(lldb::addr_t &cf_true, |
341 | 0 | lldb::addr_t &cf_false) { |
342 | 0 | cf_true = cf_false = LLDB_INVALID_ADDRESS; |
343 | 0 | } |
344 | | |
345 | 102k | bool AppleObjCRuntime::IsModuleObjCLibrary(const ModuleSP &module_sp) { |
346 | 102k | return AppleIsModuleObjCLibrary(module_sp); |
347 | 102k | } |
348 | | |
349 | 2.11k | bool AppleObjCRuntime::ReadObjCLibrary(const ModuleSP &module_sp) { |
350 | | // Maybe check here and if we have a handler already, and the UUID of this |
351 | | // module is the same as the one in the current module, then we don't have to |
352 | | // reread it? |
353 | 2.11k | m_objc_trampoline_handler_up = std::make_unique<AppleObjCTrampolineHandler>( |
354 | 2.11k | m_process->shared_from_this(), module_sp); |
355 | 2.11k | if (m_objc_trampoline_handler_up != nullptr) { |
356 | 2.11k | m_read_objc_library = true; |
357 | 2.11k | return true; |
358 | 2.11k | } else |
359 | 0 | return false; |
360 | 2.11k | } |
361 | | |
362 | | ThreadPlanSP AppleObjCRuntime::GetStepThroughTrampolinePlan(Thread &thread, |
363 | 399 | bool stop_others) { |
364 | 399 | ThreadPlanSP thread_plan_sp; |
365 | 399 | if (m_objc_trampoline_handler_up) |
366 | 399 | thread_plan_sp = m_objc_trampoline_handler_up->GetStepThroughDispatchPlan( |
367 | 399 | thread, stop_others); |
368 | 399 | return thread_plan_sp; |
369 | 399 | } |
370 | | |
371 | | // Static Functions |
372 | | ObjCLanguageRuntime::ObjCRuntimeVersions |
373 | 43.1k | AppleObjCRuntime::GetObjCVersion(Process *process, ModuleSP &objc_module_sp) { |
374 | 43.1k | if (!process) |
375 | 0 | return ObjCRuntimeVersions::eObjC_VersionUnknown; |
376 | | |
377 | 43.1k | Target &target = process->GetTarget(); |
378 | 43.1k | if (target.GetArchitecture().GetTriple().getVendor() != |
379 | 43.1k | llvm::Triple::VendorType::Apple) |
380 | 3.15k | return ObjCRuntimeVersions::eObjC_VersionUnknown; |
381 | | |
382 | 148k | for (ModuleSP module_sp : target.GetImages().Modules())39.9k { |
383 | | // One tricky bit here is that we might get called as part of the initial |
384 | | // module loading, but before all the pre-run libraries get winnowed from |
385 | | // the module list. So there might actually be an old and incorrect ObjC |
386 | | // library sitting around in the list, and we don't want to look at that. |
387 | | // That's why we call IsLoadedInTarget. |
388 | | |
389 | 148k | if (AppleIsModuleObjCLibrary(module_sp) && |
390 | 148k | module_sp->IsLoadedInTarget(&target)2.25k ) { |
391 | 2.11k | objc_module_sp = module_sp; |
392 | 2.11k | ObjectFile *ofile = module_sp->GetObjectFile(); |
393 | 2.11k | if (!ofile) |
394 | 0 | return ObjCRuntimeVersions::eObjC_VersionUnknown; |
395 | | |
396 | 2.11k | SectionList *sections = module_sp->GetSectionList(); |
397 | 2.11k | if (!sections) |
398 | 0 | return ObjCRuntimeVersions::eObjC_VersionUnknown; |
399 | 2.11k | SectionSP v1_telltale_section_sp = |
400 | 2.11k | sections->FindSectionByName(ConstString("__OBJC")); |
401 | 2.11k | if (v1_telltale_section_sp) { |
402 | 0 | return ObjCRuntimeVersions::eAppleObjC_V1; |
403 | 0 | } |
404 | 2.11k | return ObjCRuntimeVersions::eAppleObjC_V2; |
405 | 2.11k | } |
406 | 148k | } |
407 | | |
408 | 37.8k | return ObjCRuntimeVersions::eObjC_VersionUnknown; |
409 | 39.9k | } |
410 | | |
411 | 3.36k | void AppleObjCRuntime::SetExceptionBreakpoints() { |
412 | 3.36k | const bool catch_bp = false; |
413 | 3.36k | const bool throw_bp = true; |
414 | 3.36k | const bool is_internal = true; |
415 | | |
416 | 3.36k | if (!m_objc_exception_bp_sp) { |
417 | 986 | m_objc_exception_bp_sp = LanguageRuntime::CreateExceptionBreakpoint( |
418 | 986 | m_process->GetTarget(), GetLanguageType(), catch_bp, throw_bp, |
419 | 986 | is_internal); |
420 | 986 | if (m_objc_exception_bp_sp) |
421 | 986 | m_objc_exception_bp_sp->SetBreakpointKind("ObjC exception"); |
422 | 986 | } else |
423 | 2.37k | m_objc_exception_bp_sp->SetEnabled(true); |
424 | 3.36k | } |
425 | | |
426 | 3.35k | void AppleObjCRuntime::ClearExceptionBreakpoints() { |
427 | 3.35k | if (!m_process) |
428 | 0 | return; |
429 | | |
430 | 3.35k | if (m_objc_exception_bp_sp.get()) { |
431 | 3.35k | m_objc_exception_bp_sp->SetEnabled(false); |
432 | 3.35k | } |
433 | 3.35k | } |
434 | | |
435 | 3.36k | bool AppleObjCRuntime::ExceptionBreakpointsAreSet() { |
436 | 3.36k | return m_objc_exception_bp_sp && m_objc_exception_bp_sp->IsEnabled()2.37k ; |
437 | 3.36k | } |
438 | | |
439 | | bool AppleObjCRuntime::ExceptionBreakpointsExplainStop( |
440 | 71 | lldb::StopInfoSP stop_reason) { |
441 | 71 | if (!m_process) |
442 | 0 | return false; |
443 | | |
444 | 71 | if (!stop_reason || stop_reason->GetStopReason() != eStopReasonBreakpoint) |
445 | 0 | return false; |
446 | | |
447 | 71 | uint64_t break_site_id = stop_reason->GetValue(); |
448 | 71 | return m_process->GetBreakpointSiteList().BreakpointSiteContainsBreakpoint( |
449 | 71 | break_site_id, m_objc_exception_bp_sp->GetID()); |
450 | 71 | } |
451 | | |
452 | 1.04k | bool AppleObjCRuntime::CalculateHasNewLiteralsAndIndexing() { |
453 | 1.04k | if (!m_process) |
454 | 0 | return false; |
455 | | |
456 | 1.04k | Target &target(m_process->GetTarget()); |
457 | | |
458 | 1.04k | static ConstString s_method_signature( |
459 | 1.04k | "-[NSDictionary objectForKeyedSubscript:]"); |
460 | 1.04k | static ConstString s_arclite_method_signature( |
461 | 1.04k | "__arclite_objectForKeyedSubscript"); |
462 | | |
463 | 1.04k | SymbolContextList sc_list; |
464 | | |
465 | 1.04k | target.GetImages().FindSymbolsWithNameAndType(s_method_signature, |
466 | 1.04k | eSymbolTypeCode, sc_list); |
467 | 1.04k | if (sc_list.IsEmpty()) |
468 | 816 | target.GetImages().FindSymbolsWithNameAndType(s_arclite_method_signature, |
469 | 816 | eSymbolTypeCode, sc_list); |
470 | 1.04k | return !sc_list.IsEmpty(); |
471 | 1.04k | } |
472 | | |
473 | 1.00k | lldb::SearchFilterSP AppleObjCRuntime::CreateExceptionSearchFilter() { |
474 | 1.00k | Target &target = m_process->GetTarget(); |
475 | | |
476 | 1.00k | FileSpecList filter_modules; |
477 | 1.00k | if (target.GetArchitecture().GetTriple().getVendor() == llvm::Triple::Apple) { |
478 | 1.00k | filter_modules.Append(std::get<0>(GetExceptionThrowLocation())); |
479 | 1.00k | } |
480 | 1.00k | return target.GetSearchFilterForModuleList(&filter_modules); |
481 | 1.00k | } |
482 | | |
483 | | ValueObjectSP AppleObjCRuntime::GetExceptionObjectForThread( |
484 | 4 | ThreadSP thread_sp) { |
485 | 4 | auto *cpp_runtime = m_process->GetLanguageRuntime(eLanguageTypeC_plus_plus); |
486 | 4 | if (!cpp_runtime) return ValueObjectSP()0 ; |
487 | 4 | auto cpp_exception = cpp_runtime->GetExceptionObjectForThread(thread_sp); |
488 | 4 | if (!cpp_exception) return ValueObjectSP(); |
489 | | |
490 | 0 | auto descriptor = GetClassDescriptor(*cpp_exception); |
491 | 0 | if (!descriptor || !descriptor->IsValid()) return ValueObjectSP(); |
492 | | |
493 | 0 | while (descriptor) { |
494 | 0 | ConstString class_name(descriptor->GetClassName()); |
495 | 0 | if (class_name == "NSException") |
496 | 0 | return cpp_exception; |
497 | 0 | descriptor = descriptor->GetSuperclass(); |
498 | 0 | } |
499 | | |
500 | 0 | return ValueObjectSP(); |
501 | 0 | } |
502 | | |
503 | | /// Utility method for error handling in GetBacktraceThreadFromException. |
504 | | /// \param msg The message to add to the log. |
505 | | /// \return An invalid ThreadSP to be returned from |
506 | | /// GetBacktraceThreadFromException. |
507 | | [[nodiscard]] |
508 | 6 | static ThreadSP FailExceptionParsing(llvm::StringRef msg) { |
509 | 6 | Log *log = GetLog(LLDBLog::Language); |
510 | 6 | LLDB_LOG(log, "Failed getting backtrace from exception: {0}", msg); |
511 | 6 | return ThreadSP(); |
512 | 6 | } |
513 | | |
514 | | ThreadSP AppleObjCRuntime::GetBacktraceThreadFromException( |
515 | 16 | lldb::ValueObjectSP exception_sp) { |
516 | 16 | ValueObjectSP reserved_dict = |
517 | 16 | exception_sp->GetChildMemberWithName("reserved"); |
518 | 16 | if (!reserved_dict) |
519 | 4 | return FailExceptionParsing("Failed to get 'reserved' member."); |
520 | | |
521 | 12 | reserved_dict = reserved_dict->GetSyntheticValue(); |
522 | 12 | if (!reserved_dict) |
523 | 2 | return FailExceptionParsing("Failed to get synthetic value."); |
524 | | |
525 | 10 | TypeSystemClangSP scratch_ts_sp = |
526 | 10 | ScratchTypeSystemClang::GetForTarget(*exception_sp->GetTargetSP()); |
527 | 10 | if (!scratch_ts_sp) |
528 | 0 | return FailExceptionParsing("Failed to get scratch AST."); |
529 | 10 | CompilerType objc_id = scratch_ts_sp->GetBasicType(lldb::eBasicTypeObjCID); |
530 | 10 | ValueObjectSP return_addresses; |
531 | | |
532 | 10 | auto objc_object_from_address = [&exception_sp, &objc_id](uint64_t addr, |
533 | 20 | const char *name) { |
534 | 20 | Value value(addr); |
535 | 20 | value.SetCompilerType(objc_id); |
536 | 20 | auto object = ValueObjectConstResult::Create( |
537 | 20 | exception_sp->GetTargetSP().get(), value, ConstString(name)); |
538 | 20 | object = object->GetDynamicValue(eDynamicDontRunTarget); |
539 | 20 | return object; |
540 | 20 | }; |
541 | | |
542 | 10 | for (size_t idx = 0; idx < reserved_dict->GetNumChildren(); idx++0 ) { |
543 | 10 | ValueObjectSP dict_entry = reserved_dict->GetChildAtIndex(idx); |
544 | | |
545 | 10 | DataExtractor data; |
546 | 10 | data.SetAddressByteSize(dict_entry->GetProcessSP()->GetAddressByteSize()); |
547 | 10 | Status error; |
548 | 10 | dict_entry->GetData(data, error); |
549 | 10 | if (error.Fail()) return ThreadSP()0 ; |
550 | | |
551 | 10 | lldb::offset_t data_offset = 0; |
552 | 10 | auto dict_entry_key = data.GetAddress(&data_offset); |
553 | 10 | auto dict_entry_value = data.GetAddress(&data_offset); |
554 | | |
555 | 10 | auto key_nsstring = objc_object_from_address(dict_entry_key, "key"); |
556 | 10 | StreamString key_summary; |
557 | 10 | if (lldb_private::formatters::NSStringSummaryProvider( |
558 | 10 | *key_nsstring, key_summary, TypeSummaryOptions()) && |
559 | 10 | !key_summary.Empty()) { |
560 | 10 | if (key_summary.GetString() == "\"callStackReturnAddresses\"") { |
561 | 10 | return_addresses = objc_object_from_address(dict_entry_value, |
562 | 10 | "callStackReturnAddresses"); |
563 | 10 | break; |
564 | 10 | } |
565 | 10 | } |
566 | 10 | } |
567 | | |
568 | 10 | if (!return_addresses) |
569 | 0 | return FailExceptionParsing("Failed to get return addresses."); |
570 | 10 | auto frames_value = return_addresses->GetChildMemberWithName("_frames"); |
571 | 10 | if (!frames_value) |
572 | 0 | return FailExceptionParsing("Failed to get frames_value."); |
573 | 10 | addr_t frames_addr = frames_value->GetValueAsUnsigned(0); |
574 | 10 | auto count_value = return_addresses->GetChildMemberWithName("_cnt"); |
575 | 10 | if (!count_value) |
576 | 0 | return FailExceptionParsing("Failed to get count_value."); |
577 | 10 | size_t count = count_value->GetValueAsUnsigned(0); |
578 | 10 | auto ignore_value = return_addresses->GetChildMemberWithName("_ignore"); |
579 | 10 | if (!ignore_value) |
580 | 0 | return FailExceptionParsing("Failed to get ignore_value."); |
581 | 10 | size_t ignore = ignore_value->GetValueAsUnsigned(0); |
582 | | |
583 | 10 | size_t ptr_size = m_process->GetAddressByteSize(); |
584 | 10 | std::vector<lldb::addr_t> pcs; |
585 | 70 | for (size_t idx = 0; idx < count; idx++60 ) { |
586 | 60 | Status error; |
587 | 60 | addr_t pc = m_process->ReadPointerFromMemory( |
588 | 60 | frames_addr + (ignore + idx) * ptr_size, error); |
589 | 60 | pcs.push_back(pc); |
590 | 60 | } |
591 | | |
592 | 10 | if (pcs.empty()) |
593 | 0 | return FailExceptionParsing("Failed to get PC list."); |
594 | | |
595 | 10 | ThreadSP new_thread_sp(new HistoryThread(*m_process, 0, pcs)); |
596 | 10 | m_process->GetExtendedThreadList().AddThread(new_thread_sp); |
597 | 10 | return new_thread_sp; |
598 | 10 | } |
599 | | |
600 | | std::tuple<FileSpec, ConstString> |
601 | 4.11k | AppleObjCRuntime::GetExceptionThrowLocation() { |
602 | 4.11k | return std::make_tuple( |
603 | 4.11k | FileSpec("libobjc.A.dylib"), ConstString("objc_exception_throw")); |
604 | 4.11k | } |
605 | | |
606 | 8.95k | void AppleObjCRuntime::ReadObjCLibraryIfNeeded(const ModuleList &module_list) { |
607 | 8.95k | if (!HasReadObjCLibrary()) { |
608 | 2.11k | std::lock_guard<std::recursive_mutex> guard(module_list.GetMutex()); |
609 | | |
610 | 2.11k | size_t num_modules = module_list.GetSize(); |
611 | 73.6k | for (size_t i = 0; i < num_modules; i++71.5k ) { |
612 | 73.6k | auto mod = module_list.GetModuleAtIndex(i); |
613 | 73.6k | if (IsModuleObjCLibrary(mod)) { |
614 | 2.11k | ReadObjCLibrary(mod); |
615 | 2.11k | break; |
616 | 2.11k | } |
617 | 73.6k | } |
618 | 2.11k | } |
619 | 8.95k | } |
620 | | |
621 | 6.84k | void AppleObjCRuntime::ModulesDidLoad(const ModuleList &module_list) { |
622 | 6.84k | ReadObjCLibraryIfNeeded(module_list); |
623 | 6.84k | } |