bool DoExecute(Args &command, CommandReturnObject &result) override { StringList commands; commands.AppendString("thread backtrace"); Thread *thread = m_exe_ctx.GetThreadPtr(); if (thread) { char command_buffer[256]; uint32_t frame_count = thread->GetStackFrameCount(); for (uint32_t i = 0; i < frame_count; ++i) { StackFrameSP frame = thread->GetStackFrameAtIndex(i); lldb::addr_t pc = frame->GetStackID().GetPC(); snprintf(command_buffer, sizeof(command_buffer), "disassemble --bytes --address 0x%" PRIx64, pc); commands.AppendString(command_buffer); snprintf(command_buffer, sizeof(command_buffer), "image show-unwind --address 0x%" PRIx64, pc); commands.AppendString(command_buffer); } } const FileSpec &outfile_spec = m_outfile_options.GetFile().GetCurrentValue(); if (outfile_spec) { char path[PATH_MAX]; outfile_spec.GetPath(path, sizeof(path)); uint32_t open_options = File::eOpenOptionWrite | File::eOpenOptionCanCreate | File::eOpenOptionAppend | File::eOpenOptionCloseOnExec; const bool append = m_outfile_options.GetAppend().GetCurrentValue(); if (!append) open_options |= File::eOpenOptionTruncate; StreamFileSP outfile_stream = std::make_shared<StreamFile>(); Status error = outfile_stream->GetFile().Open(path, open_options); if (error.Fail()) { result.AppendErrorWithFormat("Failed to open file '%s' for %s: %s\n", path, append ? "append" : "write", error.AsCString()); result.SetStatus(eReturnStatusFailed); return false; } result.SetImmediateOutputStream(outfile_stream); } CommandInterpreterRunOptions options; options.SetStopOnError(false); options.SetEchoCommands(true); options.SetPrintResults(true); options.SetAddToHistory(false); m_interpreter.HandleCommands(commands, &m_exe_ctx, options, result); return result.Succeeded(); }
static bool BreakpointOptionsCallbackFunction (void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, lldb::user_id_t break_loc_id) { bool ret_value = true; if (baton == NULL) return true; BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton; StringList &commands = data->user_source; if (commands.GetSize() > 0) { ExecutionContext exe_ctx (context->exe_ctx_ref); Target *target = exe_ctx.GetTargetPtr(); if (target) { CommandReturnObject result; Debugger &debugger = target->GetDebugger(); // Rig up the results secondary output stream to the debugger's, so the output will come out synchronously // if the debugger is set up that way. StreamSP output_stream (debugger.GetAsyncOutputStream()); StreamSP error_stream (debugger.GetAsyncErrorStream()); result.SetImmediateOutputStream (output_stream); result.SetImmediateErrorStream (error_stream); CommandInterpreterRunOptions options; options.SetStopOnContinue(true); options.SetStopOnError (data->stop_on_error); options.SetEchoCommands (true); options.SetPrintResults (true); options.SetAddToHistory (false); debugger.GetCommandInterpreter().HandleCommands (commands, &exe_ctx, options, result); result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); } } return ret_value; }
static bool WatchpointOptionsCallbackFunction (void *baton, StoppointCallbackContext *context, lldb::user_id_t watch_id) { bool ret_value = true; if (baton == NULL) return true; WatchpointOptions::CommandData *data = (WatchpointOptions::CommandData *) baton; StringList &commands = data->user_source; if (commands.GetSize() > 0) { ExecutionContext exe_ctx (context->exe_ctx_ref); Target *target = exe_ctx.GetTargetPtr(); if (target) { CommandReturnObject result; Debugger &debugger = target->GetDebugger(); // Rig up the results secondary output stream to the debugger's, so the output will come out synchronously // if the debugger is set up that way. StreamSP output_stream (debugger.GetAsyncOutputStream()); StreamSP error_stream (debugger.GetAsyncErrorStream()); result.SetImmediateOutputStream (output_stream); result.SetImmediateErrorStream (error_stream); bool stop_on_continue = true; bool echo_commands = false; bool print_results = true; debugger.GetCommandInterpreter().HandleCommands (commands, &exe_ctx, stop_on_continue, data->stop_on_error, echo_commands, print_results, eLazyBoolNo, result); result.GetImmediateOutputStream()->Flush(); result.GetImmediateErrorStream()->Flush(); } } return ret_value; }
void REPL::IOHandlerInputComplete(IOHandler &io_handler, std::string &code) { lldb::StreamFileSP output_sp(io_handler.GetOutputStreamFile()); lldb::StreamFileSP error_sp(io_handler.GetErrorStreamFile()); bool extra_line = false; bool did_quit = false; if (code.empty()) { m_code.AppendString(""); static_cast<IOHandlerEditline &>(io_handler) .SetBaseLineNumber(m_code.GetSize() + 1); } else { Debugger &debugger = m_target.GetDebugger(); CommandInterpreter &ci = debugger.GetCommandInterpreter(); extra_line = ci.GetSpaceReplPrompts(); ExecutionContext exe_ctx(m_target.GetProcessSP() ->GetThreadList() .GetSelectedThread() ->GetSelectedFrame() .get()); lldb::ProcessSP process_sp(exe_ctx.GetProcessSP()); if (code[0] == ':') { // Meta command // Strip the ':' code.erase(0, 1); if (Args::StripSpaces(code)) { // "lldb" was followed by arguments, so just execute the command dump // the results // Turn off prompt on quit in case the user types ":quit" const bool saved_prompt_on_quit = ci.GetPromptOnQuit(); if (saved_prompt_on_quit) ci.SetPromptOnQuit(false); // Execute the command CommandReturnObject result; result.SetImmediateOutputStream(output_sp); result.SetImmediateErrorStream(error_sp); ci.HandleCommand(code.c_str(), eLazyBoolNo, result); if (saved_prompt_on_quit) ci.SetPromptOnQuit(true); if (result.GetStatus() == lldb::eReturnStatusQuit) { did_quit = true; io_handler.SetIsDone(true); if (debugger.CheckTopIOHandlerTypes( IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { // We typed "quit" or an alias to quit so we need to check if the // command interpreter is above us and tell it that it is done as // well // so we don't drop back into the command interpreter if we have // already // quit lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); if (io_handler_sp) io_handler_sp->SetIsDone(true); } } } else { // ":" was followed by no arguments, so push the LLDB command prompt if (debugger.CheckTopIOHandlerTypes( IOHandler::Type::REPL, IOHandler::Type::CommandInterpreter)) { // If the user wants to get back to the command interpreter and the // command interpreter is what launched the REPL, then just let the // REPL exit and fall back to the command interpreter. io_handler.SetIsDone(true); } else { // The REPL wasn't launched the by the command interpreter, it is the // base IOHandler, so we need to get the command interpreter and lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); if (io_handler_sp) { io_handler_sp->SetIsDone(false); debugger.PushIOHandler(ci.GetIOHandler()); } } } } else { // Unwind any expression we might have been running in case our REPL // expression crashed and the user was looking around if (m_dedicated_repl_mode) { Thread *thread = exe_ctx.GetThreadPtr(); if (thread && thread->UnwindInnermostExpression().Success()) { thread->SetSelectedFrameByIndex(0, false); exe_ctx.SetFrameSP(thread->GetSelectedFrame()); } } const bool colorize_err = error_sp->GetFile().GetIsTerminalWithColors(); EvaluateExpressionOptions expr_options; expr_options.SetCoerceToId(m_varobj_options.use_objc); expr_options.SetUnwindOnError(m_command_options.unwind_on_error); expr_options.SetIgnoreBreakpoints(m_command_options.ignore_breakpoints); expr_options.SetKeepInMemory(true); expr_options.SetUseDynamic(m_varobj_options.use_dynamic); expr_options.SetTryAllThreads(m_command_options.try_all_threads); expr_options.SetGenerateDebugInfo(true); expr_options.SetREPLEnabled(true); expr_options.SetColorizeErrors(colorize_err); expr_options.SetPoundLine(m_repl_source_path.c_str(), m_code.GetSize() + 1); if (m_command_options.timeout > 0) expr_options.SetTimeout(std::chrono::microseconds(m_command_options.timeout)); else expr_options.SetTimeout(llvm::None); expr_options.SetLanguage(GetLanguage()); PersistentExpressionState *persistent_state = m_target.GetPersistentExpressionStateForLanguage(GetLanguage()); const size_t var_count_before = persistent_state->GetSize(); const char *expr_prefix = nullptr; lldb::ValueObjectSP result_valobj_sp; Error error; lldb::ModuleSP jit_module_sp; lldb::ExpressionResults execution_results = UserExpression::Evaluate(exe_ctx, expr_options, code.c_str(), expr_prefix, result_valobj_sp, error, 0, // Line offset nullptr, // Fixed Expression &jit_module_sp); // CommandInterpreter &ci = debugger.GetCommandInterpreter(); if (process_sp && process_sp->IsAlive()) { bool add_to_code = true; bool handled = false; if (result_valobj_sp) { lldb::Format format = m_format_options.GetFormat(); if (result_valobj_sp->GetError().Success()) { handled |= PrintOneVariable(debugger, output_sp, result_valobj_sp); } else if (result_valobj_sp->GetError().GetError() == UserExpression::kNoResult) { if (format != lldb::eFormatVoid && debugger.GetNotifyVoid()) { error_sp->PutCString("(void)\n"); handled = true; } } } if (debugger.GetPrintDecls()) { for (size_t vi = var_count_before, ve = persistent_state->GetSize(); vi != ve; ++vi) { lldb::ExpressionVariableSP persistent_var_sp = persistent_state->GetVariableAtIndex(vi); lldb::ValueObjectSP valobj_sp = persistent_var_sp->GetValueObject(); PrintOneVariable(debugger, output_sp, valobj_sp, persistent_var_sp.get()); } } if (!handled) { bool useColors = error_sp->GetFile().GetIsTerminalWithColors(); switch (execution_results) { case lldb::eExpressionSetupError: case lldb::eExpressionParseError: add_to_code = false; LLVM_FALLTHROUGH; case lldb::eExpressionDiscarded: error_sp->Printf("%s\n", error.AsCString()); break; case lldb::eExpressionCompleted: break; case lldb::eExpressionInterrupted: if (useColors) { error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); } error_sp->Printf("Execution interrupted. "); if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); error_sp->Printf("Enter code to recover and continue.\nEnter LLDB " "commands to investigate (type :help for " "assistance.)\n"); break; case lldb::eExpressionHitBreakpoint: // Breakpoint was hit, drop into LLDB command interpreter if (useColors) { error_sp->Printf(ANSI_ESCAPE1(ANSI_FG_COLOR_RED)); error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_BOLD)); } output_sp->Printf("Execution stopped at breakpoint. "); if (useColors) error_sp->Printf(ANSI_ESCAPE1(ANSI_CTRL_NORMAL)); output_sp->Printf("Enter LLDB commands to investigate (type help " "for assistance.)\n"); { lldb::IOHandlerSP io_handler_sp(ci.GetIOHandler()); if (io_handler_sp) { io_handler_sp->SetIsDone(false); debugger.PushIOHandler(ci.GetIOHandler()); } } break; case lldb::eExpressionTimedOut: error_sp->Printf("error: timeout\n"); if (error.AsCString()) error_sp->Printf("error: %s\n", error.AsCString()); break; case lldb::eExpressionResultUnavailable: // Shoulnd't happen??? error_sp->Printf("error: could not fetch result -- %s\n", error.AsCString()); break; case lldb::eExpressionStoppedForDebug: // Shoulnd't happen??? error_sp->Printf("error: stopped for debug -- %s\n", error.AsCString()); break; } } if (add_to_code) { const uint32_t new_default_line = m_code.GetSize() + 1; m_code.SplitIntoLines(code); // Update our code on disk if (!m_repl_source_path.empty()) { lldb_private::File file(m_repl_source_path.c_str(), File::eOpenOptionWrite | File::eOpenOptionTruncate | File::eOpenOptionCanCreate, lldb::eFilePermissionsFileDefault); std::string code(m_code.CopyList()); code.append(1, '\n'); size_t bytes_written = code.size(); file.Write(code.c_str(), bytes_written); file.Close(); // Now set the default file and line to the REPL source file m_target.GetSourceManager().SetDefaultFileAndLine( FileSpec(m_repl_source_path, false), new_default_line); } static_cast<IOHandlerEditline &>(io_handler) .SetBaseLineNumber(m_code.GetSize() + 1); } if (extra_line) { fprintf(output_sp->GetFile().GetStream(), "\n"); } } } // Don't complain about the REPL process going away if we are in the process // of quitting. if (!did_quit && (!process_sp || !process_sp->IsAlive())) { error_sp->Printf( "error: REPL process is no longer alive, exiting REPL\n"); io_handler.SetIsDone(true); } } }