This was removed in https://github.com/llvm/llvm-project/pull/135343 in favour of making it a format variable, which we do here. This follows the precedent of the `[opt]` and `[artificial]` markers. Before: ``` thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 * frame #0: 0x000000010000037c a.out`inlined1() at inline.cpp:4:3 frame #1: 0x000000010000037c a.out`regular() at inline.cpp:6:17 frame #2: 0x00000001000003b8 a.out`inlined2() at inline.cpp:7:43 frame #3: 0x00000001000003b4 a.out`main at inline.cpp:10:3 frame #4: 0x0000000186345be4 dyld`start + 7040 ``` After (note the `[inlined]` markers): ``` thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 * frame #0: 0x000000010000037c a.out`inlined1() at inline.cpp:4:3 [inlined] frame #1: 0x000000010000037c a.out`regular() at inline.cpp:6:17 frame #2: 0x00000001000003b8 a.out`inlined2() at inline.cpp:7:43 [inlined] frame #3: 0x00000001000003b4 a.out`main at inline.cpp:10:3 frame #4: 0x0000000186345be4 dyld`start + 7040 ``` rdar://152642178
206 lines
6.3 KiB
C++
206 lines
6.3 KiB
C++
//===-- FormatEntityTest.cpp ---------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lldb/Core/FormatEntity.h"
|
|
#include "lldb/Utility/Status.h"
|
|
#include "lldb/Utility/StreamString.h"
|
|
#include "llvm/ADT/StringRef.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Testing/Support/Error.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace lldb_private;
|
|
using namespace llvm;
|
|
|
|
using Definition = FormatEntity::Entry::Definition;
|
|
using Entry = FormatEntity::Entry;
|
|
|
|
static Expected<std::string> Format(StringRef format_str) {
|
|
StreamString stream;
|
|
FormatEntity::Entry format;
|
|
Status status = FormatEntity::Parse(format_str, format);
|
|
if (status.Fail())
|
|
return status.ToError();
|
|
|
|
FormatEntity::Format(format, stream, nullptr, nullptr, nullptr, nullptr,
|
|
false, false);
|
|
return stream.GetString().str();
|
|
}
|
|
|
|
TEST(FormatEntityTest, DefinitionConstructionNameAndType) {
|
|
Definition d("foo", FormatEntity::Entry::Type::Invalid);
|
|
|
|
EXPECT_STREQ(d.name, "foo");
|
|
EXPECT_EQ(d.string, nullptr);
|
|
EXPECT_EQ(d.type, FormatEntity::Entry::Type::Invalid);
|
|
EXPECT_EQ(d.data, 0UL);
|
|
EXPECT_EQ(d.num_children, 0UL);
|
|
EXPECT_EQ(d.children, nullptr);
|
|
EXPECT_FALSE(d.keep_separator);
|
|
}
|
|
|
|
TEST(FormatEntityTest, DefinitionConstructionNameAndString) {
|
|
Definition d("foo", "string");
|
|
|
|
EXPECT_STREQ(d.name, "foo");
|
|
EXPECT_STREQ(d.string, "string");
|
|
EXPECT_EQ(d.type, FormatEntity::Entry::Type::EscapeCode);
|
|
EXPECT_EQ(d.data, 0UL);
|
|
EXPECT_EQ(d.num_children, 0UL);
|
|
EXPECT_EQ(d.children, nullptr);
|
|
EXPECT_FALSE(d.keep_separator);
|
|
}
|
|
|
|
TEST(FormatEntityTest, DefinitionConstructionNameTypeData) {
|
|
Definition d("foo", FormatEntity::Entry::Type::Invalid, 33);
|
|
|
|
EXPECT_STREQ(d.name, "foo");
|
|
EXPECT_EQ(d.string, nullptr);
|
|
EXPECT_EQ(d.type, FormatEntity::Entry::Type::Invalid);
|
|
EXPECT_EQ(d.data, 33UL);
|
|
EXPECT_EQ(d.num_children, 0UL);
|
|
EXPECT_EQ(d.children, nullptr);
|
|
EXPECT_FALSE(d.keep_separator);
|
|
}
|
|
|
|
TEST(FormatEntityTest, DefinitionConstructionNameTypeChildren) {
|
|
Definition d("foo", FormatEntity::Entry::Type::Invalid, 33);
|
|
Definition parent("parent", FormatEntity::Entry::Type::Invalid, 1, &d);
|
|
EXPECT_STREQ(parent.name, "parent");
|
|
EXPECT_STREQ(parent.string, nullptr);
|
|
EXPECT_EQ(parent.type, FormatEntity::Entry::Type::Invalid);
|
|
EXPECT_EQ(parent.num_children, 1UL);
|
|
EXPECT_EQ(parent.children, &d);
|
|
EXPECT_FALSE(parent.keep_separator);
|
|
|
|
EXPECT_STREQ(parent.children[0].name, "foo");
|
|
EXPECT_EQ(parent.children[0].string, nullptr);
|
|
EXPECT_EQ(parent.children[0].type, FormatEntity::Entry::Type::Invalid);
|
|
EXPECT_EQ(parent.children[0].data, 33UL);
|
|
EXPECT_EQ(parent.children[0].num_children, 0UL);
|
|
EXPECT_EQ(parent.children[0].children, nullptr);
|
|
EXPECT_FALSE(d.keep_separator);
|
|
}
|
|
|
|
constexpr llvm::StringRef lookupStrings[] = {
|
|
"${addr.load}",
|
|
"${addr.file}",
|
|
"${ansi.fg.black}",
|
|
"${ansi.fg.red}",
|
|
"${ansi.fg.green}",
|
|
"${ansi.fg.yellow}",
|
|
"${ansi.fg.blue}",
|
|
"${ansi.fg.purple}",
|
|
"${ansi.fg.cyan}",
|
|
"${ansi.fg.white}",
|
|
"${ansi.bg.black}",
|
|
"${ansi.bg.red}",
|
|
"${ansi.bg.green}",
|
|
"${ansi.bg.yellow}",
|
|
"${ansi.bg.blue}",
|
|
"${ansi.bg.purple}",
|
|
"${ansi.bg.cyan}",
|
|
"${ansi.bg.white}",
|
|
"${file.basename}",
|
|
"${file.dirname}",
|
|
"${file.fullpath}",
|
|
"${frame.index}",
|
|
"${frame.pc}",
|
|
"${frame.fp}",
|
|
"${frame.sp}",
|
|
"${frame.flags}",
|
|
"${frame.no-debug}",
|
|
"${frame.reg.*}",
|
|
"${frame.is-artificial}",
|
|
"${function.id}",
|
|
"${function.name}",
|
|
"${function.name-without-args}",
|
|
"${function.name-with-args}",
|
|
"${function.mangled-name}",
|
|
"${function.addr-offset}",
|
|
"${function.concrete-only-addr-offset-no-padding}",
|
|
"${function.line-offset}",
|
|
"${function.pc-offset}",
|
|
"${function.initial-function}",
|
|
"${function.changed}",
|
|
"${function.is-optimized}",
|
|
"${function.is-inlined}",
|
|
"${line.file.basename}",
|
|
"${line.file.dirname}",
|
|
"${line.file.fullpath}",
|
|
"${line.number}",
|
|
"${line.column}",
|
|
"${line.start-addr}",
|
|
"${line.end-addr}",
|
|
"${module.file.basename}",
|
|
"${module.file.dirname}",
|
|
"${module.file.fullpath}",
|
|
"${process.id}",
|
|
"${process.name}",
|
|
"${process.file.basename}",
|
|
"${process.file.dirname}",
|
|
"${process.file.fullpath}",
|
|
"${script.frame}",
|
|
"${script.process}",
|
|
"${script.target}",
|
|
"${script.thread}",
|
|
"${script.var}",
|
|
"${script.svar}",
|
|
"${script.thread}",
|
|
"${svar.dummy-svar-to-test-wildcard}",
|
|
"${thread.id}",
|
|
"${thread.protocol_id}",
|
|
"${thread.index}",
|
|
"${thread.info.*}",
|
|
"${thread.queue}",
|
|
"${thread.name}",
|
|
"${thread.stop-reason}",
|
|
"${thread.stop-reason-raw}",
|
|
"${thread.return-value}",
|
|
"${thread.completed-expression}",
|
|
"${target.arch}",
|
|
"${target.file.basename}",
|
|
"${target.file.dirname}",
|
|
"${target.file.fullpath}",
|
|
"${var.dummy-var-to-test-wildcard}"};
|
|
|
|
TEST(FormatEntityTest, LookupAllEntriesInTree) {
|
|
for (const llvm::StringRef testString : lookupStrings) {
|
|
Entry e;
|
|
EXPECT_TRUE(FormatEntity::Parse(testString, e).Success())
|
|
<< "Formatting " << testString << " did not succeed";
|
|
}
|
|
}
|
|
|
|
TEST(FormatEntityTest, Scope) {
|
|
// Scope with one alternative.
|
|
EXPECT_THAT_EXPECTED(Format("{${frame.pc}|foo}"), HasValue("foo"));
|
|
|
|
// Scope with multiple alternatives.
|
|
EXPECT_THAT_EXPECTED(Format("{${frame.pc}|${function.name}|foo}"),
|
|
HasValue("foo"));
|
|
|
|
// Escaped pipe inside a scope.
|
|
EXPECT_THAT_EXPECTED(Format("{foo\\|bar}"), HasValue("foo|bar"));
|
|
|
|
// Unescaped pipe outside a scope.
|
|
EXPECT_THAT_EXPECTED(Format("foo|bar"), HasValue("foo|bar"));
|
|
|
|
// Nested scopes. Note that scopes always resolve.
|
|
EXPECT_THAT_EXPECTED(Format("{{${frame.pc}|foo}|{bar}}"), HasValue("foo"));
|
|
EXPECT_THAT_EXPECTED(Format("{{${frame.pc}}|{bar}}"), HasValue(""));
|
|
|
|
// Pipe between scopes.
|
|
EXPECT_THAT_EXPECTED(Format("{foo}|{bar}"), HasValue("foo|bar"));
|
|
EXPECT_THAT_EXPECTED(Format("{foo}||{bar}"), HasValue("foo||bar"));
|
|
|
|
// Empty space between pipes.
|
|
EXPECT_THAT_EXPECTED(Format("{{foo}||{bar}}"), HasValue("foo"));
|
|
EXPECT_THAT_EXPECTED(Format("{${frame.pc}||{bar}}"), HasValue(""));
|
|
}
|