[lldb] colorize symbols in image lookup with a regex pattern (#69422)

Fixes https://github.com/llvm/llvm-project/issues/57372

Previously some work has already been done on this. A PR was generated
but it remained in review:
https://reviews.llvm.org/D136462

In short previous approach was following:
Changing the symbol names (making the searched part colorized) ->
printing them -> restoring the symbol names back in their original form.

The reviewers suggested that instead of changing the symbol table, this
colorization should be done in the dump functions itself. Our strategy
involves passing the searched regex pattern to the existing dump
functions responsible for printing information about the searched
symbol. This pattern is propagated until it reaches the line in the dump
functions responsible for displaying symbol information on screen.

At this point, we've introduced a new function called
"PutCStringColorHighlighted," which takes the searched pattern, a prefix and suffix,
and the text and applies colorization to highlight the pattern in the
output. This approach aims to streamline the symbol search process to
improve readability of search results.

Co-authored-by: José L. Junior <josejunior@10xengineers.ai>
This commit is contained in:
taalhaataahir0102 2023-12-08 16:09:04 +05:00 committed by GitHub
parent faecc736e2
commit c90cb6eee8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 239 additions and 35 deletions

View File

@ -14,6 +14,8 @@
#include "lldb/lldb-private-enumerations.h"
#include "lldb/lldb-types.h"
#include "llvm/ADT/StringRef.h"
#include <cstddef>
#include <cstdint>
@ -237,6 +239,12 @@ public:
/// contains the address, otherwise dumping the range that contains the
/// address.
///
/// \param[in] pattern
/// An optional regex pattern to match against the description. If
/// specified, parts of the description matching this pattern may be
/// highlighted or processed differently. If this parameter is an empty
/// string or not provided, no highlighting is applied.
///
/// \return
/// Returns \b true if the address was able to be displayed.
/// File and load addresses may be unresolved and it may not be
@ -246,8 +254,8 @@ public:
/// \see Address::DumpStyle
bool Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
DumpStyle fallback_style = DumpStyleInvalid,
uint32_t addr_byte_size = UINT32_MAX,
bool all_ranges = false) const;
uint32_t addr_byte_size = UINT32_MAX, bool all_ranges = false,
llvm::StringRef pattern = "") const;
AddressClass GetAddressClass() const;

View File

@ -321,6 +321,10 @@ public:
llvm::StringRef GetAutosuggestionAnsiSuffix() const;
llvm::StringRef GetRegexMatchAnsiPrefix() const;
llvm::StringRef GetRegexMatchAnsiSuffix() const;
bool GetShowDontUsePoHint() const;
bool GetUseSourceCache() const;

View File

@ -174,8 +174,8 @@ public:
void SetFlags(uint32_t flags) { m_flags = flags; }
void GetDescription(Stream *s, lldb::DescriptionLevel level,
Target *target) const;
void GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target,
llvm::StringRef pattern = "") const;
bool IsSynthetic() const { return m_is_synthetic; }

View File

@ -145,13 +145,19 @@ public:
/// is dumped if this flag is \b true, otherwise the line info
/// of the actual inlined function is dumped.
///
/// \param[in] pattern
/// An optional regex pattern to match against the stop context
/// description. If specified, parts of the description matching this
/// pattern may be highlighted or processed differently. If this parameter
/// is an empty string or not provided, no highlighting is applied.
///
/// \return
/// \b true if some text was dumped, \b false otherwise.
bool DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
const Address &so_addr, bool show_fullpaths,
bool show_module, bool show_inlined_frames,
bool show_function_arguments,
bool show_function_name) const;
bool show_function_arguments, bool show_function_name,
llvm::StringRef pattern = "") const;
/// Get the address range contained within a symbol context.
///
@ -217,8 +223,8 @@ public:
/// The symbol that was found, or \b nullptr if none was found.
const Symbol *FindBestGlobalDataSymbol(ConstString name, Status &error);
void GetDescription(Stream *s, lldb::DescriptionLevel level,
Target *target) const;
void GetDescription(Stream *s, lldb::DescriptionLevel level, Target *target,
llvm::StringRef pattern = "") const;
uint32_t GetResolvedMask() const;

View File

