157 lines · cpp
1//===-- Statusline.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "lldb/Core/Statusline.h"10#include "lldb/Core/Debugger.h"11#include "lldb/Core/FormatEntity.h"12#include "lldb/Host/StreamFile.h"13#include "lldb/Interpreter/CommandInterpreter.h"14#include "lldb/Symbol/SymbolContext.h"15#include "lldb/Target/Process.h"16#include "lldb/Target/StackFrame.h"17#include "lldb/Utility/AnsiTerminal.h"18#include "lldb/Utility/StreamString.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/Support/Locale.h"21 22#define ESCAPE "\x1b"23#define ANSI_NORMAL ESCAPE "[0m"24#define ANSI_SAVE_CURSOR ESCAPE "7"25#define ANSI_RESTORE_CURSOR ESCAPE "8"26#define ANSI_CLEAR_BELOW ESCAPE "[J"27#define ANSI_CLEAR_SCREEN ESCAPE "[2J"28#define ANSI_SET_SCROLL_ROWS ESCAPE "[1;%ur"29#define ANSI_TO_START_OF_ROW ESCAPE "[%u;1f"30#define ANSI_REVERSE_VIDEO ESCAPE "[7m"31#define ANSI_UP_ROWS ESCAPE "[%dA"32 33using namespace lldb;34using namespace lldb_private;35 36Statusline::Statusline(Debugger &debugger)37 : m_debugger(debugger), m_terminal_width(m_debugger.GetTerminalWidth()),38 m_terminal_height(m_debugger.GetTerminalHeight()) {}39 40Statusline::~Statusline() { Disable(); }41 42void Statusline::TerminalSizeChanged() {43 m_terminal_width = m_debugger.GetTerminalWidth();44 m_terminal_height = m_debugger.GetTerminalHeight();45 46 UpdateScrollWindow(ResizeStatusline);47 48 // Redraw the old statusline.49 Redraw(std::nullopt);50}51 52void Statusline::Enable(std::optional<ExecutionContextRef> exe_ctx_ref) {53 // Reduce the scroll window to make space for the status bar below.54 UpdateScrollWindow(EnableStatusline);55 56 // Draw the statusline.57 Redraw(exe_ctx_ref);58}59 60void Statusline::Disable() {61 // Extend the scroll window to cover the status bar.62 UpdateScrollWindow(DisableStatusline);63}64 65void Statusline::Draw(std::string str) {66 lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();67 if (!stream_sp)68 return;69 70 str = ansi::TrimAndPad(str, m_terminal_width);71 72 LockedStreamFile locked_stream = stream_sp->Lock();73 locked_stream << ANSI_SAVE_CURSOR;74 locked_stream.Printf(ANSI_TO_START_OF_ROW,75 static_cast<unsigned>(m_terminal_height));76 77 // Use "reverse video" to make sure the statusline has a background. Only do78 // this when colors are disabled, and rely on the statusline format otherwise.79 if (!m_debugger.GetUseColor())80 locked_stream << ANSI_REVERSE_VIDEO;81 82 locked_stream << str;83 locked_stream << ANSI_NORMAL;84 locked_stream << ANSI_RESTORE_CURSOR;85}86 87void Statusline::UpdateScrollWindow(ScrollWindowMode mode) {88 assert(m_terminal_width != 0 && m_terminal_height != 0);89 90 lldb::LockableStreamFileSP stream_sp = m_debugger.GetOutputStreamSP();91 if (!stream_sp)92 return;93 94 const unsigned reduced_scroll_rows = m_terminal_height - 1;95 LockedStreamFile locked_stream = stream_sp->Lock();96 97 switch (mode) {98 case EnableStatusline:99 // Move everything on the screen up.100 locked_stream << '\n';101 locked_stream.Printf(ANSI_UP_ROWS, 1);102 // Reduce the scroll window.103 locked_stream << ANSI_SAVE_CURSOR;104 locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_rows);105 locked_stream << ANSI_RESTORE_CURSOR;106 break;107 case DisableStatusline:108 // Reset the scroll window.109 locked_stream << ANSI_SAVE_CURSOR;110 locked_stream.Printf(ANSI_SET_SCROLL_ROWS,111 static_cast<unsigned>(m_terminal_height));112 locked_stream << ANSI_RESTORE_CURSOR;113 // Clear the screen below to hide the old statusline.114 locked_stream << ANSI_CLEAR_BELOW;115 break;116 case ResizeStatusline:117 // Clear the screen and update the scroll window.118 // FIXME: Find a better solution (#146919).119 locked_stream << ANSI_CLEAR_SCREEN;120 locked_stream.Printf(ANSI_SET_SCROLL_ROWS, reduced_scroll_rows);121 break;122 }123 124 m_debugger.RefreshIOHandler();125}126 127void Statusline::Redraw(std::optional<ExecutionContextRef> exe_ctx_ref) {128 // Update the cached execution context.129 if (exe_ctx_ref)130 m_exe_ctx_ref = *exe_ctx_ref;131 132 // Lock the execution context.133 ExecutionContext exe_ctx =134 m_exe_ctx_ref.Lock(/*thread_and_frame_only_if_stopped=*/false);135 136 // Compute the symbol context if we're stopped.137 SymbolContext sym_ctx;138 llvm::Expected<StoppedExecutionContext> stopped_exe_ctx =139 GetStoppedExecutionContext(&m_exe_ctx_ref);140 if (stopped_exe_ctx) {141 // The StoppedExecutionContext only ensures that we hold the run lock.142 // The process could be in an exited or unloaded state and have no frame.143 if (auto frame_sp = stopped_exe_ctx->GetFrameSP())144 sym_ctx = frame_sp->GetSymbolContext(eSymbolContextEverything);145 } else {146 // We can draw the statusline without being stopped.147 llvm::consumeError(stopped_exe_ctx.takeError());148 }149 150 StreamString stream;151 FormatEntity::Entry format = m_debugger.GetStatuslineFormat();152 FormatEntity::Format(format, stream, &sym_ctx, &exe_ctx, nullptr, nullptr,153 false, false);154 155 Draw(stream.GetString().str());156}157