Skip to content

Commit fc0e8fb

Browse files
committed
[lldb][gui] truncate long lines/names if needed
Without this, sources with long lines or variable names may overwrite panel frames, or even overrun to the following line. There's currently no way to scroll left/right in the views, so that should be added to handle these cases. This commit includes fixing constness of some Window functions, and also makes PutCStringTruncated() consistent with the added printf-like variant to take the padding as the first argument (can't add it after the format to the printf-like function). Differential Revision: https://reviews.llvm.org/D85123
1 parent f5e6fba commit fc0e8fb

File tree

4 files changed

+116
-39
lines changed

4 files changed

+116
-39
lines changed

lldb/source/Core/IOHandlerCursesGUI.cpp

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -367,23 +367,23 @@ class Window {
367367
}
368368
void Clear() { ::wclear(m_window); }
369369
void Erase() { ::werase(m_window); }
370-
Rect GetBounds() {
370+
Rect GetBounds() const {
371371
return Rect(GetParentOrigin(), GetSize());
372372
} // Get the rectangle in our parent window
373373
int GetChar() { return ::wgetch(m_window); }
374-
int GetCursorX() { return getcurx(m_window); }
375-
int GetCursorY() { return getcury(m_window); }
376-
Rect GetFrame() {
374+
int GetCursorX() const { return getcurx(m_window); }
375+
int GetCursorY() const { return getcury(m_window); }
376+
Rect GetFrame() const {
377377
return Rect(Point(), GetSize());
378378
} // Get our rectangle in our own coordinate system
379-
Point GetParentOrigin() { return Point(GetParentX(), GetParentY()); }
380-
Size GetSize() { return Size(GetWidth(), GetHeight()); }
381-
int GetParentX() { return getparx(m_window); }
382-
int GetParentY() { return getpary(m_window); }
383-
int GetMaxX() { return getmaxx(m_window); }
384-
int GetMaxY() { return getmaxy(m_window); }
385-
int GetWidth() { return GetMaxX(); }
386-
int GetHeight() { return GetMaxY(); }
379+
Point GetParentOrigin() const { return Point(GetParentX(), GetParentY()); }
380+
Size GetSize() const { return Size(GetWidth(), GetHeight()); }
381+
int GetParentX() const { return getparx(m_window); }
382+
int GetParentY() const { return getpary(m_window); }
383+
int GetMaxX() const { return getmaxx(m_window); }
384+
int GetMaxY() const { return getmaxy(m_window); }
385+
int GetWidth() const { return GetMaxX(); }
386+
int GetHeight() const { return GetMaxY(); }
387387
void MoveCursor(int x, int y) { ::wmove(m_window, y, x); }
388388
void MoveWindow(int x, int y) { MoveWindow(Point(x, y)); }
389389
void Resize(int w, int h) { ::wresize(m_window, h, w); }
@@ -396,7 +396,7 @@ class Window {
396396
::wbkgd(m_window, COLOR_PAIR(color_pair_idx));
397397
}
398398

399-
void PutCStringTruncated(const char *s, int right_pad) {
399+
void PutCStringTruncated(int right_pad, const char *s) {
400400
int bytes_left = GetWidth() - GetCursorX();
401401
if (bytes_left > right_pad) {
402402
bytes_left -= right_pad;
@@ -438,6 +438,20 @@ class Window {
438438
va_end(args);
439439
}
440440

441+
void PrintfTruncated(int right_pad, const char *format, ...)
442+
__attribute__((format(printf, 3, 4))) {
443+
va_list args;
444+
va_start(args, format);
445+
StreamString strm;
446+
strm.PrintfVarArg(format, args);
447+
va_end(args);
448+
PutCStringTruncated(right_pad, strm.GetData());
449+
}
450+
451+
size_t LimitLengthToRestOfLine(size_t length) const {
452+
return std::min<size_t>(length, std::max(0, GetWidth() - GetCursorX() - 1));
453+
}
454+
441455
void Touch() {
442456
::touchwin(m_window);
443457
if (m_parent)
@@ -553,7 +567,7 @@ class Window {
553567
} else {
554568
MoveCursor(1, GetHeight() - 1);
555569
PutChar('[');
556-
PutCStringTruncated(bottom_message, 1);
570+
PutCStringTruncated(1, bottom_message);
557571
}
558572
}
559573
if (attr)
@@ -1911,7 +1925,7 @@ class FrameTreeDelegate : public TreeDelegate {
19111925
if (FormatEntity::Format(m_format, strm, &sc, &exe_ctx, nullptr,
19121926
nullptr, false, false)) {
19131927
int right_pad = 1;
1914-
window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1928+
window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
19151929
}
19161930
}
19171931
}
@@ -1970,7 +1984,7 @@ class ThreadTreeDelegate : public TreeDelegate {
19701984
if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
19711985
nullptr, false, false)) {
19721986
int right_pad = 1;
1973-
window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
1987+
window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
19741988
}
19751989
}
19761990
}
@@ -2060,7 +2074,7 @@ class ThreadsTreeDelegate : public TreeDelegate {
20602074
if (FormatEntity::Format(m_format, strm, nullptr, &exe_ctx, nullptr,
20612075
nullptr, false, false)) {
20622076
int right_pad = 1;
2063-
window.PutCStringTruncated(strm.GetString().str().c_str(), right_pad);
2077+
window.PutCStringTruncated(right_pad, strm.GetString().str().c_str());
20642078
}
20652079
}
20662080
}
@@ -2363,29 +2377,29 @@ class ValueObjectListDelegate : public WindowDelegate {
23632377
window.AttributeOn(A_REVERSE);
23642378

23652379
if (type_name && type_name[0])
2366-
window.Printf("(%s) ", type_name);
2380+
window.PrintfTruncated(1, "(%s) ", type_name);
23672381

23682382
if (name && name[0])
2369-
window.PutCString(name);
2383+
window.PutCStringTruncated(1, name);
23702384

23712385
attr_t changd_attr = 0;
23722386
if (valobj->GetValueDidChange())
23732387
changd_attr = COLOR_PAIR(5) | A_BOLD;
23742388

23752389
if (value && value[0]) {
2376-
window.PutCString(" = ");
2390+
window.PutCStringTruncated(1, " = ");
23772391
if (changd_attr)
23782392
window.AttributeOn(changd_attr);
2379-
window.PutCString(value);
2393+
window.PutCStringTruncated(1, value);
23802394
if (changd_attr)
23812395
window.AttributeOff(changd_attr);
23822396
}
23832397

23842398
if (summary && summary[0]) {
2385-
window.PutChar(' ');
2399+
window.PutCStringTruncated(1, " ");
23862400
if (changd_attr)
23872401
window.AttributeOn(changd_attr);
2388-
window.PutCString(summary);
2402+
window.PutCStringTruncated(1, summary);
23892403
if (changd_attr)
23902404
window.AttributeOff(changd_attr);
23912405
}
@@ -2823,7 +2837,7 @@ bool HelpDialogDelegate::WindowDelegateDraw(Window &window, bool force) {
28232837
while (y <= max_y) {
28242838
window.MoveCursor(x, y);
28252839
window.PutCStringTruncated(
2826-
m_text.GetStringAtIndex(m_first_visible_line + y - min_y), 1);
2840+
1, m_text.GetStringAtIndex(m_first_visible_line + y - min_y));
28272841
++y;
28282842
}
28292843
return true;
@@ -3251,7 +3265,7 @@ class StatusBarWindowDelegate : public WindowDelegate {
32513265
if (thread && FormatEntity::Format(m_format, strm, nullptr, &exe_ctx,
32523266
nullptr, nullptr, false, false)) {
32533267
window.MoveCursor(40, 0);
3254-
window.PutCStringTruncated(strm.GetString().str().c_str(), 1);
3268+
window.PutCStringTruncated(1, strm.GetString().str().c_str());
32553269
}
32563270

32573271
window.MoveCursor(60, 0);
@@ -3477,7 +3491,7 @@ class SourceFileWindowDelegate : public WindowDelegate {
34773491
window.AttributeOn(A_REVERSE);
34783492
window.MoveCursor(1, 1);
34793493
window.PutChar(' ');
3480-
window.PutCStringTruncated(m_title.GetString().str().c_str(), 1);
3494+
window.PutCStringTruncated(1, m_title.GetString().str().c_str());
34813495
int x = window.GetCursorX();
34823496
if (x < window_width - 1) {
34833497
window.Printf("%*s", window_width - x - 1, "");
@@ -3549,8 +3563,8 @@ class SourceFileWindowDelegate : public WindowDelegate {
35493563

35503564
if (highlight_attr)
35513565
window.AttributeOn(highlight_attr);
3552-
const uint32_t line_len =
3553-
m_file_sp->GetLineLength(curr_line + 1, false);
3566+
const uint32_t line_len = window.LimitLengthToRestOfLine(
3567+
m_file_sp->GetLineLength(curr_line + 1, false));
35543568
if (line_len > 0)
35553569
window.PutCString(m_file_sp->PeekLineData(curr_line + 1), line_len);
35563570

@@ -3564,11 +3578,12 @@ class SourceFileWindowDelegate : public WindowDelegate {
35643578
if (stop_description && stop_description[0]) {
35653579
size_t stop_description_len = strlen(stop_description);
35663580
int desc_x = window_width - stop_description_len - 16;
3567-
window.Printf("%*s", desc_x - window.GetCursorX(), "");
3568-
// window.MoveCursor(window_width - stop_description_len - 15,
3569-
// line_y);
3570-
window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3571-
stop_description);
3581+
if (desc_x - window.GetCursorX() > 0)
3582+
window.Printf("%*s", desc_x - window.GetCursorX(), "");
3583+
window.MoveCursor(window_width - stop_description_len - 15,
3584+
line_y);
3585+
window.PrintfTruncated(1, "<<< Thread %u: %s ",
3586+
thread->GetIndexID(), stop_description);
35723587
}
35733588
} else {
35743589
window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
@@ -3699,7 +3714,7 @@ class SourceFileWindowDelegate : public WindowDelegate {
36993714
strm.Printf("%s", mnemonic);
37003715

37013716
int right_pad = 1;
3702-
window.PutCStringTruncated(strm.GetData(), right_pad);
3717+
window.PutCStringTruncated(right_pad, strm.GetData());
37033718

37043719
if (is_pc_line && frame_sp &&
37053720
frame_sp->GetConcreteFrameIndex() == 0) {
@@ -3711,11 +3726,12 @@ class SourceFileWindowDelegate : public WindowDelegate {
37113726
if (stop_description && stop_description[0]) {
37123727
size_t stop_description_len = strlen(stop_description);
37133728
int desc_x = window_width - stop_description_len - 16;
3714-
window.Printf("%*s", desc_x - window.GetCursorX(), "");
3715-
// window.MoveCursor(window_width - stop_description_len - 15,
3716-
// line_y);
3717-
window.Printf("<<< Thread %u: %s ", thread->GetIndexID(),
3718-
stop_description);
3729+
if (desc_x - window.GetCursorX() > 0)
3730+
window.Printf("%*s", desc_x - window.GetCursorX(), "");
3731+
window.MoveCursor(window_width - stop_description_len - 15,
3732+
line_y);
3733+
window.PrintfTruncated(1, "<<< Thread %u: %s ",
3734+
thread->GetIndexID(), stop_description);
37193735
}
37203736
} else {
37213737
window.Printf("%*s", window_width - window.GetCursorX() - 1, "");
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
C_SOURCES := main.c
2+
include Makefile.rules
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
"""
2+
Test that the 'gui' displays long lines/names correctly without overruns.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test.lldbpexpect import PExpectTest
9+
10+
class GuiViewLargeCommandTest(PExpectTest):
11+
12+
mydir = TestBase.compute_mydir(__file__)
13+
14+
# PExpect uses many timeouts internally and doesn't play well
15+
# under ASAN on a loaded machine..
16+
@skipIfAsan
17+
@skipIfCursesSupportMissing
18+
@skipIfRemote # "run" command will not work correctly for remote debug
19+
def test_gui(self):
20+
self.build()
21+
22+
# Limit columns to 80, so that long lines will not be displayed completely.
23+
self.launch(executable=self.getBuildArtifact("a.out"), dimensions=(100,80))
24+
self.expect('br set -f main.c -p "// Break here"', substrs=["Breakpoint 1", "address ="])
25+
self.expect("run", substrs=["stop reason ="])
26+
27+
escape_key = chr(27).encode()
28+
29+
# Start the GUI and close the welcome window.
30+
self.child.sendline("gui")
31+
self.child.send(escape_key)
32+
33+
# Check the sources window.
34+
self.child.expect_exact("Sources")
35+
# The string is copy&pasted from a 80-columns terminal. It will be followed by some
36+
# kind of an escape sequence (color, frame, etc.).
37+
self.child.expect_exact("int a_variable_with_a_very_looooooooooooooooooooooooooo"+chr(27))
38+
# The escape here checks that there's no content drawn by the previous line.
39+
self.child.expect_exact("int shortvar = 1;"+chr(27))
40+
# Check the triggered breakpoint marker on a long line.
41+
self.child.expect_exact("<<< Thread 1: breakpoint 1.1"+chr(27))
42+
43+
# Check the variable window.
44+
self.child.expect_exact("Variables")
45+
self.child.expect_exact("(int) a_variable_with_a_very_looooooooooooooooooooooooooooooo"+chr(27))
46+
self.child.expect_exact("(int) shortvar = 1"+chr(27))
47+
48+
# Press escape to quit the gui
49+
self.child.send(escape_key)
50+
51+
self.expect_prompt()
52+
self.quit()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
int main(int argc, char **argv) {
2+
// This is to be viewed in a 80-column terminal, so make the line below more
3+
// than 120 characters wide, to span at least two lines.
4+
int a_variable_with_a_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name = 22;
5+
int shortvar = 1;
6+
return a_variable_with_a_very_loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong_name; // Break here
7+
}

0 commit comments

Comments
 (0)