@ -231,6 +231,40 @@ public:
/// The string to be output to the stream.
size_t PutCString(llvm::StringRef cstr);
/// Output a C string to the stream with color highlighting.
///
/// Print a C string \a text to the stream, applying color highlighting to
/// the portions of the string that match the regex pattern \a pattern. The
/// pattern is matched as many times as possible throughout the string. If \a
/// pattern is nullptr, then no highlighting is applied.
///
/// The highlighting is applied by enclosing the matching text in ANSI color
/// codes. The \a prefix parameter specifies the ANSI code to start the color
/// (the standard value is assumed to be 'ansi.fg.red', representing red
/// foreground), and the \a suffix parameter specifies the ANSI code to end
/// the color (the standard value is assumed to be 'ansi.normal', resetting to
/// default text style). These constants should be defined appropriately in
/// your environment.
///
/// \param[in] text
/// The string to be output to the stream.
///
/// \param[in] pattern
/// The regex pattern to match against the \a text string. Portions of \a
/// text matching this pattern will be colorized. If this parameter is
/// nullptr, highlighting is not performed.
/// \param[in] prefix
/// The ANSI color code to start colorization. This is
/// environment-dependent.
/// \param[in] suffix
/// The ANSI color code to end colorization. This is
/// environment-dependent.
void PutCStringColorHighlighted(llvm::StringRef text,
llvm::StringRef pattern = "",
llvm::StringRef prefix = "",
llvm::StringRef suffix = "");
/// Output and End of Line character to the stream.
size_t EOL();

View File

@ -8,6 +8,7 @@
#include "CommandObjectTarget.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/IOHandler.h"
#include "lldb/Core/Module.h"
@ -1532,7 +1533,7 @@ static void DumpOsoFilesTable(Stream &strm,
static void DumpAddress(ExecutionContextScope *exe_scope,
const Address &so_addr, bool verbose, bool all_ranges,
Stream &strm) {
Stream &strm, llvm::StringRef pattern = "") {
strm.IndentMore();
strm.Indent(" Address: ");
so_addr.Dump(&strm, exe_scope, Address::DumpStyleModuleWithFileAddress);
@ -1542,13 +1543,14 @@ static void DumpAddress(ExecutionContextScope *exe_scope,
strm.Indent(" Summary: ");
const uint32_t save_indent = strm.GetIndentLevel();
strm.SetIndentLevel(save_indent + 13);
so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription);
so_addr.Dump(&strm, exe_scope, Address::DumpStyleResolvedDescription,
Address::DumpStyleInvalid, UINT32_MAX, false, pattern);
strm.SetIndentLevel(save_indent);
// Print out detailed address information when verbose is enabled
if (verbose) {
strm.EOL();
so_addr.Dump(&strm, exe_scope, Address::DumpStyleDetailedSymbolContext,
Address::DumpStyleInvalid, UINT32_MAX, all_ranges);
Address::DumpStyleInvalid, UINT32_MAX, all_ranges, pattern);
}
strm.IndentLess();
}
@ -1593,6 +1595,7 @@ static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter,
return 0;
SymbolContext sc;
const bool use_color = interpreter.GetDebugger().GetUseColor();
std::vector<uint32_t> match_indexes;
ConstString symbol_name(name);
uint32_t num_matches = 0;
@ -1618,12 +1621,19 @@ static uint32_t LookupSymbolInModule(CommandInterpreter &interpreter,
if (symbol->ValueIsAddress()) {
DumpAddress(
interpreter.GetExecutionContext().GetBestExecutionContextScope(),
symbol->GetAddressRef(), verbose, all_ranges, strm);
symbol->GetAddressRef(), verbose, all_ranges, strm,
use_color && name_is_regex ? name : nullptr);
strm.EOL();
} else {
strm.IndentMore();
strm.Indent(" Name: ");
strm.PutCString(symbol->GetDisplayName().GetStringRef());
llvm::StringRef ansi_prefix =
interpreter.GetDebugger().GetRegexMatchAnsiPrefix();
llvm::StringRef ansi_suffix =
interpreter.GetDebugger().GetRegexMatchAnsiSuffix();
strm.PutCStringColorHighlighted(
symbol->GetDisplayName().GetStringRef(),
use_color ? name : nullptr, ansi_prefix, ansi_suffix);
strm.EOL();
strm.Indent(" Value: ");
strm.Printf("0x%16.16" PRIx64 "\n", symbol->GetRawValue());

View File

@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Declaration.h"
#include "lldb/Core/DumpDataExtractor.h"
#include "lldb/Core/Module.h"
@ -28,6 +29,7 @@
#include "lldb/Target/Process.h"
#include "lldb/Target/SectionLoadList.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/DataExtractor.h"
#include "lldb/Utility/Endian.h"
@ -405,7 +407,7 @@ bool Address::GetDescription(Stream &s, Target &target,
bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
DumpStyle fallback_style, uint32_t addr_size,
bool all_ranges) const {
bool all_ranges, llvm::StringRef pattern) const {
// If the section was nullptr, only load address is going to work unless we
// are trying to deref a pointer
SectionSP section_sp(GetSection());
@ -501,7 +503,6 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
pointer_size = target->GetArchitecture().GetAddressByteSize();
else if (module_sp)
pointer_size = module_sp->GetArchitecture().GetAddressByteSize();
bool showed_info = false;
if (section_sp) {
SectionType sect_type = section_sp->GetType();
@ -515,7 +516,12 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
if (symbol) {
const char *symbol_name = symbol->GetName().AsCString();
if (symbol_name) {
s->PutCString(symbol_name);
llvm::StringRef ansi_prefix =
target->GetDebugger().GetRegexMatchAnsiPrefix();
llvm::StringRef ansi_suffix =
target->GetDebugger().GetRegexMatchAnsiSuffix();
s->PutCStringColorHighlighted(symbol_name, pattern,
ansi_prefix, ansi_suffix);
addr_t delta =
file_Addr - symbol->GetAddressRef().GetFileAddress();
if (delta)
@ -643,7 +649,7 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
pointer_sc.symbol != nullptr) {
s->PutCString(": ");
pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false,
false, true, true);
false, true, true, pattern);
}
}
}
@ -682,19 +688,22 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
// address.
sc.DumpStopContext(s, exe_scope, *this, show_fullpaths,
show_module, show_inlined_frames,
show_function_arguments, show_function_name);
show_function_arguments, show_function_name,
pattern);
} else {
// We found a symbol but it was in a different section so it
// isn't the symbol we should be showing, just show the section
// name + offset
Dump(s, exe_scope, DumpStyleSectionNameOffset);
Dump(s, exe_scope, DumpStyleSectionNameOffset, DumpStyleInvalid,
UINT32_MAX, false, pattern);
}
}
}
}
} else {
if (fallback_style != DumpStyleInvalid)
return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size,
false, pattern);
return false;
}
break;
@ -715,7 +724,7 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
sc.symbol->GetAddressRef().GetSection() != GetSection())
sc.symbol = nullptr;
}
sc.GetDescription(s, eDescriptionLevelBrief, target);
sc.GetDescription(s, eDescriptionLevelBrief, target, pattern);
if (sc.block) {
bool can_create = true;
@ -763,7 +772,8 @@ bool Address::Dump(Stream *s, ExecutionContextScope *exe_scope, DumpStyle style,
}
} else {
if (fallback_style != DumpStyleInvalid)
return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size);
return Dump(s, exe_scope, fallback_style, DumpStyleInvalid, addr_size,
false, pattern);
return false;
}
break;

