1 //===-- REPL.cpp ------------------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "lldb/Expression/REPL.h"
11 #include "lldb/Core/Debugger.h"
12 #include "lldb/Core/PluginManager.h"
13 #include "lldb/Core/StreamFile.h"
14 #include "lldb/Expression/ExpressionVariable.h"
15 #include "lldb/Expression/UserExpression.h"
16 #include "lldb/Host/HostInfo.h"
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/Utility/AnsiTerminal.h"
22
23 using namespace lldb_private;
24
REPL(LLVMCastKind kind,Target & target)25 REPL::REPL(LLVMCastKind kind, Target &target) : m_target(target), m_kind(kind) {
26 // Make sure all option values have sane defaults
27 Debugger &debugger = m_target.GetDebugger();
28 auto exe_ctx = debugger.GetCommandInterpreter().GetExecutionContext();
29 m_format_options.OptionParsingStarting(&exe_ctx);
30 m_varobj_options.OptionParsingStarting(&exe_ctx);
31 m_command_options.OptionParsingStarting(&exe_ctx);
32
33 // Default certain settings for REPL regardless of the global settings.
34 m_command_options.unwind_on_error = false;
35 m_command_options.ignore_breakpoints = false;
36 m_command_options.debug = false;
37 }
38
39 REPL::~REPL() = default;
40
Create(Status & err,lldb::LanguageType language,Debugger * debugger,Target * target,const char * repl_options)41 lldb::REPLSP REPL::Create(Status &err, lldb::LanguageType language,
42 Debugger *debugger, Target *target,
43 const char *repl_options) {
44 uint32_t idx = 0;
45 lldb::REPLSP ret;
46
47 while (REPLCreateInstance create_instance =
48 PluginManager::GetREPLCreateCallbackAtIndex(idx++)) {
49 ret = (*create_instance)(err, language, debugger, target, repl_options);
50 if (ret) {
51 break;
52 }
53 }
54
55 return ret;
56 }
57
GetSourcePath()58 std::string REPL::GetSourcePath() {
59 ConstString file_basename = GetSourceFileBasename();
60 FileSpec tmpdir_file_spec = HostInfo::GetProcessTempDir();
61 if (tmpdir_file_spec) {
62 tmpdir_file_spec.GetFilename().SetCString(file_basename.AsCString());
63 m_repl_source_path = tmpdir_file_spec.GetPath();
64 } else {
65 tmpdir_file_spec = FileSpec("/tmp");
66 tmpdir_file_spec.AppendPathComponent(file_basename.AsCString());
67 }
68
69 return tmpdir_file_spec.GetPath();
70 }
71
GetIOHandler()72 lldb::IOHandlerSP REPL::GetIOHandler() {
73 if (!m_io_handler_sp) {
74 Debugger &debugger = m_target.GetDebugger();
75 m_io_handler_sp.reset(
76 new IOHandlerEditline(debugger, IOHandler::Type::REPL,
77 "lldb-repl", // Name of input reader for history
78 llvm::StringRef("> "), // prompt
79 llvm::StringRef(". "), // Continuation prompt
80 true, // Multi-line
81 true, // The REPL prompt is always colored
82 1, // Line number
83 *this));
84
85 // Don't exit if CTRL+C is pressed
86 static_cast<IOHandlerEditline *>(m_io_handler_sp.get())
87 ->SetInterruptExits(false);
88
89 if (m_io_handler_sp->GetIsInteractive() &&
90 m_io_handler_sp->GetIsRealTerminal()) {
91 m_indent_str.assign(debugger.GetTabSize(), ' ');
92 m_enable_auto_indent = debugger.GetAutoIndent();
93 } else {
94 m_indent_str.clear();
95 m_enable_auto_indent = false;
96 }
97 }
98 return m_io_handler_sp;
99 }
100
IOHandlerActivated(IOHandler & io_handler)101 void REPL::IOHandlerActivated(IOHandler &io_handler) {
102 lldb::ProcessSP process_sp = m_target.GetProcessSP();
103 if (process_sp && process_sp->IsAlive())
104 return;
105 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
106 error_sp->Printf("REPL requires a running target process.\n");
107 io_handler.SetIsDone(true);
108 }
109
IOHandlerInterrupt(IOHandler & io_handler)110 bool REPL::IOHandlerInterrupt(IOHandler &io_handler) { return false; }
111
IOHandlerInputInterrupted(IOHandler & io_handler,std::string & line)112 void REPL::IOHandlerInputInterrupted(IOHandler &io_handler, std::string &line) {
113 }
114
IOHandlerGetFixIndentationCharacters()115 const char *REPL::IOHandlerGetFixIndentationCharacters() {
116 return (m_enable_auto_indent ? GetAutoIndentCharacters() : nullptr);
117 }
118
IOHandlerGetControlSequence(char ch)119 ConstString REPL::IOHandlerGetControlSequence(char ch) {
120 if (ch == 'd')
121 return ConstString(":quit\n");
122 return ConstString();
123 }
124
IOHandlerGetCommandPrefix()125 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
126
IOHandlerGetHelpPrologue()127 const char *REPL::IOHandlerGetHelpPrologue() {
128 return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
129 "Valid statements, expressions, and declarations are immediately "
130 "compiled and executed.\n\n"
131 "The complete set of LLDB debugging commands are also available as "
132 "described below. Commands "
133 "must be prefixed with a colon at the REPL prompt (:quit for "
134 "example.) Typing just a colon "
135 "followed by return will switch to the LLDB prompt.\n\n";
136 }
137
IOHandlerIsInputComplete(IOHandler & io_handler,StringList & lines)138 bool REPL::IOHandlerIsInputComplete(IOHandler &io_handler, StringList &lines) {
139 // Check for meta command
140 const size_t num_lines = lines.GetSize();
141 if (num_lines == 1) {
142 const char *first_line = lines.GetStringAtIndex(0);
143 if (first_line[0] == ':')
144 return true; // Meta command is a single line where that starts with ':'
145 }
146
147 // Check if REPL input is done
148 std::string source_string(lines.CopyList());
149 return SourceIsComplete(source_string);
150 }
151
CalculateActualIndentation(const StringList & lines)152 int REPL::CalculateActualIndentation(const StringList &lines) {
153 std::string last_line = lines[lines.GetSize() - 1];
154
155 int actual_indent = 0;
156 for (char &ch : last_line) {
157 if (ch != ' ')
158 break;
159 ++actual_indent;
160 }
161
162 return actual_indent;
163 }
164
IOHandlerFixIndentation(IOHandler & io_handler,const StringList & lines,int cursor_position)165 int REPL::IOHandlerFixIndentation(IOHandler &io_handler,
166 const StringList &lines,
167 int cursor_position) {
168 if (!m_enable_auto_indent)
169 return 0;
170
171 if (!lines.GetSize()) {
172 return 0;
173 }
174
175 int tab_size = io_handler.GetDebugger().GetTabSize();
176
177 lldb::offset_t desired_indent =
178 GetDesiredIndentation(lines, cursor_position, tab_size);
179
180 int actual_indent = REPL::CalculateActualIndentation(lines);
181
182 if (desired_indent == LLDB_INVALID_OFFSET)
183 return 0;
184
185 return (int)desired_indent - actual_indent;
186 }
187
IOHandlerInputComplete(IOHandler & io_handler,std::string & code)188 void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) {
189 lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile());
190 lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile());
191 bool extra_line = false;
192 bool did_quit = false;
193
194 if (code.empty()) {
195 m_code.AppendString("");
196 static_cast<IOHandlerEditline &>(io_handler)
197 .SetBaseLineNumber(m_code.GetSize() + 1);
198 } else {
199 Debugger &debugger = m_target.GetDebugger();
200 CommandInterpreter &ci = debugger.GetCommandInterpreter();
201 extra_line = ci.GetSpaceReplPrompts();
202
203 ExecutionContext exe_ctx(m_target.GetProcessSP()
204 ->GetThreadList()
205 .GetSelectedThread()
206 ->GetSelectedFrame()
207 .get());
208
209 lldb::ProcessSP process_sp(exe_ctx.GetProcessSP());
210
211 if (code[0] == ':') {
212 // Meta command
213 // Strip the ':'
214 code.erase(0, 1);
215 if (Args::StripSpaces(code)) {
216 // "lldb" was followed by arguments, so just execute the command dump
217 // the results
218
219 // Turn off prompt on quit in case the user types ":quit"
220 const bool saved_prompt_on_quit = ci.GetPromptOnQuit();
221 if (saved_prompt_on_quit)
222 ci.SetPromptOnQuit(false);
223
224 // Execute the command
225 CommandReturnObject result;
226 result.SetImmediateOutputStream(output_sp);
227 result.SetImmediateErrorStream(error_sp);
228 ci.HandleCommand(code.c_str(), eLazyBoolNo, result);
229
230 if (saved_prompt_on_quit)
231 ci.SetPromptOnQuit(true);
232
233 if (result.GetStatus() == lldb::eReturnStatusQuit) {
234 did_quit = true;
235 io_handler.SetIsDone(true);
236 if (debugger.CheckTopIOHandlerTypes(
237 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
238 // We typed "quit" or an alias to quit so we need to check if the
239 // command interpreter is above us and tell it that it is done as
240 // well so we don't drop back into the command interpreter if we
241 // have already quit
242 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
243 if (io_handler_sp)
244 io_handler_sp->SetIsDone(true);
245 }
246 }
247 } else {
248 // ":" was followed by no arguments, so push the LLDB command prompt
249 if (debugger.CheckTopIOHandlerTypes(
250 IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) {
251 // If the user wants to get back to the command interpreter and the
252 // command interpreter is what launched the REPL, then just let the
253 // REPL exit and fall back to the command interpreter.
254 io_handler.SetIsDone(true);
255 } else {
256 // The REPL wasn't launched the by the command interpreter, it is the
257 // base IOHandler, so we need to get the command interpreter and
258 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
259 if (io_handler_sp) {
260 io_handler_sp->SetIsDone(false);
261 debugger.PushIOHandler(ci.GetIOHandler());
262 }
263 }
264 }
265 } else {
266 // Unwind any expression we might have been running in case our REPL
267 // expression crashed and the user was looking around
268 if (m_dedicated_repl_mode) {
269 Thread *thread = exe_ctx.GetThreadPtr();
270 if (thread && thread->UnwindInnermostExpression().Success()) {
271 thread->SetSelectedFrameByIndex(0, false);
272 exe_ctx.SetFrameSP(thread->GetSelectedFrame());
273 }
274 }
275
276 const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors();
277
278 EvaluateExpressionOptions expr_options;
279 expr_options.SetCoerceToId(m_varobj_options.use_objc);
280 expr_options.SetUnwindOnError(m_command_options.unwind_on_error);
281 expr_options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints);
282 expr_options.SetKeepInMemory(true);
283 expr_options.SetUseDynamic(m_varobj_options.use_dynamic);
284 expr_options.SetTryAllThreads(m_command_options.try_all_threads);
285 expr_options.SetGenerateDebugInfo(true);
286 expr_options.SetREPLEnabled(true);
287 expr_options.SetColorizeErrors(colorize_err);
288 expr_options.SetPoundLine(m_repl_source_path.c_str(),
289 m_code.GetSize() + 1);
290 if (m_command_options.timeout > 0)
291 expr_options.SetTimeout(std::chrono::microseconds(m_command_options.timeout));
292 else
293 expr_options.SetTimeout(llvm::None);
294
295 expr_options.SetLanguage(GetLanguage());
296
297 PersistentExpressionState *persistent_state =
298 m_target.GetPersistentExpressionStateForLanguage(GetLanguage());
299
300 const size_t var_count_before = persistent_state->GetSize();
301
302 const char *expr_prefix = nullptr;
303 lldb::ValueObjectSP result_valobj_sp;
304 Status error;
305 lldb::ModuleSP jit_module_sp;
306 lldb::ExpressionResults execution_results =
307 UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(),
308 expr_prefix, result_valobj_sp, error,
309 0, // Line offset
310 nullptr, // Fixed Expression
311 &jit_module_sp);
312
313 // CommandInterpreter &ci = debugger.GetCommandInterpreter();
314
315 if (process_sp && process_sp->IsAlive()) {
316 bool add_to_code = true;
317 bool handled = false;
318 if (result_valobj_sp) {
319 lldb::Format format = m_format_options.GetFormat();
320
321 if (result_valobj_sp->GetError().Success()) {
322 handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp);
323 } else if (result_valobj_sp->GetError().GetError() ==
324 UserExpression::kNoResult) {
325 if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) {
326 error_sp->PutCString("(void)\n");
327 handled = true;
328 }
329 }
330 }
331
332 if (debugger.GetPrintDecls()) {
333 for (size_t vi = var_count_before, ve = persistent_state->GetSize();
334 vi != ve; ++vi) {
335 lldb::ExpressionVariableSP persistent_var_sp =
336 persistent_state->GetVariableAtIndex(vi);
337 lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject();
338
339 PrintOneVariable(debugger, output_sp, valobj_sp,
340 persistent_var_sp.get());
341 }
342 }
343
344 if (!handled) {
345 bool useColors = error_sp->GetFile().GetIsTerminalWithColors();
346 switch (execution_results) {
347 case lldb::eExpressionSetupError:
348 case lldb::eExpressionParseError:
349 add_to_code = false;
350 LLVM_FALLTHROUGH;
351 case lldb::eExpressionDiscarded:
352 error_sp->Printf("%s\n", error.AsCString());
353 break;
354
355 case lldb::eExpressionCompleted:
356 break;
357 case lldb::eExpressionInterrupted:
358 if (useColors) {
359 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
360 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
361 }
362 error_sp->Printf("Execution interrupted. ");
363 if (useColors)
364 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
365 error_sp->Printf("Enter code to recover and continue.\nEnter LLDB "
366 "commands to investigate (type :help for "
367 "assistance.)\n");
368 break;
369
370 case lldb::eExpressionHitBreakpoint:
371 // Breakpoint was hit, drop into LLDB command interpreter
372 if (useColors) {
373 error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED));
374 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD));
375 }
376 output_sp->Printf("Execution stopped at breakpoint. ");
377 if (useColors)
378 error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL));
379 output_sp->Printf("Enter LLDB commands to investigate (type help "
380 "for assistance.)\n");
381 {
382 lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler());
383 if (io_handler_sp) {
384 io_handler_sp->SetIsDone(false);
385 debugger.PushIOHandler(ci.GetIOHandler());
386 }
387 }
388 break;
389
390 case lldb::eExpressionTimedOut:
391 error_sp->Printf("error: timeout\n");
392 if (error.AsCString())
393 error_sp->Printf("error: %s\n", error.AsCString());
394 break;
395 case lldb::eExpressionResultUnavailable:
396 // Shoulnd't happen???
397 error_sp->Printf("error: could not fetch result -- %s\n",
398 error.AsCString());
399 break;
400 case lldb::eExpressionStoppedForDebug:
401 // Shoulnd't happen???
402 error_sp->Printf("error: stopped for debug -- %s\n",
403 error.AsCString());
404 break;
405 }
406 }
407
408 if (add_to_code) {
409 const uint32_t new_default_line = m_code.GetSize() + 1;
410
411 m_code.SplitIntoLines(code);
412
413 // Update our code on disk
414 if (!m_repl_source_path.empty()) {
415 lldb_private::File file;
416 FileSystem::Instance().Open(file, FileSpec(m_repl_source_path),
417 File::eOpenOptionWrite |
418 File::eOpenOptionTruncate |
419 File::eOpenOptionCanCreate,
420 lldb::eFilePermissionsFileDefault);
421 std::string code(m_code.CopyList());
422 code.append(1, '\n');
423 size_t bytes_written = code.size();
424 file.Write(code.c_str(), bytes_written);
425 file.Close();
426
427 // Now set the default file and line to the REPL source file
428 m_target.GetSourceManager().SetDefaultFileAndLine(
429 FileSpec(m_repl_source_path), new_default_line);
430 }
431 static_cast<IOHandlerEditline &>(io_handler)
432 .SetBaseLineNumber(m_code.GetSize() + 1);
433 }
434 if (extra_line) {
435 fprintf(output_sp->GetFile().GetStream(), "\n");
436 }
437 }
438 }
439
440 // Don't complain about the REPL process going away if we are in the
441 // process of quitting.
442 if (!did_quit && (!process_sp || !process_sp->IsAlive())) {
443 error_sp->Printf(
444 "error: REPL process is no longer alive, exiting REPL\n");
445 io_handler.SetIsDone(true);
446 }
447 }
448 }
449
IOHandlerComplete(IOHandler & io_handler,const char * current_line,const char * cursor,const char * last_char,int skip_first_n_matches,int max_matches,StringList & matches,StringList & descriptions)450 int REPL::IOHandlerComplete(IOHandler &io_handler, const char *current_line,
451 const char *cursor, const char *last_char,
452 int skip_first_n_matches, int max_matches,
453 StringList &matches, StringList &descriptions) {
454 matches.Clear();
455
456 llvm::StringRef line(current_line, cursor - current_line);
457
458 // Complete an LLDB command if the first character is a colon...
459 if (!line.empty() && line[0] == ':') {
460 Debugger &debugger = m_target.GetDebugger();
461
462 // auto complete LLDB commands
463 const char *lldb_current_line = line.substr(1).data();
464 return debugger.GetCommandInterpreter().HandleCompletion(
465 lldb_current_line, cursor, last_char, skip_first_n_matches, max_matches,
466 matches, descriptions);
467 }
468
469 // Strip spaces from the line and see if we had only spaces
470 line = line.ltrim();
471 if (line.empty()) {
472 // Only spaces on this line, so just indent
473 matches.AppendString(m_indent_str);
474 return 1;
475 }
476
477 std::string current_code;
478 current_code.append(m_code.CopyList());
479
480 IOHandlerEditline &editline = static_cast<IOHandlerEditline &>(io_handler);
481 const StringList *current_lines = editline.GetCurrentLines();
482 if (current_lines) {
483 const uint32_t current_line_idx = editline.GetCurrentLineIndex();
484
485 if (current_line_idx < current_lines->GetSize()) {
486 for (uint32_t i = 0; i < current_line_idx; ++i) {
487 const char *line_cstr = current_lines->GetStringAtIndex(i);
488 if (line_cstr) {
489 current_code.append("\n");
490 current_code.append(line_cstr);
491 }
492 }
493 }
494 }
495
496 if (cursor > current_line) {
497 current_code.append("\n");
498 current_code.append(current_line, cursor - current_line);
499 }
500
501 return CompleteCode(current_code, matches);
502 }
503
QuitCommandOverrideCallback(void * baton,const char ** argv)504 bool QuitCommandOverrideCallback(void *baton, const char **argv) {
505 Target *target = (Target *)baton;
506 lldb::ProcessSP process_sp(target->GetProcessSP());
507 if (process_sp) {
508 process_sp->Destroy(false);
509 process_sp->GetTarget().GetDebugger().ClearIOHandlers();
510 }
511 return false;
512 }
513
RunLoop()514 Status REPL::RunLoop() {
515 Status error;
516
517 error = DoInitialization();
518 m_repl_source_path = GetSourcePath();
519
520 if (!error.Success())
521 return error;
522
523 Debugger &debugger = m_target.GetDebugger();
524
525 lldb::IOHandlerSP io_handler_sp(GetIOHandler());
526
527 FileSpec save_default_file;
528 uint32_t save_default_line = 0;
529
530 if (!m_repl_source_path.empty()) {
531 // Save the current default file and line
532 m_target.GetSourceManager().GetDefaultFileAndLine(save_default_file,
533 save_default_line);
534 }
535
536 debugger.PushIOHandler(io_handler_sp);
537
538 // Check if we are in dedicated REPL mode where LLDB was start with the "--
539 // repl" option from the command line. Currently we know this by checking if
540 // the debugger already has a IOHandler thread.
541 if (!debugger.HasIOHandlerThread()) {
542 // The debugger doesn't have an existing IOHandler thread, so this must be
543 // dedicated REPL mode...
544 m_dedicated_repl_mode = true;
545 debugger.StartIOHandlerThread();
546 llvm::StringRef command_name_str("quit");
547 CommandObject *cmd_obj =
548 debugger.GetCommandInterpreter().GetCommandObjectForCommand(
549 command_name_str);
550 if (cmd_obj) {
551 assert(command_name_str.empty());
552 cmd_obj->SetOverrideCallback(QuitCommandOverrideCallback, &m_target);
553 }
554 }
555
556 // Wait for the REPL command interpreter to get popped
557 io_handler_sp->WaitForPop();
558
559 if (m_dedicated_repl_mode) {
560 // If we were in dedicated REPL mode we would have started the IOHandler
561 // thread, and we should kill our process
562 lldb::ProcessSP process_sp = m_target.GetProcessSP();
563 if (process_sp && process_sp->IsAlive())
564 process_sp->Destroy(false);
565
566 // Wait for the IO handler thread to exit (TODO: don't do this if the IO
567 // handler thread already exists...)
568 debugger.JoinIOHandlerThread();
569 }
570
571 // Restore the default file and line
572 if (save_default_file && save_default_line != 0)
573 m_target.GetSourceManager().SetDefaultFileAndLine(save_default_file,
574 save_default_line);
575 return error;
576 }
577