1 | //===-- LLVMUserExpression.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/Expression/LLVMUserExpression.h" |
10 | #include "lldb/Core/Module.h" |
11 | #include "lldb/Expression/DiagnosticManager.h" |
12 | #include "lldb/Expression/ExpressionVariable.h" |
13 | #include "lldb/Expression/IRExecutionUnit.h" |
14 | #include "lldb/Expression/IRInterpreter.h" |
15 | #include "lldb/Expression/Materializer.h" |
16 | #include "lldb/Host/HostInfo.h" |
17 | #include "lldb/Symbol/Block.h" |
18 | #include "lldb/Symbol/Function.h" |
19 | #include "lldb/Symbol/ObjectFile.h" |
20 | #include "lldb/Symbol/SymbolVendor.h" |
21 | #include "lldb/Symbol/Type.h" |
22 | #include "lldb/Symbol/VariableList.h" |
23 | #include "lldb/Target/ABI.h" |
24 | #include "lldb/Target/ExecutionContext.h" |
25 | #include "lldb/Target/Process.h" |
26 | #include "lldb/Target/StackFrame.h" |
27 | #include "lldb/Target/Target.h" |
28 | #include "lldb/Target/ThreadPlan.h" |
29 | #include "lldb/Target/ThreadPlanCallUserExpression.h" |
30 | #include "lldb/Utility/ConstString.h" |
31 | #include "lldb/Utility/ErrorMessages.h" |
32 | #include "lldb/Utility/LLDBLog.h" |
33 | #include "lldb/Utility/Log.h" |
34 | #include "lldb/Utility/StreamString.h" |
35 | #include "lldb/ValueObject/ValueObjectConstResult.h" |
36 | |
37 | using namespace lldb; |
38 | using namespace lldb_private; |
39 | |
40 | char LLVMUserExpression::ID; |
41 | |
42 | LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope, |
43 | llvm::StringRef expr, |
44 | llvm::StringRef prefix, |
45 | SourceLanguage language, |
46 | ResultType desired_type, |
47 | const EvaluateExpressionOptions &options) |
48 | : UserExpression(exe_scope, expr, prefix, language, desired_type, options), |
49 | m_stack_frame_bottom(LLDB_INVALID_ADDRESS), |
50 | m_stack_frame_top(LLDB_INVALID_ADDRESS), m_allow_cxx(false), |
51 | m_allow_objc(false), m_transformed_text(), m_execution_unit_sp(), |
52 | m_materializer_up(), m_jit_module_wp(), m_target(nullptr), |
53 | m_can_interpret(false), m_materialized_address(LLDB_INVALID_ADDRESS) {} |
54 | |
55 | LLVMUserExpression::~LLVMUserExpression() { |
56 | if (m_target) { |
57 | lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock()); |
58 | if (jit_module_sp) |
59 | m_target->GetImages().Remove(module_sp: jit_module_sp); |
60 | } |
61 | } |
62 | |
63 | lldb::ExpressionResults |
64 | LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager, |
65 | ExecutionContext &exe_ctx, |
66 | const EvaluateExpressionOptions &options, |
67 | lldb::UserExpressionSP &shared_ptr_to_me, |
68 | lldb::ExpressionVariableSP &result) { |
69 | // The expression log is quite verbose, and if you're just tracking the |
70 | // execution of the expression, it's quite convenient to have these logs come |
71 | // out with the STEP log as well. |
72 | Log *log(GetLog(mask: LLDBLog::Expressions | LLDBLog::Step)); |
73 | |
74 | if (m_jit_start_addr == LLDB_INVALID_ADDRESS && !m_can_interpret) { |
75 | diagnostic_manager.PutString( |
76 | severity: lldb::eSeverityError, |
77 | str: "Expression can't be run, because there is no JIT compiled function" ); |
78 | return lldb::eExpressionSetupError; |
79 | } |
80 | |
81 | lldb::addr_t struct_address = LLDB_INVALID_ADDRESS; |
82 | |
83 | if (!PrepareToExecuteJITExpression(diagnostic_manager, exe_ctx, |
84 | struct_address)) { |
85 | diagnostic_manager.Printf( |
86 | severity: lldb::eSeverityError, |
87 | format: "errored out in %s, couldn't PrepareToExecuteJITExpression" , |
88 | __FUNCTION__); |
89 | return lldb::eExpressionSetupError; |
90 | } |
91 | |
92 | lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS; |
93 | lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS; |
94 | |
95 | if (m_can_interpret) { |
96 | llvm::Module *module = m_execution_unit_sp->GetModule(); |
97 | llvm::Function *function = m_execution_unit_sp->GetFunction(); |
98 | |
99 | if (!module || !function) { |
100 | diagnostic_manager.PutString( |
101 | severity: lldb::eSeverityError, str: "supposed to interpret, but nothing is there" ); |
102 | return lldb::eExpressionSetupError; |
103 | } |
104 | |
105 | Status interpreter_error; |
106 | |
107 | std::vector<lldb::addr_t> args; |
108 | |
109 | if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) { |
110 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
111 | format: "errored out in %s, couldn't AddArguments" , |
112 | __FUNCTION__); |
113 | return lldb::eExpressionSetupError; |
114 | } |
115 | |
116 | function_stack_bottom = m_stack_frame_bottom; |
117 | function_stack_top = m_stack_frame_top; |
118 | |
119 | IRInterpreter::Interpret(module&: *module, function&: *function, args, execution_unit&: *m_execution_unit_sp, |
120 | error&: interpreter_error, stack_frame_bottom: function_stack_bottom, |
121 | stack_frame_top: function_stack_top, exe_ctx, timeout: options.GetTimeout()); |
122 | |
123 | if (!interpreter_error.Success()) { |
124 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
125 | format: "supposed to interpret, but failed: %s" , |
126 | interpreter_error.AsCString()); |
127 | return lldb::eExpressionDiscarded; |
128 | } |
129 | } else { |
130 | if (!exe_ctx.HasThreadScope()) { |
131 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
132 | format: "%s called with no thread selected" , |
133 | __FUNCTION__); |
134 | return lldb::eExpressionSetupError; |
135 | } |
136 | |
137 | // Store away the thread ID for error reporting, in case it exits |
138 | // during execution: |
139 | lldb::tid_t expr_thread_id = exe_ctx.GetThreadRef().GetID(); |
140 | |
141 | Address wrapper_address(m_jit_start_addr); |
142 | |
143 | std::vector<lldb::addr_t> args; |
144 | |
145 | if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) { |
146 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
147 | format: "errored out in %s, couldn't AddArguments" , |
148 | __FUNCTION__); |
149 | return lldb::eExpressionSetupError; |
150 | } |
151 | |
152 | lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression( |
153 | exe_ctx.GetThreadRef(), wrapper_address, args, options, |
154 | shared_ptr_to_me)); |
155 | |
156 | StreamString ss; |
157 | if (!call_plan_sp || !call_plan_sp->ValidatePlan(error: &ss)) { |
158 | diagnostic_manager.PutString(severity: lldb::eSeverityError, str: ss.GetString()); |
159 | return lldb::eExpressionSetupError; |
160 | } |
161 | |
162 | ThreadPlanCallUserExpression *user_expression_plan = |
163 | static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get()); |
164 | |
165 | lldb::addr_t function_stack_pointer = |
166 | user_expression_plan->GetFunctionStackPointer(); |
167 | |
168 | function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize(); |
169 | function_stack_top = function_stack_pointer; |
170 | |
171 | LLDB_LOGF(log, |
172 | "-- [UserExpression::Execute] Execution of expression begins --" ); |
173 | |
174 | if (exe_ctx.GetProcessPtr()) |
175 | exe_ctx.GetProcessPtr()->SetRunningUserExpression(true); |
176 | |
177 | lldb::ExpressionResults execution_result = |
178 | exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, thread_plan_sp&: call_plan_sp, options, |
179 | diagnostic_manager); |
180 | |
181 | if (exe_ctx.GetProcessPtr()) |
182 | exe_ctx.GetProcessPtr()->SetRunningUserExpression(false); |
183 | |
184 | LLDB_LOGF(log, "-- [UserExpression::Execute] Execution of expression " |
185 | "completed --" ); |
186 | |
187 | if (execution_result == lldb::eExpressionInterrupted || |
188 | execution_result == lldb::eExpressionHitBreakpoint) { |
189 | const char *error_desc = nullptr; |
190 | const char *explanation = execution_result == lldb::eExpressionInterrupted |
191 | ? "was interrupted" |
192 | : "hit a breakpoint" ; |
193 | |
194 | if (user_expression_plan) { |
195 | if (auto real_stop_info_sp = user_expression_plan->GetRealStopInfo()) |
196 | error_desc = real_stop_info_sp->GetDescription(); |
197 | } |
198 | |
199 | if (error_desc) |
200 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
201 | format: "Expression execution %s: %s." , explanation, |
202 | error_desc); |
203 | else |
204 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
205 | format: "Expression execution %s." , explanation); |
206 | |
207 | if ((execution_result == lldb::eExpressionInterrupted && |
208 | options.DoesUnwindOnError()) || |
209 | (execution_result == lldb::eExpressionHitBreakpoint && |
210 | options.DoesIgnoreBreakpoints())) |
211 | diagnostic_manager.AppendMessageToDiagnostic( |
212 | str: "The process has been returned to the state before expression " |
213 | "evaluation." ); |
214 | else { |
215 | if (execution_result == lldb::eExpressionHitBreakpoint) |
216 | user_expression_plan->TransferExpressionOwnership(); |
217 | diagnostic_manager.AppendMessageToDiagnostic( |
218 | str: "The process has been left at the point where it was " |
219 | "interrupted, use \"thread return -x\" to return to the state " |
220 | "before expression evaluation." ); |
221 | } |
222 | |
223 | return execution_result; |
224 | } |
225 | |
226 | if (execution_result == lldb::eExpressionStoppedForDebug) { |
227 | diagnostic_manager.PutString( |
228 | severity: lldb::eSeverityInfo, |
229 | str: "Expression execution was halted at the first instruction of the " |
230 | "expression function because \"debug\" was requested.\n" |
231 | "Use \"thread return -x\" to return to the state before expression " |
232 | "evaluation." ); |
233 | return execution_result; |
234 | } |
235 | |
236 | if (execution_result == lldb::eExpressionThreadVanished) { |
237 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
238 | format: "Couldn't execute expression: the thread on " |
239 | "which the expression was being run (0x%" PRIx64 |
240 | ") exited during its execution." , |
241 | expr_thread_id); |
242 | return execution_result; |
243 | } |
244 | |
245 | if (execution_result != lldb::eExpressionCompleted) { |
246 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
247 | format: "Couldn't execute expression: result was %s" , |
248 | toString(e: execution_result).c_str()); |
249 | return execution_result; |
250 | } |
251 | } |
252 | |
253 | if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result, |
254 | function_stack_bottom, function_stack_top)) { |
255 | return lldb::eExpressionCompleted; |
256 | } |
257 | |
258 | return lldb::eExpressionResultUnavailable; |
259 | } |
260 | |
261 | bool LLVMUserExpression::FinalizeJITExecution( |
262 | DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, |
263 | lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom, |
264 | lldb::addr_t function_stack_top) { |
265 | Log *log = GetLog(mask: LLDBLog::Expressions); |
266 | |
267 | LLDB_LOGF(log, "-- [UserExpression::FinalizeJITExecution] Dematerializing " |
268 | "after execution --" ); |
269 | |
270 | if (!m_dematerializer_sp) { |
271 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
272 | format: "Couldn't apply expression side effects : no " |
273 | "dematerializer is present" ); |
274 | return false; |
275 | } |
276 | |
277 | Status dematerialize_error; |
278 | |
279 | m_dematerializer_sp->Dematerialize(err&: dematerialize_error, frame_top: function_stack_bottom, |
280 | frame_bottom: function_stack_top); |
281 | |
282 | if (!dematerialize_error.Success()) { |
283 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
284 | format: "Couldn't apply expression side effects : %s" , |
285 | dematerialize_error.AsCString(default_error_str: "unknown error" )); |
286 | return false; |
287 | } |
288 | |
289 | result = |
290 | GetResultAfterDematerialization(exe_scope: exe_ctx.GetBestExecutionContextScope()); |
291 | |
292 | if (result) |
293 | result->TransferAddress(); |
294 | |
295 | m_dematerializer_sp.reset(); |
296 | |
297 | return true; |
298 | } |
299 | |
300 | bool LLVMUserExpression::PrepareToExecuteJITExpression( |
301 | DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx, |
302 | lldb::addr_t &struct_address) { |
303 | lldb::TargetSP target; |
304 | lldb::ProcessSP process; |
305 | lldb::StackFrameSP frame; |
306 | |
307 | if (!LockAndCheckContext(exe_ctx, target_sp&: target, process_sp&: process, frame_sp&: frame)) { |
308 | diagnostic_manager.PutString( |
309 | severity: lldb::eSeverityError, |
310 | str: "The context has changed before we could JIT the expression!" ); |
311 | return false; |
312 | } |
313 | |
314 | if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) { |
315 | if (m_materialized_address == LLDB_INVALID_ADDRESS) { |
316 | Status alloc_error; |
317 | |
318 | IRMemoryMap::AllocationPolicy policy = |
319 | m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly |
320 | : IRMemoryMap::eAllocationPolicyMirror; |
321 | |
322 | const bool zero_memory = false; |
323 | |
324 | m_materialized_address = m_execution_unit_sp->Malloc( |
325 | size: m_materializer_up->GetStructByteSize(), |
326 | alignment: m_materializer_up->GetStructAlignment(), |
327 | permissions: lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy, |
328 | zero_memory, error&: alloc_error); |
329 | |
330 | if (!alloc_error.Success()) { |
331 | diagnostic_manager.Printf( |
332 | severity: lldb::eSeverityError, |
333 | format: "Couldn't allocate space for materialized struct: %s" , |
334 | alloc_error.AsCString()); |
335 | return false; |
336 | } |
337 | } |
338 | |
339 | struct_address = m_materialized_address; |
340 | |
341 | if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) { |
342 | Status alloc_error; |
343 | |
344 | size_t stack_frame_size = target->GetExprAllocSize(); |
345 | if (stack_frame_size == 0) { |
346 | ABISP abi_sp; |
347 | if (process && (abi_sp = process->GetABI())) |
348 | stack_frame_size = abi_sp->GetStackFrameSize(); |
349 | else |
350 | stack_frame_size = 512 * 1024; |
351 | } |
352 | |
353 | const bool zero_memory = false; |
354 | |
355 | m_stack_frame_bottom = m_execution_unit_sp->Malloc( |
356 | size: stack_frame_size, alignment: 8, |
357 | permissions: lldb::ePermissionsReadable | lldb::ePermissionsWritable, |
358 | policy: IRMemoryMap::eAllocationPolicyHostOnly, zero_memory, error&: alloc_error); |
359 | |
360 | m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; |
361 | |
362 | if (!alloc_error.Success()) { |
363 | diagnostic_manager.Printf( |
364 | severity: lldb::eSeverityError, |
365 | format: "Couldn't allocate space for the stack frame: %s" , |
366 | alloc_error.AsCString()); |
367 | return false; |
368 | } |
369 | } |
370 | |
371 | Status materialize_error; |
372 | |
373 | m_dematerializer_sp = m_materializer_up->Materialize( |
374 | frame_sp&: frame, map&: *m_execution_unit_sp, process_address: struct_address, err&: materialize_error); |
375 | |
376 | if (!materialize_error.Success()) { |
377 | diagnostic_manager.Printf(severity: lldb::eSeverityError, |
378 | format: "Couldn't materialize: %s" , |
379 | materialize_error.AsCString()); |
380 | return false; |
381 | } |
382 | } |
383 | return true; |
384 | } |
385 | |
386 | |