Index: include/lldb/Core/FormatEntity.h =================================================================== --- include/lldb/Core/FormatEntity.h +++ include/lldb/Core/FormatEntity.h @@ -69,6 +69,7 @@ ThreadID, ThreadProtocolID, ThreadIndexID, + ThreadCount, ThreadName, ThreadQueue, ThreadStopReason, @@ -82,6 +83,8 @@ File, Lang, FrameIndex, + FrameIndex1Based, + FrameCount, FrameNoDebug, FrameRegisterPC, FrameRegisterSP, Index: include/lldb/Core/IOHandler.h =================================================================== --- include/lldb/Core/IOHandler.h +++ include/lldb/Core/IOHandler.h @@ -12,6 +12,7 @@ #include "lldb/Core/ValueObjectList.h" #include "lldb/Host/Predicate.h" +#include "lldb/Interpreter/PromptInterpolation.h" #include "lldb/Utility/ConstString.h" #include "lldb/Utility/Flags.h" #include "lldb/Utility/Stream.h" @@ -431,6 +432,8 @@ const char *last_char, int skip_first_n_matches, int max_matches, StringList &matches, void *baton); + + static const char* PromptCallback(Editline *editline, void *baton); #endif protected: @@ -438,6 +441,7 @@ std::unique_ptr m_editline_ap; #endif IOHandlerDelegate &m_delegate; + PromptInterpolation m_prompt_interpolator; std::string m_prompt; std::string m_continuation_prompt; StringList *m_current_lines_ptr; @@ -448,6 +452,8 @@ bool m_interrupt_exits; bool m_editing; // Set to true when fetching a line manually (not using // libedit) +private: + std::string m_prompt_string; }; // The order of base classes is important. Look at the constructor of Index: include/lldb/Host/Editline.h =================================================================== --- include/lldb/Host/Editline.h +++ include/lldb/Host/Editline.h @@ -90,7 +90,7 @@ class EditlineHistory; typedef std::shared_ptr EditlineHistorySP; - + typedef bool (*IsInputCompleteCallbackType)(Editline *editline, StringList &lines, void *baton); @@ -103,6 +103,9 @@ int skip_first_n_matches, int max_matches, StringList &matches, void *baton); +typedef const char *(*PromptCallbackType)(Editline *editline, + void *baton); + /// Status used to decide when and how to start editing another line in /// multi-line sessions enum class EditorStatus { @@ -180,6 +183,9 @@ /// Cancel this edit and oblitarate all trace of it bool Cancel(); + /// Register a callback to retrieve the prompt. + void SetPromptCallback(PromptCallbackType callback, void *baton); + /// Register a callback for the tab key void SetAutoCompleteCallback(CompleteCallbackType callback, void *baton); @@ -324,6 +330,8 @@ bool CompleteCharacter(char ch, EditLineGetCharType &out); + const char* InvokePromptCallback(); + private: #if LLDB_EDITLINE_USE_WCHAR std::wstring_convert> m_utf8conv; @@ -358,7 +366,8 @@ const char *m_fix_indentation_callback_chars = nullptr; CompleteCallbackType m_completion_callback = nullptr; void *m_completion_callback_baton = nullptr; - + PromptCallbackType m_prompt_callback = nullptr; + void *m_prompt_callback_baton = nullptr; std::mutex m_output_mutex; }; } Index: include/lldb/Interpreter/PromptInterpolation.h =================================================================== --- /dev/null +++ include/lldb/Interpreter/PromptInterpolation.h @@ -0,0 +1,35 @@ +//===-- PromptInterpolation.h --------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef liblldb_PromptInterpolation_h_ +#define liblldb_PromptInterpolation_h_ + +#include + +namespace lldb_private { +class Debugger; +} + +namespace lldb_private { + +class PromptInterpolation { +public: + PromptInterpolation(Debugger& debugger) : + m_debugger(debugger) { + } + + std::string InterpolatePrompt(const std::string& prompt_format); +private: + Debugger &m_debugger; +}; + +} // namespace lldb_private + +#endif // liblldb_PromptInterpolation_h_ + Index: source/Core/FormatEntity.cpp =================================================================== --- source/Core/FormatEntity.cpp +++ source/Core/FormatEntity.cpp @@ -122,6 +122,8 @@ static FormatEntity::Entry::Definition g_frame_child_entries[] = { ENTRY("index", FrameIndex, UInt32), + ENTRY("index1based", FrameIndex1Based, UInt32), + ENTRY("count", FrameCount, UInt32), ENTRY("pc", FrameRegisterPC, UInt64), ENTRY("fp", FrameRegisterFP, UInt64), ENTRY("sp", FrameRegisterSP, UInt64), @@ -170,6 +172,7 @@ ENTRY("id", ThreadID, UInt64), ENTRY("protocol_id", ThreadProtocolID, UInt64), ENTRY("index", ThreadIndexID, UInt32), + ENTRY("count", ThreadCount, UInt32), ENTRY_CHILDREN("info", ThreadInfo, None, g_string_entry), ENTRY("queue", ThreadQueue, CString), ENTRY("name", ThreadName, CString), @@ -337,6 +340,7 @@ ENUM_TO_CSTR(ThreadID); ENUM_TO_CSTR(ThreadProtocolID); ENUM_TO_CSTR(ThreadIndexID); + ENUM_TO_CSTR(ThreadCount); ENUM_TO_CSTR(ThreadName); ENUM_TO_CSTR(ThreadQueue); ENUM_TO_CSTR(ThreadStopReason); @@ -350,6 +354,8 @@ ENUM_TO_CSTR(File); ENUM_TO_CSTR(Lang); ENUM_TO_CSTR(FrameIndex); + ENUM_TO_CSTR(FrameIndex1Based); + ENUM_TO_CSTR(FrameCount); ENUM_TO_CSTR(FrameNoDebug); ENUM_TO_CSTR(FrameRegisterPC); ENUM_TO_CSTR(FrameRegisterSP); @@ -1239,6 +1245,19 @@ } return false; + case Entry::Type::ThreadCount: + if (exe_ctx) { + if (exe_ctx->HasProcessScope()) { + auto thread_count = exe_ctx->GetProcessSP()->GetThreadList().GetSize(); + const char *format = "%" PRIu32; + if (!entry.printf_format.empty()) + format = entry.printf_format.c_str(); + s.Printf(format, thread_count); + return true; + } + } + return false; + case Entry::Type::ThreadIndexID: if (exe_ctx) { Thread *thread = exe_ctx->GetThreadPtr(); @@ -1421,6 +1440,32 @@ } return false; + case Entry::Type::FrameIndex1Based: + if (exe_ctx) { + StackFrame *frame = exe_ctx->GetFramePtr(); + if (frame) { + const char *format = "%" PRIu32; + if (!entry.printf_format.empty()) + format = entry.printf_format.c_str(); + s.Printf(format, frame->GetFrameIndex() + 1); + return true; + } + } + return false; + + case Entry::Type::FrameCount: + if (exe_ctx) { + Thread* thread = exe_ctx->GetThreadPtr(); + if (thread) { + const char *format = "%" PRIu32; + if (!entry.printf_format.empty()) + format = entry.printf_format.c_str(); + s.Printf(format, thread->GetStackFrameCount()); + return true; + } + } + return false; + case Entry::Type::FrameRegisterPC: if (exe_ctx) { StackFrame *frame = exe_ctx->GetFramePtr(); Index: source/Core/IOHandler.cpp =================================================================== --- source/Core/IOHandler.cpp +++ source/Core/IOHandler.cpp @@ -293,7 +293,8 @@ #ifndef LLDB_DISABLE_LIBEDIT m_editline_ap(), #endif - m_delegate(delegate), m_prompt(), m_continuation_prompt(), + m_delegate(delegate), m_prompt_interpolator(m_debugger), + m_prompt(), m_continuation_prompt(), m_current_lines_ptr(nullptr), m_base_line_number(line_number_start), m_curr_line_idx(UINT32_MAX), m_multi_line(multi_line), m_color_prompts(color_prompts), m_interrupt_exits(true), @@ -311,6 +312,7 @@ m_color_prompts)); m_editline_ap->SetIsInputCompleteCallback(IsInputCompleteCallback, this); m_editline_ap->SetAutoCompleteCallback(AutoCompleteCallback, this); + m_editline_ap->SetPromptCallback(PromptCallback, this); // See if the delegate supports fixing indentation const char *indent_chars = delegate.IOHandlerGetFixIndentationCharacters(); if (indent_chars) { @@ -445,6 +447,15 @@ max_matches, matches); return 0; } + +const char* IOHandlerEditline::PromptCallback(Editline *editline, void *baton) { + IOHandlerEditline *editline_reader = (IOHandlerEditline *)baton; + editline_reader->m_prompt_string = + editline_reader->m_prompt_interpolator.InterpolatePrompt( + editline_reader->m_prompt); + return editline_reader->m_prompt_string.c_str(); +} + #endif const char *IOHandlerEditline::GetPrompt() { @@ -463,7 +474,6 @@ bool IOHandlerEditline::SetPrompt(llvm::StringRef prompt) { m_prompt = prompt; - #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) m_editline_ap->SetPrompt(m_prompt.empty() ? nullptr : m_prompt.c_str()); Index: source/Host/common/Editline.cpp =================================================================== --- source/Host/common/Editline.cpp +++ source/Host/common/Editline.cpp @@ -955,10 +955,17 @@ el_set(m_editline, EL_CLIENTDATA, this); el_set(m_editline, EL_SIGNAL, 0); el_set(m_editline, EL_EDITOR, "emacs"); - el_set(m_editline, EL_PROMPT, - (EditlinePromptCallbackType)([](EditLine *editline) { - return Editline::InstanceFor(editline)->Prompt(); - })); + if (m_prompt_callback) { + el_set(m_editline, EL_PROMPT, + (EditlinePromptCallbackType)([](EditLine *editline) { + return Editline::InstanceFor(editline)->InvokePromptCallback(); + })); + } else { + el_set(m_editline, EL_PROMPT, + (EditlinePromptCallbackType)([](EditLine *editline) { + return Editline::InstanceFor(editline)->Prompt(); + })); + } el_wset(m_editline, EL_GETCFN, (EditlineGetCharCallbackType)([]( EditLine *editline, EditLineGetCharType *c) { @@ -1238,6 +1245,11 @@ return result; } +void Editline::SetPromptCallback(PromptCallbackType callback, void *baton) { + m_prompt_callback = callback; + m_prompt_callback_baton = baton; +} + void Editline::SetAutoCompleteCallback(CompleteCallbackType callback, void *baton) { m_completion_callback = callback; @@ -1385,3 +1397,8 @@ } #endif } + +const char* Editline::InvokePromptCallback() { + return m_prompt_callback(this, m_prompt_callback_baton); +} + Index: source/Interpreter/CMakeLists.txt =================================================================== --- source/Interpreter/CMakeLists.txt +++ source/Interpreter/CMakeLists.txt @@ -41,6 +41,7 @@ OptionGroupVariable.cpp OptionGroupWatchpoint.cpp Options.cpp + PromptInterpolation.cpp Property.cpp ScriptInterpreter.cpp Index: source/Interpreter/PromptInterpolation.cpp =================================================================== --- /dev/null +++ source/Interpreter/PromptInterpolation.cpp @@ -0,0 +1,65 @@ +//===-- PromptInterpolation.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lldb/Interpreter/PromptInterpolation.h" + +#include "lldb/Core/Debugger.h" +#include "lldb/Target/Process.h" +#include "lldb/Target/RegisterContext.h" +#include "lldb/Target/StackFrame.h" +#include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" +#include "lldb/Target/Thread.h" + +#include + +using namespace lldb; +using namespace lldb_private; + +const std::string kFallbackPrompt = "(lldb) "; + +namespace lldb_private { + +std::string PromptInterpolation::InterpolatePrompt( + const std::string& prompt_format) { + std::stringstream interpolated_prompt; + + TargetSP target = m_debugger.GetSelectedTarget(); + + if (!target) { + interpolated_prompt << kFallbackPrompt; + return interpolated_prompt.str(); + } + + StackFrameSP frame_sp; + const SymbolContext *sc = nullptr; + if (target->GetProcessSP()) { + ThreadList& thread_list = target->GetProcessSP()->GetThreadList(); + frame_sp = thread_list.GetSelectedThread()->GetSelectedFrame(); + sc = &frame_sp->GetSymbolContext(eSymbolContextEverything); + } + ExecutionContext exe_ctx(frame_sp); + + FormatEntity::Entry root; + Status parse_status = FormatEntity::Parse(prompt_format, root); + bool formatted = false; + StreamString strm; + if (parse_status.Success()) { + formatted = FormatEntity::Format(root, strm, sc, &exe_ctx, + nullptr, nullptr, false, false); + if (formatted) { + interpolated_prompt << strm.GetData(); + return interpolated_prompt.str(); + } + } + interpolated_prompt << kFallbackPrompt; + return interpolated_prompt.str(); +} + +} // namespace lldb_private