View File

@ -203,6 +203,14 @@ let Definition = "debugger" in {
Global,
DefaultStringValue<"${ansi.normal}">,
Desc<"When displaying suggestion in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the suggestion.">;
def ShowRegexMatchAnsiPrefix: Property<"show-regex-match-ansi-prefix", "String">,
Global,
DefaultStringValue<"${ansi.fg.red}">,
Desc<"When displaying a regex match in a color-enabled terminal, use the ANSI terminal code specified in this format immediately before the match.">;
def ShowRegexMatchAnsiSuffix: Property<"show-regex-match-ansi-suffix", "String">,
Global,
DefaultStringValue<"${ansi.normal}">,
Desc<"When displaying a regex match in a color-enabled terminal, use the ANSI terminal code specified in this format immediately after the match.">;
def ShowDontUsePoHint: Property<"show-dont-use-po-hint", "Boolean">,
Global,
DefaultTrue,

View File

@ -453,6 +453,18 @@ llvm::StringRef Debugger::GetAutosuggestionAnsiSuffix() const {
idx, g_debugger_properties[idx].default_cstr_value);
}
llvm::StringRef Debugger::GetRegexMatchAnsiPrefix() const {
const uint32_t idx = ePropertyShowRegexMatchAnsiPrefix;
return GetPropertyAtIndexAs<llvm::StringRef>(
idx, g_debugger_properties[idx].default_cstr_value);
}
llvm::StringRef Debugger::GetRegexMatchAnsiSuffix() const {
const uint32_t idx = ePropertyShowRegexMatchAnsiSuffix;
return GetPropertyAtIndexAs<llvm::StringRef>(
idx, g_debugger_properties[idx].default_cstr_value);
}
bool Debugger::GetShowDontUsePoHint() const {
const uint32_t idx = ePropertyShowDontUsePoHint;
return GetPropertyAtIndexAs<bool>(

View File

@ -8,6 +8,8 @@
#include "lldb/Symbol/Symbol.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
#include "lldb/Core/Section.h"
@ -225,7 +227,7 @@ bool Symbol::IsTrampoline() const { return m_type == eSymbolTypeTrampoline; }
bool Symbol::IsIndirect() const { return m_type == eSymbolTypeResolver; }
void Symbol::GetDescription(Stream *s, lldb::DescriptionLevel level,
Target *target) const {
Target *target, llvm::StringRef pattern) const {
s->Printf("id = {0x%8.8x}", m_uid);
if (m_addr_range.GetBaseAddress().GetSection()) {
@ -252,11 +254,20 @@ void Symbol::GetDescription(Stream *s, lldb::DescriptionLevel level,
s->Printf(", value = 0x%16.16" PRIx64,
m_addr_range.GetBaseAddress().GetOffset());
}
ConstString demangled = GetMangled().GetDemangledName();
if (demangled)
s->Printf(", name=\"%s\"", demangled.AsCString());
if (m_mangled.GetMangledName())
s->Printf(", mangled=\"%s\"", m_mangled.GetMangledName().AsCString());
llvm::StringRef ansi_prefix = target->GetDebugger().GetRegexMatchAnsiPrefix();
llvm::StringRef ansi_suffix = target->GetDebugger().GetRegexMatchAnsiSuffix();
if (ConstString demangled = m_mangled.GetDemangledName()) {
s->PutCString(", name=\"");
s->PutCStringColorHighlighted(demangled.GetStringRef(), pattern,
ansi_prefix, ansi_suffix);
s->PutCString("\"");
}
if (ConstString mangled_name = m_mangled.GetMangledName()) {
s->PutCString(", mangled=\"");
s->PutCStringColorHighlighted(mangled_name.GetStringRef(), pattern,
ansi_prefix, ansi_suffix);
s->PutCString("\"");
}
}
void Symbol::Dump(Stream *s, Target *target, uint32_t index,

View File

@ -8,6 +8,7 @@
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Core/Address.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
@ -71,7 +72,8 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
const Address &addr, bool show_fullpaths,
bool show_module, bool show_inlined_frames,
bool show_function_arguments,
bool show_function_name) const {
bool show_function_name,
llvm::StringRef pattern) const {
bool dumped_something = false;
if (show_module && module_sp) {
if (show_fullpaths)
@ -81,7 +83,6 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
s->PutChar('`');
dumped_something = true;
}
if (function != nullptr) {
SymbolContext inline_parent_sc;
Address inline_parent_addr;
@ -94,8 +95,16 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
name = function->GetNameNoArguments();
if (!name)
name = function->GetName();
if (name)
name.Dump(s);
if (name) {
llvm::StringRef ansi_prefix;
llvm::StringRef ansi_suffix;
if (target_sp) {
ansi_prefix = target_sp->GetDebugger().GetRegexMatchAnsiPrefix();
ansi_suffix = target_sp->GetDebugger().GetRegexMatchAnsiSuffix();
}
s->PutCStringColorHighlighted(name.GetStringRef(), pattern, ansi_prefix,
ansi_suffix);
}
}
if (addr.IsValid()) {
@ -163,7 +172,14 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
dumped_something = true;
if (symbol->GetType() == eSymbolTypeTrampoline)
s->PutCString("symbol stub for: ");
symbol->GetName().Dump(s);
llvm::StringRef ansi_prefix;
llvm::StringRef ansi_suffix;
if (target_sp) {
ansi_prefix = target_sp->GetDebugger().GetRegexMatchAnsiPrefix();
ansi_suffix = target_sp->GetDebugger().GetRegexMatchAnsiSuffix();
}
s->PutCStringColorHighlighted(symbol->GetName().GetStringRef(), pattern,
ansi_prefix, ansi_suffix);
}
if (addr.IsValid() && symbol->ValueIsAddress()) {
@ -186,7 +202,8 @@ bool SymbolContext::DumpStopContext(Stream *s, ExecutionContextScope *exe_scope,
}
void SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level,
Target *target) const {
Target *target,
llvm::StringRef pattern) const {
if (module_sp) {
s->Indent(" Module: file = \"");
module_sp->GetFileSpec().Dump(s->AsRawOstream());
@ -246,7 +263,7 @@ void SymbolContext::GetDescription(Stream *s, lldb::DescriptionLevel level,
if (symbol != nullptr) {
s->Indent(" Symbol: ");
symbol->GetDescription(s, level, target);
symbol->GetDescription(s, level, target, pattern);
s->EOL();
}

View File

@ -8,11 +8,13 @@
#include "lldb/Utility/Stream.h"
#include "lldb/Utility/AnsiTerminal.h"
#include "lldb/Utility/Endian.h"
#include "lldb/Utility/VASPrintf.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/LEB128.h"
#include "llvm/Support/Regex.h"
#include <string>
@ -70,6 +72,34 @@ size_t Stream::PutCString(llvm::StringRef str) {
return bytes_written;
}
void Stream::PutCStringColorHighlighted(llvm::StringRef text,
llvm::StringRef pattern,
llvm::StringRef prefix,
llvm::StringRef suffix) {
// Only apply color formatting when a pattern is present and both prefix and
// suffix are specified. In the absence of these conditions, output the text
// without color formatting.
if (pattern.empty() || (prefix.empty() && suffix.empty())) {
PutCString(text);
return;
}
llvm::Regex reg_pattern(pattern);
llvm::SmallVector<llvm::StringRef, 1> matches;
llvm::StringRef remaining = text;
std::string format_str = lldb_private::ansi::FormatAnsiTerminalCodes(
prefix.str() + "%.*s" + suffix.str());
while (reg_pattern.match(remaining, &matches)) {
llvm::StringRef match = matches[0];
size_t match_start_pos = match.data() - remaining.data();
PutCString(remaining.take_front(match_start_pos));
Printf(format_str.c_str(), match.size(), match.data());
remaining = remaining.drop_front(match_start_pos + match.size());
}
if (remaining.size())
PutCString(remaining);
}
// Print a double quoted NULL terminated C string to the stream using the
// printf format in "format".
void Stream::QuotedCString(const char *cstr, const char *format) {

View File

@ -0,0 +1,54 @@
# RUN: %clang_host -g %S/Inputs/main.c -o %t
# Checking simple regex search
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK1
# CHECK1: Name: {{.+}}31mma{{.+}}0min.c
# Checking complex regex searches
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'image lookup -r -s main.c|foo' | FileCheck %s --check-prefix CHECK2
# CHECK2: Name: {{.+}}31mmain.c{{.+}}0m
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'image lookup -r -s m[abc]' | FileCheck %s --check-prefix CHECK3
# CHECK3: Name: {{.+}}31mma{{.+}}0min.c
# Checking to ensure that no attempt is made to color anything when there are no matching symbols found
# RUN: %lldb %t -o 'settings set use-color true' -o 'image lookup -r -s IMPPATTERN123456' | FileCheck %s --check-prefix CHECK4
# CHECK4-NOT: {{[0-9]+}} symbols match the regular expression
# Checking multiple matches on same symbol
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'image lookup -r -s (ma|n)' | FileCheck %s --check-prefix CHECK5
# CHECK5: Name: {{.+}}31mma{{.+}}0mi{{.+}}31mn{{.+}}0m.c
# Checking no colorization without regex search
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'image lookup -s main' | FileCheck %s --check-prefix CHECK6
# CHECK6: Summary: {{.+}}`main at main.c:2
# Checking no colorization when use-color is false
# RUN: %lldb %t -b -o 'settings set use-color false' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK7
# CHECK7: Name: main.c
# Checking for custom colors
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'settings set show-regex-match-ansi-prefix ${ansi.fg.green}' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK8
# CHECK8: Name: {{.+}}32mma{{.+}}0min.c
# Checking for functionality when there's prefix but no suffix
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'settings set show-regex-match-ansi-prefix ${ansi.fg.red}' -o 'settings set show-regex-match-ansi-suffix ""' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK9
# CHECK9: Name: {{.+}}31mmain.c
# Checking for functionality when there's suffix but no prefix
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'settings set show-regex-match-ansi-prefix ""' -o 'settings set show-regex-match-ansi-suffix ${ansi.fg.red}' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK10
# CHECK10: Name: ma{{.+}}31min.c
# Checking for no colorization when there's neither suffix nor prefix
# RUN: %lldb %t -b -o 'settings set use-color true' -o 'settings set show-regex-match-ansi-prefix ""' -o 'settings set show-regex-match-ansi-suffix ""' -o 'image lookup -r -s ma' | FileCheck %s --check-prefix CHECK11
# CHECK11: Name: main.c