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