1 | //===-- UserExpression.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 <cstdio> |
10 | #include <sys/types.h> |
11 | |
12 | #include <cstdlib> |
13 | #include <map> |
14 | #include <string> |
15 | |
16 | #include "lldb/Core/Module.h" |
17 | #include "lldb/Core/ValueObjectConstResult.h" |
18 | #include "lldb/Expression/DiagnosticManager.h" |
19 | #include "lldb/Expression/ExpressionVariable.h" |
20 | #include "lldb/Expression/IRExecutionUnit.h" |
21 | #include "lldb/Expression/IRInterpreter.h" |
22 | #include "lldb/Expression/Materializer.h" |
23 | #include "lldb/Expression/UserExpression.h" |
24 | #include "lldb/Host/HostInfo.h" |
25 | #include "lldb/Symbol/Block.h" |
26 | #include "lldb/Symbol/Function.h" |
27 | #include "lldb/Symbol/ObjectFile.h" |
28 | #include "lldb/Symbol/SymbolVendor.h" |
29 | #include "lldb/Symbol/Type.h" |
30 | #include "lldb/Symbol/TypeSystem.h" |
31 | #include "lldb/Symbol/VariableList.h" |
32 | #include "lldb/Target/ExecutionContext.h" |
33 | #include "lldb/Target/Process.h" |
34 | #include "lldb/Target/StackFrame.h" |
35 | #include "lldb/Target/Target.h" |
36 | #include "lldb/Target/ThreadPlan.h" |
37 | #include "lldb/Target/ThreadPlanCallUserExpression.h" |
38 | #include "lldb/Utility/LLDBLog.h" |
39 | #include "lldb/Utility/Log.h" |
40 | #include "lldb/Utility/State.h" |
41 | #include "lldb/Utility/StreamString.h" |
42 | |
43 | using namespace lldb_private; |
44 | |
45 | char UserExpression::ID; |
46 | |
47 | UserExpression::UserExpression(ExecutionContextScope &exe_scope, |
48 | llvm::StringRef expr, llvm::StringRef prefix, |
49 | lldb::LanguageType language, |
50 | ResultType desired_type, |
51 | const EvaluateExpressionOptions &options) |
52 | : Expression(exe_scope), m_expr_text(std::string(expr)), |
53 | m_expr_prefix(std::string(prefix)), m_language(language), |
54 | m_desired_type(desired_type), m_options(options) {} |
55 | |
56 | UserExpression::~UserExpression() = default; |
57 | |
58 | void UserExpression::InstallContext(ExecutionContext &exe_ctx) { |
59 | m_jit_process_wp = exe_ctx.GetProcessSP(); |
60 | |
61 | lldb::StackFrameSP frame_sp = exe_ctx.GetFrameSP(); |
62 | |
63 | if (frame_sp) |
64 | m_address = frame_sp->GetFrameCodeAddress(); |
65 | } |
66 | |
67 | bool UserExpression::LockAndCheckContext(ExecutionContext &exe_ctx, |
68 | lldb::TargetSP &target_sp, |
69 | lldb::ProcessSP &process_sp, |
70 | lldb::StackFrameSP &frame_sp) { |
71 | lldb::ProcessSP expected_process_sp = m_jit_process_wp.lock(); |
72 | process_sp = exe_ctx.GetProcessSP(); |
73 | |
74 | if (process_sp != expected_process_sp) |
75 | return false; |
76 | |
77 | process_sp = exe_ctx.GetProcessSP(); |
78 | target_sp = exe_ctx.GetTargetSP(); |
79 | frame_sp = exe_ctx.GetFrameSP(); |
80 | |
81 | if (m_address.IsValid()) { |
82 | if (!frame_sp) |
83 | return false; |
84 | return (Address::CompareLoadAddress(lhs: m_address, |
85 | rhs: frame_sp->GetFrameCodeAddress(), |
86 | target: target_sp.get()) == 0); |
87 | } |
88 | |
89 | return true; |
90 | } |
91 | |
92 | bool UserExpression::MatchesContext(ExecutionContext &exe_ctx) { |
93 | lldb::TargetSP target_sp; |
94 | lldb::ProcessSP process_sp; |
95 | lldb::StackFrameSP frame_sp; |
96 | |
97 | return LockAndCheckContext(exe_ctx, target_sp, process_sp, frame_sp); |
98 | } |
99 | |
100 | lldb::ValueObjectSP UserExpression::GetObjectPointerValueObject( |
101 | lldb::StackFrameSP frame_sp, llvm::StringRef object_name, Status &err) { |
102 | err.Clear(); |
103 | |
104 | if (!frame_sp) { |
105 | err.SetErrorStringWithFormatv( |
106 | format: "Couldn't load '{0}' because the context is incomplete" , args&: object_name); |
107 | return {}; |
108 | } |
109 | |
110 | lldb::VariableSP var_sp; |
111 | lldb::ValueObjectSP valobj_sp; |
112 | |
113 | return frame_sp->GetValueForVariableExpressionPath( |
114 | var_expr: object_name, use_dynamic: lldb::eNoDynamicValues, |
115 | options: StackFrame::eExpressionPathOptionCheckPtrVsMember | |
116 | StackFrame::eExpressionPathOptionsNoFragileObjcIvar | |
117 | StackFrame::eExpressionPathOptionsNoSyntheticChildren | |
118 | StackFrame::eExpressionPathOptionsNoSyntheticArrayRange, |
119 | var_sp, error&: err); |
120 | } |
121 | |
122 | lldb::addr_t UserExpression::GetObjectPointer(lldb::StackFrameSP frame_sp, |
123 | llvm::StringRef object_name, |
124 | Status &err) { |
125 | auto valobj_sp = |
126 | GetObjectPointerValueObject(frame_sp: std::move(frame_sp), object_name, err); |
127 | |
128 | if (!err.Success() || !valobj_sp.get()) |
129 | return LLDB_INVALID_ADDRESS; |
130 | |
131 | lldb::addr_t ret = valobj_sp->GetValueAsUnsigned(LLDB_INVALID_ADDRESS); |
132 | |
133 | if (ret == LLDB_INVALID_ADDRESS) { |
134 | err.SetErrorStringWithFormatv( |
135 | format: "Couldn't load '{0}' because its value couldn't be evaluated" , |
136 | args&: object_name); |
137 | return LLDB_INVALID_ADDRESS; |
138 | } |
139 | |
140 | return ret; |
141 | } |
142 | |
143 | lldb::ExpressionResults |
144 | UserExpression::Evaluate(ExecutionContext &exe_ctx, |
145 | const EvaluateExpressionOptions &options, |
146 | llvm::StringRef expr, llvm::StringRef prefix, |
147 | lldb::ValueObjectSP &result_valobj_sp, Status &error, |
148 | std::string *fixed_expression, ValueObject *ctx_obj) { |
149 | Log *log(GetLog(mask: LLDBLog::Expressions | LLDBLog::Step)); |
150 | |
151 | if (ctx_obj) { |
152 | static unsigned const ctx_type_mask = lldb::TypeFlags::eTypeIsClass | |
153 | lldb::TypeFlags::eTypeIsStructUnion | |
154 | lldb::TypeFlags::eTypeIsReference; |
155 | if (!(ctx_obj->GetTypeInfo() & ctx_type_mask)) { |
156 | LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of " |
157 | "an invalid type, can't run expressions." ); |
158 | error.SetErrorString("a context object of an invalid type passed" ); |
159 | return lldb::eExpressionSetupError; |
160 | } |
161 | } |
162 | |
163 | if (ctx_obj && ctx_obj->GetTypeInfo() & lldb::TypeFlags::eTypeIsReference) { |
164 | Status error; |
165 | lldb::ValueObjectSP deref_ctx_sp = ctx_obj->Dereference(error); |
166 | if (!error.Success()) { |
167 | LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a context object of " |
168 | "a reference type that can't be dereferenced, can't run " |
169 | "expressions." ); |
170 | error.SetErrorString( |
171 | "passed context object of an reference type cannot be deferenced" ); |
172 | return lldb::eExpressionSetupError; |
173 | } |
174 | |
175 | ctx_obj = deref_ctx_sp.get(); |
176 | } |
177 | |
178 | lldb_private::ExecutionPolicy execution_policy = options.GetExecutionPolicy(); |
179 | lldb::LanguageType language = options.GetLanguage(); |
180 | const ResultType desired_type = options.DoesCoerceToId() |
181 | ? UserExpression::eResultTypeId |
182 | : UserExpression::eResultTypeAny; |
183 | lldb::ExpressionResults execution_results = lldb::eExpressionSetupError; |
184 | |
185 | Target *target = exe_ctx.GetTargetPtr(); |
186 | if (!target) { |
187 | LLDB_LOG(log, "== [UserExpression::Evaluate] Passed a NULL target, can't " |
188 | "run expressions." ); |
189 | error.SetErrorString("expression passed a null target" ); |
190 | return lldb::eExpressionSetupError; |
191 | } |
192 | |
193 | Process *process = exe_ctx.GetProcessPtr(); |
194 | |
195 | if (process == nullptr && execution_policy == eExecutionPolicyAlways) { |
196 | LLDB_LOG(log, "== [UserExpression::Evaluate] No process, but the policy is " |
197 | "eExecutionPolicyAlways" ); |
198 | |
199 | error.SetErrorString("expression needed to run but couldn't: no process" ); |
200 | |
201 | return execution_results; |
202 | } |
203 | |
204 | // Since we might need to allocate memory, we need to be stopped to run |
205 | // an expression. |
206 | if (process != nullptr && process->GetState() != lldb::eStateStopped) { |
207 | error.SetErrorStringWithFormatv( |
208 | format: "unable to evaluate expression while the process is {0}: the process " |
209 | "must be stopped because the expression might require allocating " |
210 | "memory." , |
211 | args: StateAsCString(state: process->GetState())); |
212 | return execution_results; |
213 | } |
214 | |
215 | // Explicitly force the IR interpreter to evaluate the expression when the |
216 | // there is no process that supports running the expression for us. Don't |
217 | // change the execution policy if we have the special top-level policy that |
218 | // doesn't contain any expression and there is nothing to interpret. |
219 | if (execution_policy != eExecutionPolicyTopLevel && |
220 | (process == nullptr || !process->CanJIT())) |
221 | execution_policy = eExecutionPolicyNever; |
222 | |
223 | // We need to set the expression execution thread here, turns out parse can |
224 | // call functions in the process of looking up symbols, which will escape the |
225 | // context set by exe_ctx passed to Execute. |
226 | lldb::ThreadSP thread_sp = exe_ctx.GetThreadSP(); |
227 | ThreadList::ExpressionExecutionThreadPusher execution_thread_pusher( |
228 | thread_sp); |
229 | |
230 | llvm::StringRef full_prefix; |
231 | llvm::StringRef option_prefix(options.GetPrefix()); |
232 | std::string full_prefix_storage; |
233 | if (!prefix.empty() && !option_prefix.empty()) { |
234 | full_prefix_storage = std::string(prefix); |
235 | full_prefix_storage.append(str: std::string(option_prefix)); |
236 | full_prefix = full_prefix_storage; |
237 | } else if (!prefix.empty()) |
238 | full_prefix = prefix; |
239 | else |
240 | full_prefix = option_prefix; |
241 | |
242 | // If the language was not specified in the expression command, set it to the |
243 | // language in the target's properties if specified, else default to the |
244 | // langage for the frame. |
245 | if (language == lldb::eLanguageTypeUnknown) { |
246 | if (target->GetLanguage() != lldb::eLanguageTypeUnknown) |
247 | language = target->GetLanguage(); |
248 | else if (StackFrame *frame = exe_ctx.GetFramePtr()) |
249 | language = frame->GetLanguage(); |
250 | } |
251 | |
252 | lldb::UserExpressionSP user_expression_sp( |
253 | target->GetUserExpressionForLanguage(expr, prefix: full_prefix, language, |
254 | desired_type, options, ctx_obj, |
255 | error)); |
256 | if (error.Fail() || !user_expression_sp) { |
257 | LLDB_LOG(log, "== [UserExpression::Evaluate] Getting expression: {0} ==" , |
258 | error.AsCString()); |
259 | return lldb::eExpressionSetupError; |
260 | } |
261 | |
262 | LLDB_LOG(log, "== [UserExpression::Evaluate] Parsing expression {0} ==" , |
263 | expr.str()); |
264 | |
265 | const bool keep_expression_in_memory = true; |
266 | const bool generate_debug_info = options.GetGenerateDebugInfo(); |
267 | |
268 | if (options.InvokeCancelCallback(phase: lldb::eExpressionEvaluationParse)) { |
269 | error.SetErrorString("expression interrupted by callback before parse" ); |
270 | result_valobj_sp = ValueObjectConstResult::Create( |
271 | exe_scope: exe_ctx.GetBestExecutionContextScope(), error); |
272 | return lldb::eExpressionInterrupted; |
273 | } |
274 | |
275 | DiagnosticManager diagnostic_manager; |
276 | |
277 | bool parse_success = |
278 | user_expression_sp->Parse(diagnostic_manager, exe_ctx, execution_policy, |
279 | keep_result_in_memory: keep_expression_in_memory, generate_debug_info); |
280 | |
281 | // Calculate the fixed expression always, since we need it for errors. |
282 | std::string tmp_fixed_expression; |
283 | if (fixed_expression == nullptr) |
284 | fixed_expression = &tmp_fixed_expression; |
285 | |
286 | *fixed_expression = user_expression_sp->GetFixedText().str(); |
287 | |
288 | // If there is a fixed expression, try to parse it: |
289 | if (!parse_success) { |
290 | // Delete the expression that failed to parse before attempting to parse |
291 | // the next expression. |
292 | user_expression_sp.reset(); |
293 | |
294 | execution_results = lldb::eExpressionParseError; |
295 | if (!fixed_expression->empty() && options.GetAutoApplyFixIts()) { |
296 | const uint64_t max_fix_retries = options.GetRetriesWithFixIts(); |
297 | for (uint64_t i = 0; i < max_fix_retries; ++i) { |
298 | // Try parsing the fixed expression. |
299 | lldb::UserExpressionSP fixed_expression_sp( |
300 | target->GetUserExpressionForLanguage( |
301 | expr: fixed_expression->c_str(), prefix: full_prefix, language, desired_type, |
302 | options, ctx_obj, error)); |
303 | DiagnosticManager fixed_diagnostic_manager; |
304 | parse_success = fixed_expression_sp->Parse( |
305 | diagnostic_manager&: fixed_diagnostic_manager, exe_ctx, execution_policy, |
306 | keep_result_in_memory: keep_expression_in_memory, generate_debug_info); |
307 | if (parse_success) { |
308 | diagnostic_manager.Clear(); |
309 | user_expression_sp = fixed_expression_sp; |
310 | break; |
311 | } else { |
312 | // The fixed expression also didn't parse. Let's check for any new |
313 | // Fix-Its we could try. |
314 | if (!fixed_expression_sp->GetFixedText().empty()) { |
315 | *fixed_expression = fixed_expression_sp->GetFixedText().str(); |
316 | } else { |
317 | // Fixed expression didn't compile without a fixit, don't retry and |
318 | // don't tell the user about it. |
319 | fixed_expression->clear(); |
320 | break; |
321 | } |
322 | } |
323 | } |
324 | } |
325 | |
326 | if (!parse_success) { |
327 | std::string msg; |
328 | { |
329 | llvm::raw_string_ostream os(msg); |
330 | if (!diagnostic_manager.Diagnostics().empty()) |
331 | os << diagnostic_manager.GetString(); |
332 | else |
333 | os << "expression failed to parse (no further compiler diagnostics)" ; |
334 | if (target->GetEnableNotifyAboutFixIts() && fixed_expression && |
335 | !fixed_expression->empty()) |
336 | os << "\nfixed expression suggested:\n " << *fixed_expression; |
337 | } |
338 | error.SetExpressionError(execution_results, mssg: msg.c_str()); |
339 | } |
340 | } |
341 | |
342 | if (parse_success) { |
343 | lldb::ExpressionVariableSP expr_result; |
344 | |
345 | if (execution_policy == eExecutionPolicyNever && |
346 | !user_expression_sp->CanInterpret()) { |
347 | LLDB_LOG(log, "== [UserExpression::Evaluate] Expression may not run, but " |
348 | "is not constant ==" ); |
349 | |
350 | if (!diagnostic_manager.Diagnostics().size()) |
351 | error.SetExpressionError(lldb::eExpressionSetupError, |
352 | mssg: "expression needed to run but couldn't" ); |
353 | } else if (execution_policy == eExecutionPolicyTopLevel) { |
354 | error.SetError(err: UserExpression::kNoResult, type: lldb::eErrorTypeGeneric); |
355 | return lldb::eExpressionCompleted; |
356 | } else { |
357 | if (options.InvokeCancelCallback(phase: lldb::eExpressionEvaluationExecution)) { |
358 | error.SetExpressionError( |
359 | lldb::eExpressionInterrupted, |
360 | mssg: "expression interrupted by callback before execution" ); |
361 | result_valobj_sp = ValueObjectConstResult::Create( |
362 | exe_scope: exe_ctx.GetBestExecutionContextScope(), error); |
363 | return lldb::eExpressionInterrupted; |
364 | } |
365 | |
366 | diagnostic_manager.Clear(); |
367 | |
368 | LLDB_LOG(log, "== [UserExpression::Evaluate] Executing expression ==" ); |
369 | |
370 | execution_results = |
371 | user_expression_sp->Execute(diagnostic_manager, exe_ctx, options, |
372 | shared_ptr_to_me&: user_expression_sp, result&: expr_result); |
373 | |
374 | if (execution_results != lldb::eExpressionCompleted) { |
375 | LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed " |
376 | "abnormally ==" ); |
377 | |
378 | if (!diagnostic_manager.Diagnostics().size()) |
379 | error.SetExpressionError( |
380 | execution_results, mssg: "expression failed to execute, unknown error" ); |
381 | else |
382 | error.SetExpressionError(execution_results, |
383 | mssg: diagnostic_manager.GetString().c_str()); |
384 | } else { |
385 | if (expr_result) { |
386 | result_valobj_sp = expr_result->GetValueObject(); |
387 | result_valobj_sp->SetPreferredDisplayLanguage(language); |
388 | |
389 | LLDB_LOG(log, |
390 | "== [UserExpression::Evaluate] Execution completed " |
391 | "normally with result {0} ==" , |
392 | result_valobj_sp->GetValueAsCString()); |
393 | } else { |
394 | LLDB_LOG(log, "== [UserExpression::Evaluate] Execution completed " |
395 | "normally with no result ==" ); |
396 | |
397 | error.SetError(err: UserExpression::kNoResult, type: lldb::eErrorTypeGeneric); |
398 | } |
399 | } |
400 | } |
401 | } |
402 | |
403 | if (options.InvokeCancelCallback(phase: lldb::eExpressionEvaluationComplete)) { |
404 | error.SetExpressionError( |
405 | lldb::eExpressionInterrupted, |
406 | mssg: "expression interrupted by callback after complete" ); |
407 | return lldb::eExpressionInterrupted; |
408 | } |
409 | |
410 | if (result_valobj_sp.get() == nullptr) { |
411 | result_valobj_sp = ValueObjectConstResult::Create( |
412 | exe_scope: exe_ctx.GetBestExecutionContextScope(), error); |
413 | } |
414 | |
415 | return execution_results; |
416 | } |
417 | |
418 | lldb::ExpressionResults |
419 | UserExpression::Execute(DiagnosticManager &diagnostic_manager, |
420 | ExecutionContext &exe_ctx, |
421 | const EvaluateExpressionOptions &options, |
422 | lldb::UserExpressionSP &shared_ptr_to_me, |
423 | lldb::ExpressionVariableSP &result_var) { |
424 | lldb::ExpressionResults expr_result = DoExecute( |
425 | diagnostic_manager, exe_ctx, options, shared_ptr_to_me, result&: result_var); |
426 | Target *target = exe_ctx.GetTargetPtr(); |
427 | if (options.GetSuppressPersistentResult() && result_var && target) { |
428 | if (auto *persistent_state = |
429 | target->GetPersistentExpressionStateForLanguage(language: m_language)) |
430 | persistent_state->RemovePersistentVariable(variable: result_var); |
431 | } |
432 | return expr_result; |
433 | } |
434 | |