1 //===-- REPL.cpp ----------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Expression/REPL.h"
10 #include "lldb/Core/Debugger.h"
11 #include "lldb/Core/PluginManager.h"
12 #include "lldb/Expression/ExpressionVariable.h"
13 #include "lldb/Expression/UserExpression.h"
14 #include "lldb/Host/HostInfo.h"
15 #include "lldb/Host/StreamFile.h"
16 #include "lldb/Interpreter/CommandInterpreter.h"
17 #include "lldb/Interpreter/CommandReturnObject.h"
18 #include "lldb/Target/Thread.h"
19 #include "lldb/Utility/AnsiTerminal.h"
23 using namespace lldb_private
;
27 REPL::REPL(Target
&target
) : m_target(target
) {
28 // Make sure all option values have sane defaults
29 Debugger
&debugger
= m_target
.GetDebugger();
30 debugger
.SetShowProgress(false);
31 auto exe_ctx
= debugger
.GetCommandInterpreter().GetExecutionContext();
32 m_format_options
.OptionParsingStarting(&exe_ctx
);
33 m_varobj_options
.OptionParsingStarting(&exe_ctx
);
36 REPL::~REPL() = default;
38 lldb::REPLSP
REPL::Create(Status
&err
, lldb::LanguageType language
,
39 Debugger
*debugger
, Target
*target
,
40 const char *repl_options
) {
44 while (REPLCreateInstance create_instance
=
45 PluginManager::GetREPLCreateCallbackAtIndex(idx
)) {
46 LanguageSet supported_languages
=
47 PluginManager::GetREPLSupportedLanguagesAtIndex(idx
++);
48 if (!supported_languages
[language
])
50 ret
= (*create_instance
)(err
, language
, debugger
, target
, repl_options
);
59 std::string
REPL::GetSourcePath() {
60 llvm::StringRef file_basename
= GetSourceFileBasename();
61 FileSpec tmpdir_file_spec
= HostInfo::GetProcessTempDir();
62 if (tmpdir_file_spec
) {
63 tmpdir_file_spec
.SetFilename(file_basename
);
64 m_repl_source_path
= tmpdir_file_spec
.GetPath();
66 tmpdir_file_spec
= FileSpec("/tmp");
67 tmpdir_file_spec
.AppendPathComponent(file_basename
);
70 return tmpdir_file_spec
.GetPath();
73 lldb::IOHandlerSP
REPL::GetIOHandler() {
74 if (!m_io_handler_sp
) {
75 Debugger
&debugger
= m_target
.GetDebugger();
76 m_io_handler_sp
= std::make_shared
<IOHandlerEditline
>(
77 debugger
, IOHandler::Type::REPL
,
78 "lldb-repl", // Name of input reader for history
79 llvm::StringRef("> "), // prompt
80 llvm::StringRef(". "), // Continuation prompt
82 true, // The REPL prompt is always colored
86 // Don't exit if CTRL+C is pressed
87 static_cast<IOHandlerEditline
*>(m_io_handler_sp
.get())
88 ->SetInterruptExits(false);
90 if (m_io_handler_sp
->GetIsInteractive() &&
91 m_io_handler_sp
->GetIsRealTerminal()) {
92 m_indent_str
.assign(debugger
.GetTabSize(), ' ');
93 m_enable_auto_indent
= debugger
.GetAutoIndent();
96 m_enable_auto_indent
= false;
99 return m_io_handler_sp
;
102 void REPL::IOHandlerActivated(IOHandler
&io_handler
, bool interactive
) {
103 lldb::ProcessSP process_sp
= m_target
.GetProcessSP();
104 if (process_sp
&& process_sp
->IsAlive())
106 lldb::StreamFileSP
error_sp(io_handler
.GetErrorStreamFileSP());
107 error_sp
->Printf("REPL requires a running target process.\n");
108 io_handler
.SetIsDone(true);
111 bool REPL::IOHandlerInterrupt(IOHandler
&io_handler
) { return false; }
113 void REPL::IOHandlerInputInterrupted(IOHandler
&io_handler
, std::string
&line
) {
116 const char *REPL::IOHandlerGetFixIndentationCharacters() {
117 return (m_enable_auto_indent
? GetAutoIndentCharacters() : nullptr);
120 llvm::StringRef
REPL::IOHandlerGetControlSequence(char ch
) {
121 static constexpr llvm::StringLiteral
control_sequence(":quit\n");
123 return control_sequence
;
127 const char *REPL::IOHandlerGetCommandPrefix() { return ":"; }
129 const char *REPL::IOHandlerGetHelpPrologue() {
130 return "\nThe REPL (Read-Eval-Print-Loop) acts like an interpreter. "
131 "Valid statements, expressions, and declarations are immediately "
132 "compiled and executed.\n\n"
133 "The complete set of LLDB debugging commands are also available as "
134 "described below.\n\nCommands "
135 "must be prefixed with a colon at the REPL prompt (:quit for "
136 "example.) Typing just a colon "
137 "followed by return will switch to the LLDB prompt.\n\n"
138 "Type “< path” to read in code from a text file “path”.\n\n";
141 bool REPL::IOHandlerIsInputComplete(IOHandler
&io_handler
, StringList
&lines
) {
142 // Check for meta command
143 const size_t num_lines
= lines
.GetSize();
144 if (num_lines
== 1) {
145 const char *first_line
= lines
.GetStringAtIndex(0);
146 if (first_line
[0] == ':')
147 return true; // Meta command is a single line where that starts with ':'
150 // Check if REPL input is done
151 std::string
source_string(lines
.CopyList());
152 return SourceIsComplete(source_string
);
155 int REPL::CalculateActualIndentation(const StringList
&lines
) {
156 std::string last_line
= lines
[lines
.GetSize() - 1];
158 int actual_indent
= 0;
159 for (char &ch
: last_line
) {
165 return actual_indent
;
168 int REPL::IOHandlerFixIndentation(IOHandler
&io_handler
,
169 const StringList
&lines
,
170 int cursor_position
) {
171 if (!m_enable_auto_indent
)
174 if (!lines
.GetSize()) {
178 int tab_size
= io_handler
.GetDebugger().GetTabSize();
180 lldb::offset_t desired_indent
=
181 GetDesiredIndentation(lines
, cursor_position
, tab_size
);
183 int actual_indent
= REPL::CalculateActualIndentation(lines
);
185 if (desired_indent
== LLDB_INVALID_OFFSET
)
188 return (int)desired_indent
- actual_indent
;
191 static bool ReadCode(const std::string
&path
, std::string
&code
,
192 lldb::StreamFileSP
&error_sp
) {
193 auto &fs
= FileSystem::Instance();
194 llvm::Twine
pathTwine(path
);
195 if (!fs
.Exists(pathTwine
)) {
196 error_sp
->Printf("no such file at path '%s'\n", path
.c_str());
199 if (!fs
.Readable(pathTwine
)) {
200 error_sp
->Printf("could not read file at path '%s'\n", path
.c_str());
203 const size_t file_size
= fs
.GetByteSize(pathTwine
);
204 const size_t max_size
= code
.max_size();
205 if (file_size
> max_size
) {
206 error_sp
->Printf("file at path '%s' too large: "
207 "file_size = %zu, max_size = %zu\n",
208 path
.c_str(), file_size
, max_size
);
211 auto data_sp
= fs
.CreateDataBuffer(pathTwine
);
212 if (data_sp
== nullptr) {
213 error_sp
->Printf("could not create buffer for file at path '%s'\n",
217 code
.assign((const char *)data_sp
->GetBytes(), data_sp
->GetByteSize());
221 void REPL::IOHandlerInputComplete(IOHandler
&io_handler
, std::string
&code
) {
222 lldb::StreamFileSP
output_sp(io_handler
.GetOutputStreamFileSP());
223 lldb::StreamFileSP
error_sp(io_handler
.GetErrorStreamFileSP());
224 bool extra_line
= false;
225 bool did_quit
= false;
228 m_code
.AppendString("");
229 static_cast<IOHandlerEditline
&>(io_handler
)
230 .SetBaseLineNumber(m_code
.GetSize() + 1);
232 Debugger
&debugger
= m_target
.GetDebugger();
233 CommandInterpreter
&ci
= debugger
.GetCommandInterpreter();
234 extra_line
= ci
.GetSpaceReplPrompts();
236 ExecutionContext
exe_ctx(m_target
.GetProcessSP()
239 ->GetSelectedFrame(DoNoSelectMostRelevantFrame
)
242 lldb::ProcessSP
process_sp(exe_ctx
.GetProcessSP());
244 if (code
[0] == ':') {
248 if (!llvm::StringRef(code
).trim().empty()) {
249 // "lldb" was followed by arguments, so just execute the command dump
252 // Turn off prompt on quit in case the user types ":quit"
253 const bool saved_prompt_on_quit
= ci
.GetPromptOnQuit();
254 if (saved_prompt_on_quit
)
255 ci
.SetPromptOnQuit(false);
257 // Execute the command
258 CommandReturnObject
result(debugger
.GetUseColor());
259 result
.SetImmediateOutputStream(output_sp
);
260 result
.SetImmediateErrorStream(error_sp
);
261 ci
.HandleCommand(code
.c_str(), eLazyBoolNo
, result
);
263 if (saved_prompt_on_quit
)
264 ci
.SetPromptOnQuit(true);
266 if (result
.GetStatus() == lldb::eReturnStatusQuit
) {
268 io_handler
.SetIsDone(true);
269 if (debugger
.CheckTopIOHandlerTypes(
270 IOHandler::Type::REPL
, IOHandler::Type::CommandInterpreter
)) {
271 // We typed "quit" or an alias to quit so we need to check if the
272 // command interpreter is above us and tell it that it is done as
273 // well so we don't drop back into the command interpreter if we
275 lldb::IOHandlerSP
io_handler_sp(ci
.GetIOHandler());
277 io_handler_sp
->SetIsDone(true);
281 // ":" was followed by no arguments, so push the LLDB command prompt
282 if (debugger
.CheckTopIOHandlerTypes(
283 IOHandler::Type::REPL
, IOHandler::Type::CommandInterpreter
)) {
284 // If the user wants to get back to the command interpreter and the
285 // command interpreter is what launched the REPL, then just let the
286 // REPL exit and fall back to the command interpreter.
287 io_handler
.SetIsDone(true);
289 // The REPL wasn't launched the by the command interpreter, it is the
290 // base IOHandler, so we need to get the command interpreter and
291 lldb::IOHandlerSP
io_handler_sp(ci
.GetIOHandler());
293 io_handler_sp
->SetIsDone(false);
294 debugger
.RunIOHandlerAsync(ci
.GetIOHandler());
299 if (code
[0] == '<') {
300 // User wants to read code from a file.
301 // Interpret rest of line as a literal path.
302 auto path
= llvm::StringRef(code
.substr(1)).trim().str();
303 if (!ReadCode(path
, code
, error_sp
)) {
308 // Unwind any expression we might have been running in case our REPL
309 // expression crashed and the user was looking around
310 if (m_dedicated_repl_mode
) {
311 Thread
*thread
= exe_ctx
.GetThreadPtr();
312 if (thread
&& thread
->UnwindInnermostExpression().Success()) {
313 thread
->SetSelectedFrameByIndex(0, false);
315 thread
->GetSelectedFrame(DoNoSelectMostRelevantFrame
));
319 const bool colorize_err
= error_sp
->GetFile().GetIsTerminalWithColors();
321 EvaluateExpressionOptions expr_options
= m_expr_options
;
322 expr_options
.SetCoerceToId(m_varobj_options
.use_objc
);
323 expr_options
.SetKeepInMemory(true);
324 expr_options
.SetUseDynamic(m_varobj_options
.use_dynamic
);
325 expr_options
.SetGenerateDebugInfo(true);
326 expr_options
.SetREPLEnabled(true);
327 expr_options
.SetColorizeErrors(colorize_err
);
328 expr_options
.SetPoundLine(m_repl_source_path
.c_str(),
329 m_code
.GetSize() + 1);
331 expr_options
.SetLanguage(GetLanguage());
333 PersistentExpressionState
*persistent_state
=
334 m_target
.GetPersistentExpressionStateForLanguage(GetLanguage());
335 if (!persistent_state
)
338 const size_t var_count_before
= persistent_state
->GetSize();
340 const char *expr_prefix
= nullptr;
341 lldb::ValueObjectSP result_valobj_sp
;
343 lldb::ExpressionResults execution_results
=
344 UserExpression::Evaluate(exe_ctx
, expr_options
, code
.c_str(),
345 expr_prefix
, result_valobj_sp
, error
,
346 nullptr); // fixed expression
348 if (llvm::Error err
= OnExpressionEvaluated(exe_ctx
, code
, expr_options
,
350 result_valobj_sp
, error
)) {
351 *error_sp
<< llvm::toString(std::move(err
)) << "\n";
352 } else if (process_sp
&& process_sp
->IsAlive()) {
353 bool add_to_code
= true;
354 bool handled
= false;
355 if (result_valobj_sp
) {
356 lldb::Format format
= m_format_options
.GetFormat();
358 if (result_valobj_sp
->GetError().Success()) {
359 handled
|= PrintOneVariable(debugger
, output_sp
, result_valobj_sp
);
360 } else if (result_valobj_sp
->GetError().GetError() ==
361 UserExpression::kNoResult
) {
362 if (format
!= lldb::eFormatVoid
&& debugger
.GetNotifyVoid()) {
363 error_sp
->PutCString("(void)\n");
369 if (debugger
.GetPrintDecls()) {
370 for (size_t vi
= var_count_before
, ve
= persistent_state
->GetSize();
372 lldb::ExpressionVariableSP persistent_var_sp
=
373 persistent_state
->GetVariableAtIndex(vi
);
374 lldb::ValueObjectSP valobj_sp
= persistent_var_sp
->GetValueObject();
376 PrintOneVariable(debugger
, output_sp
, valobj_sp
,
377 persistent_var_sp
.get());
382 bool useColors
= error_sp
->GetFile().GetIsTerminalWithColors();
383 switch (execution_results
) {
384 case lldb::eExpressionSetupError
:
385 case lldb::eExpressionParseError
:
388 case lldb::eExpressionDiscarded
:
389 error_sp
->Printf("%s\n", error
.AsCString());
392 case lldb::eExpressionCompleted
:
394 case lldb::eExpressionInterrupted
:
396 error_sp
->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED
));
397 error_sp
->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD
));
399 error_sp
->Printf("Execution interrupted. ");
401 error_sp
->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL
));
402 error_sp
->Printf("Enter code to recover and continue.\nEnter LLDB "
403 "commands to investigate (type :help for "
407 case lldb::eExpressionHitBreakpoint
:
408 // Breakpoint was hit, drop into LLDB command interpreter
410 error_sp
->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED
));
411 error_sp
->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD
));
413 output_sp
->Printf("Execution stopped at breakpoint. ");
415 error_sp
->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL
));
416 output_sp
->Printf("Enter LLDB commands to investigate (type help "
417 "for assistance.)\n");
419 lldb::IOHandlerSP
io_handler_sp(ci
.GetIOHandler());
421 io_handler_sp
->SetIsDone(false);
422 debugger
.RunIOHandlerAsync(ci
.GetIOHandler());
427 case lldb::eExpressionTimedOut
:
428 error_sp
->Printf("error: timeout\n");
429 if (error
.AsCString())
430 error_sp
->Printf("error: %s\n", error
.AsCString());
432 case lldb::eExpressionResultUnavailable
:
433 // Shoulnd't happen???
434 error_sp
->Printf("error: could not fetch result -- %s\n",
437 case lldb::eExpressionStoppedForDebug
:
438 // Shoulnd't happen???
439 error_sp
->Printf("error: stopped for debug -- %s\n",
442 case lldb::eExpressionThreadVanished
:
443 // Shoulnd't happen???
444 error_sp
->Printf("error: expression thread vanished -- %s\n",
451 const uint32_t new_default_line
= m_code
.GetSize() + 1;
453 m_code
.SplitIntoLines(code
);
455 // Update our code on disk
456 if (!m_repl_source_path
.empty()) {
457 auto file
= FileSystem::Instance().Open(
458 FileSpec(m_repl_source_path
),
459 File::eOpenOptionWriteOnly
| File::eOpenOptionTruncate
|
460 File::eOpenOptionCanCreate
,
461 lldb::eFilePermissionsFileDefault
);
463 std::string
code(m_code
.CopyList());
464 code
.append(1, '\n');
465 size_t bytes_written
= code
.size();
466 file
.get()->Write(code
.c_str(), bytes_written
);
469 std::string message
= llvm::toString(file
.takeError());
470 error_sp
->Printf("error: couldn't open %s: %s\n",
471 m_repl_source_path
.c_str(), message
.c_str());
474 // Now set the default file and line to the REPL source file
475 m_target
.GetSourceManager().SetDefaultFileAndLine(
476 FileSpec(m_repl_source_path
), new_default_line
);
478 static_cast<IOHandlerEditline
&>(io_handler
)
479 .SetBaseLineNumber(m_code
.GetSize() + 1);
482 output_sp
->Printf("\n");
487 // Don't complain about the REPL process going away if we are in the
488 // process of quitting.
489 if (!did_quit
&& (!process_sp
|| !process_sp
->IsAlive())) {
491 "error: REPL process is no longer alive, exiting REPL\n");
492 io_handler
.SetIsDone(true);
497 void REPL::IOHandlerComplete(IOHandler
&io_handler
,
498 CompletionRequest
&request
) {
499 // Complete an LLDB command if the first character is a colon...
500 if (request
.GetRawLine().startswith(":")) {
501 Debugger
&debugger
= m_target
.GetDebugger();
503 // auto complete LLDB commands
504 llvm::StringRef new_line
= request
.GetRawLine().drop_front();
505 CompletionResult sub_result
;
506 CompletionRequest
sub_request(new_line
, request
.GetRawCursorPos() - 1,
508 debugger
.GetCommandInterpreter().HandleCompletion(sub_request
);
509 StringList matches
, descriptions
;
510 sub_result
.GetMatches(matches
);
511 // Prepend command prefix that was excluded in the completion request.
512 if (request
.GetCursorIndex() == 0)
513 for (auto &match
: matches
)
514 match
.insert(0, 1, ':');
515 sub_result
.GetDescriptions(descriptions
);
516 request
.AddCompletions(matches
, descriptions
);
520 // Strip spaces from the line and see if we had only spaces
521 if (request
.GetRawLine().trim().empty()) {
522 // Only spaces on this line, so just indent
523 request
.AddCompletion(m_indent_str
);
527 std::string current_code
;
528 current_code
.append(m_code
.CopyList());
530 IOHandlerEditline
&editline
= static_cast<IOHandlerEditline
&>(io_handler
);
531 StringList current_lines
= editline
.GetCurrentLines();
532 const uint32_t current_line_idx
= editline
.GetCurrentLineIndex();
534 if (current_line_idx
< current_lines
.GetSize()) {
535 for (uint32_t i
= 0; i
< current_line_idx
; ++i
) {
536 const char *line_cstr
= current_lines
.GetStringAtIndex(i
);
538 current_code
.append("\n");
539 current_code
.append(line_cstr
);
544 current_code
.append("\n");
545 current_code
+= request
.GetRawLine();
547 CompleteCode(current_code
, request
);
550 bool QuitCommandOverrideCallback(void *baton
, const char **argv
) {
551 Target
*target
= (Target
*)baton
;
552 lldb::ProcessSP
process_sp(target
->GetProcessSP());
554 process_sp
->Destroy(false);
555 process_sp
->GetTarget().GetDebugger().ClearIOHandlers();
560 Status
REPL::RunLoop() {
563 error
= DoInitialization();
564 m_repl_source_path
= GetSourcePath();
566 if (!error
.Success())
569 Debugger
&debugger
= m_target
.GetDebugger();
571 lldb::IOHandlerSP
io_handler_sp(GetIOHandler());
573 FileSpec save_default_file
;
574 uint32_t save_default_line
= 0;
576 if (!m_repl_source_path
.empty()) {
577 // Save the current default file and line
578 m_target
.GetSourceManager().GetDefaultFileAndLine(save_default_file
,
582 debugger
.RunIOHandlerAsync(io_handler_sp
);
584 // Check if we are in dedicated REPL mode where LLDB was start with the "--
585 // repl" option from the command line. Currently we know this by checking if
586 // the debugger already has a IOHandler thread.
587 if (!debugger
.HasIOHandlerThread()) {
588 // The debugger doesn't have an existing IOHandler thread, so this must be
589 // dedicated REPL mode...
590 m_dedicated_repl_mode
= true;
591 debugger
.StartIOHandlerThread();
592 llvm::StringRef
command_name_str("quit");
593 CommandObject
*cmd_obj
=
594 debugger
.GetCommandInterpreter().GetCommandObjectForCommand(
597 assert(command_name_str
.empty());
598 cmd_obj
->SetOverrideCallback(QuitCommandOverrideCallback
, &m_target
);
602 // Wait for the REPL command interpreter to get popped
603 io_handler_sp
->WaitForPop();
605 if (m_dedicated_repl_mode
) {
606 // If we were in dedicated REPL mode we would have started the IOHandler
607 // thread, and we should kill our process
608 lldb::ProcessSP process_sp
= m_target
.GetProcessSP();
609 if (process_sp
&& process_sp
->IsAlive())
610 process_sp
->Destroy(false);
612 // Wait for the IO handler thread to exit (TODO: don't do this if the IO
613 // handler thread already exists...)
614 debugger
.JoinIOHandlerThread();
617 // Restore the default file and line
618 if (save_default_file
&& save_default_line
!= 0)
619 m_target
.GetSourceManager().SetDefaultFileAndLine(save_default_file
,