Skip to content

[lldb] Improve summary string handling of dollar chars #8961

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 119 additions & 125 deletions lldb/source/Core/FormatEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2139,148 +2139,142 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry,
} break;

case '$':
if (format.size() == 1) {
// '$' at the end of a format string, just print the '$'
format = format.drop_front(); // Skip the '$'
if (format.empty() || format.front() != '{') {
// Print '$' when not followed by '{'.
parent_entry.AppendText("$");
} else {
format = format.drop_front(); // Skip the '$'

if (format[0] == '{') {
format = format.drop_front(); // Skip the '{'

llvm::StringRef variable, variable_format;
error = FormatEntity::ExtractVariableInfo(format, variable,
variable_format);
if (error.Fail())
return error;
bool verify_is_thread_id = false;
Entry entry;
if (!variable_format.empty()) {
entry.printf_format = variable_format.str();

// If the format contains a '%' we are going to assume this is a
// printf style format. So if you want to format your thread ID
// using "0x%llx" you can use: ${thread.id%0x%llx}
//
// If there is no '%' in the format, then it is assumed to be a
// LLDB format name, or one of the extended formats specified in
// the switch statement below.

if (entry.printf_format.find('%') == std::string::npos) {
bool clear_printf = false;

if (FormatManager::GetFormatFromCString(
entry.printf_format.c_str(), false, entry.fmt)) {
// We have an LLDB format, so clear the printf format
format = format.drop_front(); // Skip the '{'

llvm::StringRef variable, variable_format;
error = FormatEntity::ExtractVariableInfo(format, variable,
variable_format);
if (error.Fail())
return error;
bool verify_is_thread_id = false;
Entry entry;
if (!variable_format.empty()) {
entry.printf_format = variable_format.str();

// If the format contains a '%' we are going to assume this is a
// printf style format. So if you want to format your thread ID
// using "0x%llx" you can use: ${thread.id%0x%llx}
//
// If there is no '%' in the format, then it is assumed to be a
// LLDB format name, or one of the extended formats specified in
// the switch statement below.

if (entry.printf_format.find('%') == std::string::npos) {
bool clear_printf = false;

if (FormatManager::GetFormatFromCString(entry.printf_format.c_str(),
false, entry.fmt)) {
// We have an LLDB format, so clear the printf format
clear_printf = true;
} else if (entry.printf_format.size() == 1) {
switch (entry.printf_format[0]) {
case '@': // if this is an @ sign, print ObjC description
entry.number = ValueObject::
eValueObjectRepresentationStyleLanguageSpecific;
clear_printf = true;
} else if (entry.printf_format.size() == 1) {
switch (entry.printf_format[0]) {
case '@': // if this is an @ sign, print ObjC description
entry.number = ValueObject::
eValueObjectRepresentationStyleLanguageSpecific;
clear_printf = true;
break;
case 'V': // if this is a V, print the value using the default
// format
entry.number =
ValueObject::eValueObjectRepresentationStyleValue;
clear_printf = true;
break;
case 'L': // if this is an L, print the location of the value
entry.number =
ValueObject::eValueObjectRepresentationStyleLocation;
clear_printf = true;
break;
case 'S': // if this is an S, print the summary after all
entry.number =
ValueObject::eValueObjectRepresentationStyleSummary;
clear_printf = true;
break;
case '#': // if this is a '#', print the number of children
entry.number =
ValueObject::eValueObjectRepresentationStyleChildrenCount;
clear_printf = true;
break;
case 'T': // if this is a 'T', print the type
entry.number =
ValueObject::eValueObjectRepresentationStyleType;
clear_printf = true;
break;
case 'N': // if this is a 'N', print the name
entry.number =
ValueObject::eValueObjectRepresentationStyleName;
clear_printf = true;
break;
case '>': // if this is a '>', print the expression path
entry.number = ValueObject::
eValueObjectRepresentationStyleExpressionPath;
clear_printf = true;
break;
default:
error.SetErrorStringWithFormat("invalid format: '%s'",
entry.printf_format.c_str());
return error;
}
} else if (FormatManager::GetFormatFromCString(
entry.printf_format.c_str(), true, entry.fmt)) {
break;
case 'V': // if this is a V, print the value using the default
// format
entry.number =
ValueObject::eValueObjectRepresentationStyleValue;
clear_printf = true;
} else if (entry.printf_format == "tid") {
verify_is_thread_id = true;
} else {
break;
case 'L': // if this is an L, print the location of the value
entry.number =
ValueObject::eValueObjectRepresentationStyleLocation;
clear_printf = true;
break;
case 'S': // if this is an S, print the summary after all
entry.number =
ValueObject::eValueObjectRepresentationStyleSummary;
clear_printf = true;
break;
case '#': // if this is a '#', print the number of children
entry.number =
ValueObject::eValueObjectRepresentationStyleChildrenCount;
clear_printf = true;
break;
case 'T': // if this is a 'T', print the type
entry.number = ValueObject::eValueObjectRepresentationStyleType;
clear_printf = true;
break;
case 'N': // if this is a 'N', print the name
entry.number = ValueObject::eValueObjectRepresentationStyleName;
clear_printf = true;
break;
case '>': // if this is a '>', print the expression path
entry.number =
ValueObject::eValueObjectRepresentationStyleExpressionPath;
clear_printf = true;
break;
default:
error.SetErrorStringWithFormat("invalid format: '%s'",
entry.printf_format.c_str());
return error;
}

// Our format string turned out to not be a printf style format
// so lets clear the string
if (clear_printf)
entry.printf_format.clear();
} else if (FormatManager::GetFormatFromCString(
entry.printf_format.c_str(), true, entry.fmt)) {
clear_printf = true;
} else if (entry.printf_format == "tid") {
verify_is_thread_id = true;
} else {
error.SetErrorStringWithFormat("invalid format: '%s'",
entry.printf_format.c_str());
return error;
}
}

// Check for dereferences
if (variable[0] == '*') {
entry.deref = true;
variable = variable.drop_front();
// Our format string turned out to not be a printf style format
// so lets clear the string
if (clear_printf)
entry.printf_format.clear();
}
}

error = ParseEntry(variable, &g_root, entry);
if (error.Fail())
return error;
// Check for dereferences
if (variable[0] == '*') {
entry.deref = true;
variable = variable.drop_front();
}

if (verify_is_thread_id) {
if (entry.type != Entry::Type::ThreadID &&
entry.type != Entry::Type::ThreadProtocolID) {
error.SetErrorString("the 'tid' format can only be used on "
"${thread.id} and ${thread.protocol_id}");
}
error = ParseEntry(variable, &g_root, entry);
if (error.Fail())
return error;

if (verify_is_thread_id) {
if (entry.type != Entry::Type::ThreadID &&
entry.type != Entry::Type::ThreadProtocolID) {
error.SetErrorString("the 'tid' format can only be used on "
"${thread.id} and ${thread.protocol_id}");
}
}

switch (entry.type) {
case Entry::Type::Variable:
case Entry::Type::VariableSynthetic:
if (entry.number == 0) {
if (entry.string.empty())
entry.number =
ValueObject::eValueObjectRepresentationStyleValue;
else
entry.number =
ValueObject::eValueObjectRepresentationStyleSummary;
}
break;
default:
// Make sure someone didn't try to dereference anything but ${var}
// or ${svar}
if (entry.deref) {
error.SetErrorStringWithFormat(
"${%s} can't be dereferenced, only ${var} and ${svar} can.",
variable.str().c_str());
return error;
}
switch (entry.type) {
case Entry::Type::Variable:
case Entry::Type::VariableSynthetic:
if (entry.number == 0) {
if (entry.string.empty())
entry.number = ValueObject::eValueObjectRepresentationStyleValue;
else
entry.number =
ValueObject::eValueObjectRepresentationStyleSummary;
}
break;
default:
// Make sure someone didn't try to dereference anything but ${var}
// or ${svar}
if (entry.deref) {
error.SetErrorStringWithFormat(
"${%s} can't be dereferenced, only ${var} and ${svar} can.",
variable.str().c_str());
return error;
}
parent_entry.AppendEntry(std::move(entry));
}
parent_entry.AppendEntry(std::move(entry));
}
break;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
C_SOURCES = main.c
include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil


class TestCase(TestBase):
def test_summary_string_with_bare_dollar_char(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.runCmd("type summary add --summary-string '$ $CASH $' --no-value Dollars")
self.expect("v cash", startstr="(Dollars) cash = $ $CASH $")

def test_summary_string_with_bare_dollar_char_before_var(self):
self.build()
lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c"))
self.runCmd("type summary add --summary-string '$${var}' --no-value Dollars")
self.expect("v cash", startstr="(Dollars) cash = $99")
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdio.h>

typedef int Dollars;

int main() {
Dollars cash = 99;
printf("break here: %d\n", cash);
return 0;
}