Skip to content

Commit e416887

Browse files
authored
Merge pull request #10650 from swiftlang/cherrypick-swift/release/6.2-a37e475efc22-3e235a7c601d-45cd708184e1
[lldb] Change the statusline format to print "no target"
2 parents 2b83cd9 + 1aae913 commit e416887

File tree

5 files changed

+160
-69
lines changed

5 files changed

+160
-69
lines changed

lldb/include/lldb/Core/FormatEntity.h

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111

1212
#include "lldb/lldb-enumerations.h"
1313
#include "lldb/lldb-types.h"
14+
#include "llvm/ADT/SmallVector.h"
1415
#include <algorithm>
1516
#include <cstddef>
1617
#include <cstdint>
17-
1818
#include <string>
1919
#include <vector>
2020

@@ -158,9 +158,7 @@ struct Entry {
158158
}
159159

160160
Entry(Type t = Type::Invalid, const char *s = nullptr,
161-
const char *f = nullptr)
162-
: string(s ? s : ""), printf_format(f ? f : ""), type(t) {}
163-
161+
const char *f = nullptr);
164162
Entry(llvm::StringRef s);
165163
Entry(char ch);
166164

@@ -170,15 +168,19 @@ struct Entry {
170168

171169
void AppendText(const char *cstr);
172170

173-
void AppendEntry(const Entry &&entry) { children.push_back(entry); }
171+
void AppendEntry(const Entry &&entry);
172+
173+
void StartAlternative();
174174

175175
void Clear() {
176176
string.clear();
177177
printf_format.clear();
178-
children.clear();
178+
children_stack.clear();
179+
children_stack.emplace_back();
179180
type = Type::Invalid;
180181
fmt = lldb::eFormatDefault;
181182
number = 0;
183+
level = 0;
182184
deref = false;
183185
}
184186

@@ -191,13 +193,7 @@ struct Entry {
191193
return false;
192194
if (printf_format != rhs.printf_format)
193195
return false;
194-
const size_t n = children.size();
195-
const size_t m = rhs.children.size();
196-
for (size_t i = 0; i < std::min<size_t>(n, m); ++i) {
197-
if (!(children[i] == rhs.children[i]))
198-
return false;
199-
}
200-
if (children != rhs.children)
196+
if (children_stack != rhs.children_stack)
201197
return false;
202198
if (type != rhs.type)
203199
return false;
@@ -208,9 +204,18 @@ struct Entry {
208204
return true;
209205
}
210206

207+
std::vector<Entry> &GetChildren();
208+
211209
std::string string;
212210
std::string printf_format;
213-
std::vector<Entry> children;
211+
212+
/// A stack of children entries, used by Scope entries to provide alterantive
213+
/// children. All other entries have a stack of size 1.
214+
/// @{
215+
llvm::SmallVector<std::vector<Entry>, 1> children_stack;
216+
size_t level = 0;
217+
/// @}
218+
214219
Type type;
215220
lldb::Format fmt = lldb::eFormatDefault;
216221
lldb::addr_t number = 0;

lldb/source/Core/CoreProperties.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ let Definition = "debugger" in {
237237
: Property<"statusline-format", "FormatEntity">,
238238
Global,
239239
DefaultStringValue<
240-
"${ansi.negative}{${target.file.basename}}{ "
240+
"${ansi.negative}{${target.file.basename}|no target}{ "
241241
"${separator}${line.file.basename}:${line.number}:${line.column}}{ "
242242
"${separator}${thread.stop-reason}}{ "
243243
"${separator}{${progress.count} }${progress.message}}">,

lldb/source/Core/FormatEntity.cpp

Lines changed: 63 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
#include "llvm/Support/Regex.h"
6161
#include "llvm/TargetParser/Triple.h"
6262

63+
#include <cassert>
6364
#include <cctype>
6465
#include <cinttypes>
6566
#include <cstdio>
@@ -281,31 +282,53 @@ constexpr Definition g_top_level_entries[] = {
281282
constexpr Definition g_root = Entry::DefinitionWithChildren(
282283
"<root>", EntryType::Root, g_top_level_entries);
283284

285+
FormatEntity::Entry::Entry(Type t, const char *s, const char *f)
286+
: string(s ? s : ""), printf_format(f ? f : ""), children_stack({{}}),
287+
type(t) {}
288+
284289
FormatEntity::Entry::Entry(llvm::StringRef s)
285-
: string(s.data(), s.size()), printf_format(), children(),
286-
type(Type::String) {}
290+
: string(s.data(), s.size()), children_stack({{}}), type(Type::String) {}
287291

288292
FormatEntity::Entry::Entry(char ch)
289-
: string(1, ch), printf_format(), children(), type(Type::String) {}
293+
: string(1, ch), printf_format(), children_stack({{}}), type(Type::String) {
294+
}
295+
296+
std::vector<Entry> &FormatEntity::Entry::GetChildren() {
297+
assert(level < children_stack.size());
298+
return children_stack[level];
299+
}
290300

291301
void FormatEntity::Entry::AppendChar(char ch) {
292-
if (children.empty() || children.back().type != Entry::Type::String)
293-
children.push_back(Entry(ch));
302+
auto &entries = GetChildren();
303+
if (entries.empty() || entries.back().type != Entry::Type::String)
304+
entries.push_back(Entry(ch));
294305
else
295-
children.back().string.append(1, ch);
306+
entries.back().string.append(1, ch);
296307
}
297308

298309
void FormatEntity::Entry::AppendText(const llvm::StringRef &s) {
299-
if (children.empty() || children.back().type != Entry::Type::String)
300-
children.push_back(Entry(s));
310+
auto &entries = GetChildren();
311+
if (entries.empty() || entries.back().type != Entry::Type::String)
312+
entries.push_back(Entry(s));
301313
else
302-
children.back().string.append(s.data(), s.size());
314+
entries.back().string.append(s.data(), s.size());
303315
}
304316

305317
void FormatEntity::Entry::AppendText(const char *cstr) {
306318
return AppendText(llvm::StringRef(cstr));
307319
}
308320

321+
void FormatEntity::Entry::AppendEntry(const Entry &&entry) {
322+
auto &entries = GetChildren();
323+
entries.push_back(entry);
324+
}
325+
326+
void FormatEntity::Entry::StartAlternative() {
327+
assert(type == Entry::Type::Scope);
328+
children_stack.emplace_back();
329+
level++;
330+
}
331+
309332
#define ENUM_TO_CSTR(eee) \
310333
case FormatEntity::Entry::Type::eee: \
311334
return #eee
@@ -405,8 +428,9 @@ void FormatEntity::Entry::Dump(Stream &s, int depth) const {
405428
if (deref)
406429
s.Printf("deref = true, ");
407430
s.EOL();
408-
for (const auto &child : children) {
409-
child.Dump(s, depth + 1);
431+
for (const auto &children : children_stack) {
432+
for (const auto &child : children)
433+
child.Dump(s, depth + 1);
410434
}
411435
}
412436

@@ -1308,7 +1332,7 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
13081332
return true;
13091333

13101334
case Entry::Type::Root:
1311-
for (const auto &child : entry.children) {
1335+
for (const auto &child : entry.children_stack[0]) {
13121336
if (!Format(child, s, sc, exe_ctx, addr, valobj, function_changed,
13131337
initial_function)) {
13141338
return false; // If any item of root fails, then the formatting fails
@@ -1322,19 +1346,26 @@ bool FormatEntity::Format(const Entry &entry, Stream &s,
13221346

13231347
case Entry::Type::Scope: {
13241348
StreamString scope_stream;
1325-
bool success = false;
1326-
for (const auto &child : entry.children) {
1327-
success = Format(child, scope_stream, sc, exe_ctx, addr, valobj,
1328-
function_changed, initial_function);
1329-
if (!success)
1330-
break;
1349+
auto format_children = [&](const std::vector<Entry> &children) {
1350+
scope_stream.Clear();
1351+
for (const auto &child : children) {
1352+
if (!Format(child, scope_stream, sc, exe_ctx, addr, valobj,
1353+
function_changed, initial_function))
1354+
return false;
1355+
}
1356+
return true;
1357+
};
1358+
1359+
for (auto &children : entry.children_stack) {
1360+
if (format_children(children)) {
1361+
s.Write(scope_stream.GetString().data(),
1362+
scope_stream.GetString().size());
1363+
return true;
1364+
}
13311365
}
1332-
// Only if all items in a scope succeed, then do we print the output into
1333-
// the main stream
1334-
if (success)
1335-
s.Write(scope_stream.GetString().data(), scope_stream.GetString().size());
1336-
}
1366+
13371367
return true; // Scopes always successfully print themselves
1368+
}
13381369

13391370
case Entry::Type::Variable:
13401371
case Entry::Type::VariableSynthetic:
@@ -2132,7 +2163,7 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
21322163
uint32_t depth) {
21332164
Status error;
21342165
while (!format.empty() && error.Success()) {
2135-
const size_t non_special_chars = format.find_first_of("${}\\");
2166+
const size_t non_special_chars = format.find_first_of("${}\\|");
21362167

21372168
if (non_special_chars == llvm::StringRef::npos) {
21382169
// No special characters, just string bytes so add them and we are done
@@ -2169,6 +2200,14 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
21692200
.drop_front(); // Skip the '}' as we are at the end of the scope
21702201
return error;
21712202

2203+
case '|':
2204+
format = format.drop_front(); // Skip the '|'
2205+
if (parent_entry.type == Entry::Type::Scope)
2206+
parent_entry.StartAlternative();
2207+
else
2208+
parent_entry.AppendChar('|');
2209+
break;
2210+
21722211
case '\\': {
21732212
format = format.drop_front(); // Skip the '\' character
21742213
if (format.empty()) {

lldb/test/API/functionalities/statusline/TestStatusline.py

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@
66
from lldbsuite.test.lldbpexpect import PExpectTest
77

88

9+
# PExpect uses many timeouts internally and doesn't play well
10+
# under ASAN on a loaded machine..
11+
@skipIfAsan
912
class TestStatusline(PExpectTest):
13+
# Change this value to something smaller to make debugging this test less
14+
# tedious.
15+
TIMEOUT = 60
16+
17+
TERMINAL_HEIGHT = 10
18+
TERMINAL_WIDTH = 60
19+
1020
def do_setup(self):
1121
# Create a target and run to a breakpoint.
1222
exe = self.getBuildArtifact("a.out")
@@ -15,36 +25,34 @@ def do_setup(self):
1525
)
1626
self.expect('breakpoint set -p "Break here"', substrs=["Breakpoint 1"])
1727
self.expect("run", substrs=["stop reason"])
28+
self.resize()
29+
30+
def resize(self):
31+
# Change the terminal dimensions. When we launch the tests, we reset
32+
# all the settings, leaving the terminal dimensions unset.
33+
self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH)
1834

19-
# PExpect uses many timeouts internally and doesn't play well
20-
# under ASAN on a loaded machine..
21-
@skipIfAsan
2235
def test(self):
2336
"""Basic test for the statusline."""
2437
self.build()
25-
self.launch()
38+
self.launch(timeout=self.TIMEOUT)
2639
self.do_setup()
2740

28-
# Change the terminal dimensions.
29-
terminal_height = 10
30-
terminal_width = 60
31-
self.child.setwinsize(terminal_height, terminal_width)
32-
3341
# Enable the statusline and check for the control character and that we
3442
# can see the target, the location and the stop reason.
3543
self.expect('set set separator "| "')
3644
self.expect(
3745
"set set show-statusline true",
3846
[
39-
"\x1b[0;{}r".format(terminal_height - 1),
47+
"\x1b[0;{}r".format(self.TERMINAL_HEIGHT - 1),
4048
"a.out | main.c:2:11 | breakpoint 1.1 ",
4149
],
4250
)
4351

4452
# Change the terminal dimensions and make sure it's reflected immediately.
45-
self.child.setwinsize(terminal_height, 25)
53+
self.child.setwinsize(self.TERMINAL_HEIGHT, 25)
4654
self.child.expect(re.escape("a.out | main.c:2:11 | bre"))
47-
self.child.setwinsize(terminal_height, terminal_width)
55+
self.child.setwinsize(self.TERMINAL_HEIGHT, self.TERMINAL_WIDTH)
4856

4957
# Change the separator.
5058
self.expect('set set separator "S "', ["a.out S main.c:2:11"])
@@ -58,23 +66,15 @@ def test(self):
5866

5967
# Hide the statusline and check or the control character.
6068
self.expect(
61-
"set set show-statusline false", ["\x1b[0;{}r".format(terminal_height)]
69+
"set set show-statusline false", ["\x1b[0;{}r".format(self.TERMINAL_HEIGHT)]
6270
)
6371

64-
# PExpect uses many timeouts internally and doesn't play well
65-
# under ASAN on a loaded machine..
66-
@skipIfAsan
6772
def test_no_color(self):
6873
"""Basic test for the statusline with colors disabled."""
6974
self.build()
70-
self.launch(use_colors=False)
75+
self.launch(use_colors=False, timeout=self.TIMEOUT)
7176
self.do_setup()
7277

73-
# Change the terminal dimensions.
74-
terminal_height = 10
75-
terminal_width = 60
76-
self.child.setwinsize(terminal_height, terminal_width)
77-
7878
# Enable the statusline and check for the "reverse video" control character.
7979
self.expect(
8080
"set set show-statusline true",
@@ -87,15 +87,20 @@ def test_deadlock(self):
8787
"""Regression test for lock inversion between the statusline mutex and
8888
the output mutex."""
8989
self.build()
90-
self.launch(extra_args=["-o", "settings set use-color false"])
90+
self.launch(
91+
extra_args=["-o", "settings set use-color false"], timeout=self.TIMEOUT
92+
)
9193
self.child.expect("(lldb)")
92-
93-
# Change the terminal dimensions.
94-
terminal_height = 10
95-
terminal_width = 60
96-
self.child.setwinsize(terminal_height, terminal_width)
94+
self.resize()
9795

9896
exe = self.getBuildArtifact("a.out")
9997

10098
self.expect("file {}".format(exe), ["Current executable"])
10199
self.expect("help", ["Debugger commands"])
100+
101+
def test_no_target(self):
102+
"""Test that we print "no target" when launched without a target."""
103+
self.launch(timeout=self.TIMEOUT)
104+
self.resize()
105+
106+
self.expect("set set show-statusline true", ["no target"])

0 commit comments

Comments
 (0)