Skip to content

Commit 06791d4

Browse files
committed
[lldb] Improve summary string handling of dollar chars (llvm#98190)
This improves the handling of `$` (dollar) characters in summary strings in the following ways: 1. When a `$` is not followed by an open paren (`{`), it should be treated as a literal character and preserved in the output. Previously, the dollar would be consumed by the parser and not shown in the output. 2. When a `$` is the last character of a format string, this change eliminates the infinite loop lldb would enter into. rdar://131392446 (cherry picked from commit 10f3f06)
1 parent ffe3bf3 commit 06791d4

File tree

4 files changed

+148
-125
lines changed

4 files changed

+148
-125
lines changed

lldb/source/Core/FormatEntity.cpp

Lines changed: 119 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,148 +2139,142 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
21392139
} break;
21402140

21412141
case '$':
2142-
if (format.size() == 1) {
2143-
// '$' at the end of a format string, just print the '$'
2142+
format = format.drop_front(); // Skip the '$'
2143+
if (format.empty() || format.front() != '{') {
2144+
// Print '$' when not followed by '{'.
21442145
parent_entry.AppendText("$");
21452146
} else {
2146-
format = format.drop_front(); // Skip the '$'
2147-
2148-
if (format[0] == '{') {
2149-
format = format.drop_front(); // Skip the '{'
2150-
2151-
llvm::StringRef variable, variable_format;
2152-
error = FormatEntity::ExtractVariableInfo(format, variable,
2153-
variable_format);
2154-
if (error.Fail())
2155-
return error;
2156-
bool verify_is_thread_id = false;
2157-
Entry entry;
2158-
if (!variable_format.empty()) {
2159-
entry.printf_format = variable_format.str();
2160-
2161-
// If the format contains a '%' we are going to assume this is a
2162-
// printf style format. So if you want to format your thread ID
2163-
// using "0x%llx" you can use: ${thread.id%0x%llx}
2164-
//
2165-
// If there is no '%' in the format, then it is assumed to be a
2166-
// LLDB format name, or one of the extended formats specified in
2167-
// the switch statement below.
2168-
2169-
if (entry.printf_format.find('%') == std::string::npos) {
2170-
bool clear_printf = false;
2171-
2172-
if (FormatManager::GetFormatFromCString(
2173-
entry.printf_format.c_str(), false, entry.fmt)) {
2174-
// We have an LLDB format, so clear the printf format
2147+
format = format.drop_front(); // Skip the '{'
2148+
2149+
llvm::StringRef variable, variable_format;
2150+
error = FormatEntity::ExtractVariableInfo(format, variable,
2151+
variable_format);
2152+
if (error.Fail())
2153+
return error;
2154+
bool verify_is_thread_id = false;
2155+
Entry entry;
2156+
if (!variable_format.empty()) {
2157+
entry.printf_format = variable_format.str();
2158+
2159+
// If the format contains a '%' we are going to assume this is a
2160+
// printf style format. So if you want to format your thread ID
2161+
// using "0x%llx" you can use: ${thread.id%0x%llx}
2162+
//
2163+
// If there is no '%' in the format, then it is assumed to be a
2164+
// LLDB format name, or one of the extended formats specified in
2165+
// the switch statement below.
2166+
2167+
if (entry.printf_format.find('%') == std::string::npos) {
2168+
bool clear_printf = false;
2169+
2170+
if (FormatManager::GetFormatFromCString(entry.printf_format.c_str(),
2171+
false, entry.fmt)) {
2172+
// We have an LLDB format, so clear the printf format
2173+
clear_printf = true;
2174+
} else if (entry.printf_format.size() == 1) {
2175+
switch (entry.printf_format[0]) {
2176+
case '@': // if this is an @ sign, print ObjC description
2177+
entry.number = ValueObject::
2178+
eValueObjectRepresentationStyleLanguageSpecific;
21752179
clear_printf = true;
2176-
} else if (entry.printf_format.size() == 1) {
2177-
switch (entry.printf_format[0]) {
2178-
case '@': // if this is an @ sign, print ObjC description
2179-
entry.number = ValueObject::
2180-
eValueObjectRepresentationStyleLanguageSpecific;
2181-
clear_printf = true;
2182-
break;
2183-
case 'V': // if this is a V, print the value using the default
2184-
// format
2185-
entry.number =
2186-
ValueObject::eValueObjectRepresentationStyleValue;
2187-
clear_printf = true;
2188-
break;
2189-
case 'L': // if this is an L, print the location of the value
2190-
entry.number =
2191-
ValueObject::eValueObjectRepresentationStyleLocation;
2192-
clear_printf = true;
2193-
break;
2194-
case 'S': // if this is an S, print the summary after all
2195-
entry.number =
2196-
ValueObject::eValueObjectRepresentationStyleSummary;
2197-
clear_printf = true;
2198-
break;
2199-
case '#': // if this is a '#', print the number of children
2200-
entry.number =
2201-
ValueObject::eValueObjectRepresentationStyleChildrenCount;
2202-
clear_printf = true;
2203-
break;
2204-
case 'T': // if this is a 'T', print the type
2205-
entry.number =
2206-
ValueObject::eValueObjectRepresentationStyleType;
2207-
clear_printf = true;
2208-
break;
2209-
case 'N': // if this is a 'N', print the name
2210-
entry.number =
2211-
ValueObject::eValueObjectRepresentationStyleName;
2212-
clear_printf = true;
2213-
break;
2214-
case '>': // if this is a '>', print the expression path
2215-
entry.number = ValueObject::
2216-
eValueObjectRepresentationStyleExpressionPath;
2217-
clear_printf = true;
2218-
break;
2219-
default:
2220-
error.SetErrorStringWithFormat("invalid format: '%s'",
2221-
entry.printf_format.c_str());
2222-
return error;
2223-
}
2224-
} else if (FormatManager::GetFormatFromCString(
2225-
entry.printf_format.c_str(), true, entry.fmt)) {
2180+
break;
2181+
case 'V': // if this is a V, print the value using the default
2182+
// format
2183+
entry.number =
2184+
ValueObject::eValueObjectRepresentationStyleValue;
22262185
clear_printf = true;
2227-
} else if (entry.printf_format == "tid") {
2228-
verify_is_thread_id = true;
2229-
} else {
2186+
break;
2187+
case 'L': // if this is an L, print the location of the value
2188+
entry.number =
2189+
ValueObject::eValueObjectRepresentationStyleLocation;
2190+
clear_printf = true;
2191+
break;
2192+
case 'S': // if this is an S, print the summary after all
2193+
entry.number =
2194+
ValueObject::eValueObjectRepresentationStyleSummary;
2195+
clear_printf = true;
2196+
break;
2197+
case '#': // if this is a '#', print the number of children
2198+
entry.number =
2199+
ValueObject::eValueObjectRepresentationStyleChildrenCount;
2200+
clear_printf = true;
2201+
break;
2202+
case 'T': // if this is a 'T', print the type
2203+
entry.number = ValueObject::eValueObjectRepresentationStyleType;
2204+
clear_printf = true;
2205+
break;
2206+
case 'N': // if this is a 'N', print the name
2207+
entry.number = ValueObject::eValueObjectRepresentationStyleName;
2208+
clear_printf = true;
2209+
break;
2210+
case '>': // if this is a '>', print the expression path
2211+
entry.number =
2212+
ValueObject::eValueObjectRepresentationStyleExpressionPath;
2213+
clear_printf = true;
2214+
break;
2215+
default:
22302216
error.SetErrorStringWithFormat("invalid format: '%s'",
22312217
entry.printf_format.c_str());
22322218
return error;
22332219
}
2234-
2235-
// Our format string turned out to not be a printf style format
2236-
// so lets clear the string
2237-
if (clear_printf)
2238-
entry.printf_format.clear();
2220+
} else if (FormatManager::GetFormatFromCString(
2221+
entry.printf_format.c_str(), true, entry.fmt)) {
2222+
clear_printf = true;
2223+
} else if (entry.printf_format == "tid") {
2224+
verify_is_thread_id = true;
2225+
} else {
2226+
error.SetErrorStringWithFormat("invalid format: '%s'",
2227+
entry.printf_format.c_str());
2228+
return error;
22392229
}
2240-
}
22412230

2242-
// Check for dereferences
2243-
if (variable[0] == '*') {
2244-
entry.deref = true;
2245-
variable = variable.drop_front();
2231+
// Our format string turned out to not be a printf style format
2232+
// so lets clear the string
2233+
if (clear_printf)
2234+
entry.printf_format.clear();
22462235
}
2236+
}
22472237

2248-
error = ParseEntry(variable, &g_root, entry);
2249-
if (error.Fail())
2250-
return error;
2238+
// Check for dereferences
2239+
if (variable[0] == '*') {
2240+
entry.deref = true;
2241+
variable = variable.drop_front();
2242+
}
22512243

2252-
if (verify_is_thread_id) {
2253-
if (entry.type != Entry::Type::ThreadID &&
2254-
entry.type != Entry::Type::ThreadProtocolID) {
2255-
error.SetErrorString("the 'tid' format can only be used on "
2256-
"${thread.id} and ${thread.protocol_id}");
2257-
}
2244+
error = ParseEntry(variable, &g_root, entry);
2245+
if (error.Fail())
2246+
return error;
2247+
2248+
if (verify_is_thread_id) {
2249+
if (entry.type != Entry::Type::ThreadID &&
2250+
entry.type != Entry::Type::ThreadProtocolID) {
2251+
error.SetErrorString("the 'tid' format can only be used on "
2252+
"${thread.id} and ${thread.protocol_id}");
22582253
}
2254+
}
22592255

2260-
switch (entry.type) {
2261-
case Entry::Type::Variable:
2262-
case Entry::Type::VariableSynthetic:
2263-
if (entry.number == 0) {
2264-
if (entry.string.empty())
2265-
entry.number =
2266-
ValueObject::eValueObjectRepresentationStyleValue;
2267-
else
2268-
entry.number =
2269-
ValueObject::eValueObjectRepresentationStyleSummary;
2270-
}
2271-
break;
2272-
default:
2273-
// Make sure someone didn't try to dereference anything but ${var}
2274-
// or ${svar}
2275-
if (entry.deref) {
2276-
error.SetErrorStringWithFormat(
2277-
"${%s} can't be dereferenced, only ${var} and ${svar} can.",
2278-
variable.str().c_str());
2279-
return error;
2280-
}
2256+
switch (entry.type) {
2257+
case Entry::Type::Variable:
2258+
case Entry::Type::VariableSynthetic:
2259+
if (entry.number == 0) {
2260+
if (entry.string.empty())
2261+
entry.number = ValueObject::eValueObjectRepresentationStyleValue;
2262+
else
2263+
entry.number =
2264+
ValueObject::eValueObjectRepresentationStyleSummary;
2265+
}
2266+
break;
2267+
default:
2268+
// Make sure someone didn't try to dereference anything but ${var}
2269+
// or ${svar}
2270+
if (entry.deref) {
2271+
error.SetErrorStringWithFormat(
2272+
"${%s} can't be dereferenced, only ${var} and ${svar} can.",
2273+
variable.str().c_str());
2274+
return error;
22812275
}
2282-
parent_entry.AppendEntry(std::move(entry));
22832276
}
2277+
parent_entry.AppendEntry(std::move(entry));
22842278
}
22852279
break;
22862280
}
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: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import lldb
2+
from lldbsuite.test.decorators import *
3+
from lldbsuite.test.lldbtest import *
4+
from lldbsuite.test import lldbutil
5+
6+
7+
class TestCase(TestBase):
8+
def test_summary_string_with_bare_dollar_char(self):
9+
self.build()
10+
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
11+
self.runCmd("type summary add --summary-string '$ $CASH $' --no-value Dollars")
12+
self.expect("v cash", startstr="(Dollars) cash = $ $CASH $")
13+
14+
def test_summary_string_with_bare_dollar_char_before_var(self):
15+
self.build()
16+
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
17+
self.runCmd("type summary add --summary-string '$${var}' --no-value Dollars")
18+
self.expect("v cash", startstr="(Dollars) cash = $99")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#include <stdio.h>
2+
3+
typedef int Dollars;
4+
5+
int main() {
6+
Dollars cash = 99;
7+
printf("break here: %d\n", cash);
8+
return 0;
9+
}

0 commit comments

Comments
 (0)