-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[lldb] upgrade HandleFrameFormatVariable callees to llvm::Expected #144731
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
[lldb] upgrade HandleFrameFormatVariable callees to llvm::Expected #144731
Conversation
@llvm/pr-subscribers-lldb Author: Charles Zablit (charles-zablit) ChangesUpgrade the callees of This patch also bundles the logic of validating the demangled name and information into a single reusable function to reduce code duplication. Full diff: https://github.com/llvm/llvm-project/pull/144731.diff 1 Files Affected:
diff --git a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
index 0f18abb47591d..1810c07652a2b 100644
--- a/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
+++ b/lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
@@ -236,199 +236,140 @@ static bool PrettyPrintFunctionNameWithArgs(Stream &out_stream,
return true;
}
-static std::optional<llvm::StringRef>
-GetDemangledBasename(const SymbolContext &sc) {
+static llvm::Expected<std::pair<llvm::StringRef, DemangledNameInfo>>
+GetAndValidateInfo(const SymbolContext &sc) {
Mangled mangled = sc.GetPossiblyInlinedFunctionName();
if (!mangled)
- return std::nullopt;
+ return llvm::createStringError("Function does not have a mangled name.");
auto demangled_name = mangled.GetDemangledName().GetStringRef();
if (demangled_name.empty())
- return std::nullopt;
+ return llvm::createStringError("Function does not have a demangled name.");
const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
if (!info)
- return std::nullopt;
+ return llvm::createStringError("Function does not have demangled info.");
// Function without a basename is nonsense.
if (!info->hasBasename())
- return std::nullopt;
+ return llvm::createStringError("Info do not have basename range.");
- return demangled_name.slice(info->BasenameRange.first,
- info->BasenameRange.second);
+ return std::make_pair(demangled_name, *info);
}
-static std::optional<llvm::StringRef>
-GetDemangledTemplateArguments(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
+static llvm::Expected<llvm::StringRef>
+GetDemangledBasename(const SymbolContext &sc) {
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
+ return demangled_name.slice(info.BasenameRange.first,
+ info.BasenameRange.second);
+}
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
+static llvm::Expected<llvm::StringRef>
+GetDemangledTemplateArguments(const SymbolContext &sc) {
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
+
+ auto [demangled_name, info] = *info_or_err;
- if (info->ArgumentsRange.first < info->BasenameRange.second)
- return std::nullopt;
+ if (info.ArgumentsRange.first < info.BasenameRange.second)
+ return llvm::createStringError("Arguments in info are invalid.");
- return demangled_name.slice(info->BasenameRange.second,
- info->ArgumentsRange.first);
+ return demangled_name.slice(info.BasenameRange.second,
+ info.ArgumentsRange.first);
}
-static std::optional<llvm::StringRef>
+static llvm::Expected<llvm::StringRef>
GetDemangledReturnTypeLHS(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
+ if (info.ScopeRange.first >= demangled_name.size())
+ return llvm::createStringError("Scope range is invalid.");
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
-
- if (info->ScopeRange.first >= demangled_name.size())
- return std::nullopt;
-
- return demangled_name.substr(0, info->ScopeRange.first);
+ return demangled_name.substr(0, info.ScopeRange.first);
}
-static std::optional<llvm::StringRef>
+static llvm::Expected<llvm::StringRef>
GetDemangledFunctionQualifiers(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
-
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
+ if (info.QualifiersRange.second < info.QualifiersRange.first)
+ return llvm::createStringError("Qualifiers range is invalid.");
- if (info->QualifiersRange.second < info->QualifiersRange.first)
- return std::nullopt;
-
- return demangled_name.slice(info->QualifiersRange.first,
- info->QualifiersRange.second);
+ return demangled_name.slice(info.QualifiersRange.first,
+ info.QualifiersRange.second);
}
-static std::optional<llvm::StringRef>
+static llvm::Expected<llvm::StringRef>
GetDemangledReturnTypeRHS(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
-
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
-
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- if (info->QualifiersRange.first < info->ArgumentsRange.second)
- return std::nullopt;
+ if (info.QualifiersRange.first < info.ArgumentsRange.second)
+ return llvm::createStringError("Qualifiers range is invalid.");
- return demangled_name.slice(info->ArgumentsRange.second,
- info->QualifiersRange.first);
+ return demangled_name.slice(info.ArgumentsRange.second,
+ info.QualifiersRange.first);
}
-static std::optional<llvm::StringRef>
+static llvm::Expected<llvm::StringRef>
GetDemangledScope(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
-
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
-
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- if (info->ScopeRange.second < info->ScopeRange.first)
- return std::nullopt;
+ if (info.ScopeRange.second < info.ScopeRange.first)
+ return llvm::createStringError("Info do not have basename range.");
- return demangled_name.slice(info->ScopeRange.first, info->ScopeRange.second);
+ return demangled_name.slice(info.ScopeRange.first, info.ScopeRange.second);
}
/// Handles anything printed after the FunctionEncoding ItaniumDemangle
/// node. Most notably the DotSUffix node.
-static std::optional<llvm::StringRef>
+static llvm::Expected<llvm::StringRef>
GetDemangledFunctionSuffix(const SymbolContext &sc) {
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return std::nullopt;
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err)
+ return info_or_err.takeError();
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return std::nullopt;
+ auto [demangled_name, info] = *info_or_err;
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return std::nullopt;
-
- // Function without a basename is nonsense.
- if (!info->hasBasename())
- return std::nullopt;
-
- return demangled_name.slice(info->SuffixRange.first,
- info->SuffixRange.second);
+ return demangled_name.slice(info.SuffixRange.first, info.SuffixRange.second);
}
static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) {
assert(sc.symbol);
- Mangled mangled = sc.GetPossiblyInlinedFunctionName();
- if (!mangled)
- return false;
-
- auto demangled_name = mangled.GetDemangledName().GetStringRef();
- if (demangled_name.empty())
- return false;
-
- const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo();
- if (!info)
- return false;
-
- // Function without a basename is nonsense.
- if (!info->hasBasename())
+ auto info_or_err = GetAndValidateInfo(sc);
+ if (!info_or_err) {
+ info_or_err.takeError();
return false;
+ }
+ auto [demangled_name, info] = *info_or_err;
- if (info->ArgumentsRange.second < info->ArgumentsRange.first)
+ if (info.ArgumentsRange.second < info.ArgumentsRange.first)
return false;
- s << demangled_name.slice(info->ArgumentsRange.first,
- info->ArgumentsRange.second);
+ s << demangled_name.slice(info.ArgumentsRange.first,
+ info.ArgumentsRange.second);
return true;
}
@@ -1954,32 +1895,37 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
FormatEntity::Entry::Type type, Stream &s) {
switch (type) {
case FormatEntity::Entry::Type::FunctionScope: {
- std::optional<llvm::StringRef> scope = GetDemangledScope(sc);
- if (!scope)
+ auto scope_or_err = GetDemangledScope(sc);
+ if (!scope_or_err) {
+ llvm::consumeError(scope_or_err.takeError());
return false;
+ }
- s << *scope;
+ s << *scope_or_err;
return true;
}
case FormatEntity::Entry::Type::FunctionBasename: {
- std::optional<llvm::StringRef> name = GetDemangledBasename(sc);
- if (!name)
+ auto name_or_err = GetDemangledBasename(sc);
+ if (!name_or_err) {
+ llvm::consumeError(name_or_err.takeError());
return false;
+ }
- s << *name;
+ s << *name_or_err;
return true;
}
case FormatEntity::Entry::Type::FunctionTemplateArguments: {
- std::optional<llvm::StringRef> template_args =
- GetDemangledTemplateArguments(sc);
- if (!template_args)
+ auto template_args_or_err = GetDemangledTemplateArguments(sc);
+ if (!template_args_or_err) {
+ llvm::consumeError(template_args_or_err.takeError());
return false;
+ }
- s << *template_args;
+ s << *template_args_or_err;
return true;
}
@@ -2008,38 +1954,46 @@ bool CPlusPlusLanguage::HandleFrameFormatVariable(
return true;
}
case FormatEntity::Entry::Type::FunctionReturnRight: {
- std::optional<llvm::StringRef> return_rhs = GetDemangledReturnTypeRHS(sc);
- if (!return_rhs)
+ auto return_rhs_or_err = GetDemangledReturnTypeRHS(sc);
+ if (!return_rhs_or_err) {
+ llvm::consumeError(return_rhs_or_err.takeError());
return false;
+ }
- s << *return_rhs;
+ s << *return_rhs_or_err;
return true;
}
case FormatEntity::Entry::Type::FunctionReturnLeft: {
- std::optional<llvm::StringRef> return_lhs = GetDemangledReturnTypeLHS(sc);
- if (!return_lhs)
+ auto return_lhs_or_err = GetDemangledReturnTypeLHS(sc);
+ if (!return_lhs_or_err) {
+ llvm::consumeError(return_lhs_or_err.takeError());
return false;
+ }
- s << *return_lhs;
+ s << *return_lhs_or_err;
return true;
}
case FormatEntity::Entry::Type::FunctionQualifiers: {
- std::optional<llvm::StringRef> quals = GetDemangledFunctionQualifiers(sc);
- if (!quals)
+ auto quals_or_err = GetDemangledFunctionQualifiers(sc);
+ if (!quals_or_err) {
+ llvm::consumeError(quals_or_err.takeError());
return false;
+ }
- s << *quals;
+ s << *quals_or_err;
return true;
}
case FormatEntity::Entry::Type::FunctionSuffix: {
- std::optional<llvm::StringRef> suffix = GetDemangledFunctionSuffix(sc);
- if (!suffix)
+ auto suffix_or_err = GetDemangledFunctionSuffix(sc);
+ if (!suffix_or_err) {
+ llvm::consumeError(suffix_or_err.takeError());
return false;
+ }
- s << *suffix;
+ s << *suffix_or_err;
return true;
}
|
if (!info->hasBasename()) | ||
auto info_or_err = GetAndValidateInfo(sc); | ||
if (!info_or_err) { | ||
llvm::consumeError(info_or_err.takeError()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we log here instead of consuming the error?
Something like:
llvm::consumeError(info_or_err.takeError()); | |
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), info_or_err.takeError(), "Failed to retrieve demangled info: {0}" |
Might need to downgrade it to LLDB_LOG_ERRORV
if we find that this is a common/unimportant error path
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same applies to all the other places where we consumeError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks! LLDB_LOG_ERROR
seems to be reasonable for now, I did not encounter an error message while testing on a small C++ program.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems reasonable, modulo the logging comment
|
||
auto demangled_name = mangled.GetDemangledName().GetStringRef(); | ||
if (demangled_name.empty()) | ||
return std::nullopt; | ||
return llvm::createStringError("Function does not have a demangled name."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return llvm::createStringError("Function does not have a demangled name."); | |
return llvm::createStringError("Function '%s' does not have a demangled name.", mangled.GetMangledName().AsCString("")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added better messages for all the createStringError
.
|
||
const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo(); | ||
if (!info) | ||
return std::nullopt; | ||
return llvm::createStringError("Function does not have demangled info."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return llvm::createStringError("Function does not have demangled info."); | |
return llvm::createStringError("Function '%s' does not have demangled info.", demangled_name.data()); |
|
||
// Function without a basename is nonsense. | ||
if (!info->hasBasename()) | ||
return std::nullopt; | ||
return llvm::createStringError("Info do not have basename range."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return llvm::createStringError("Info do not have basename range."); | |
return llvm::createStringError("DemangledInfo for '%s' do not have basename range.", demangled_name.data()); |
if (!info) | ||
return std::nullopt; | ||
if (info.ScopeRange.first >= demangled_name.size()) | ||
return llvm::createStringError("Scope range is invalid."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return llvm::createStringError("Scope range is invalid."); | |
return llvm::createStringError("Scope range for '%s' is invalid.", demangled_name.data()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you similarly include the demangled_name in the error message elsewhere too?
auto name_or_err = GetDemangledBasename(sc); | ||
if (!name_or_err) { | ||
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), name_or_err.takeError(), | ||
"Failed to retrieve basename: {0}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Failed to retrieve basename: {0}"); | |
"Failed to handle ${function.basename} frame-format variable: {0}"); |
And do this for the other messages in this switch too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added better messages for all the LLDB_LOG_ERROR
.
I think we should wait before the following PR gets merged before merging, in order to use the new |
1af4c9e
to
6d8e8c0
Compare
Done ✅ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just left a couple follow-up comments. Otherwise LGTM!
if (!quals_or_err) { | ||
LLDB_LOG_ERROR( | ||
GetLog(LLDBLog::Language), quals_or_err.takeError(), | ||
"Failed to handle ${function.qualifiers} frame-format variable: {0}"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies, I think you need to escape the {
here. Since that's the syntax for specifying the format index. I think {{
is the way to do that
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem, thanks!
✅ With the latest revision this PR passed the C/C++ code formatter. |
if (!info->hasBasename()) | ||
return std::nullopt; | ||
if (!info.hasQualifiers()) | ||
return llvm::createStringError("Qualifiers range for '%s' is invalid.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, not having qualifiers on a demangled name doesn't necessarily mean this is fatal. This explains the latest linux failures. Some methods just don't have qualifiers. I guess we need to return ""
in that case. Should hasQualifiers
also accept cases where QualifiersRange.start == QualifiersRange.end
? Which just means they don't exist, but didn't fail to parse. I'm also happy to just remove this check. I think that's why i used to only check for hasBasename
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could add another method called areQualifiersEmpty
(or better name) which checks if QualifiersRange.start == QualifiersRange.end
. The check would look like
if(!hasQualifiers() && !areQualifiersEmpty())
return createStringError(...);
return demangled_name.slice(...);
This way we keep the semantic meaning of hasQualifiers
and we can still surface parsing errors, which we would not be able to do if we remove the check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ended up adding a isXEmpty()
method for each range which checks if xRange.first == xRange.second
. This fixes the test.
@@ -74,24 +74,48 @@ struct DemangledNameInfo { | |||
return BasenameRange.second > BasenameRange.first; | |||
} | |||
|
|||
/// Returns \c true if `BasenameRange` is empty. | |||
bool isBasenameEmpty() const { | |||
return BasenameRange.first == BasenameRange.second; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm I think I'd prefer having only a single API that tells us "do we have a valid entity or not". E.g., hasValidQualifiers
/hasValidBasename
/etc. Is there a reason we can't just modify the existing hasXXX
helpers to be Range.second >= Range.first
? If we are going to call !hasXXX && !isEmpty
then might as well fold them into a single API.
But if there's reasons we can't do that, this seems fine too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that since we are calling !hasX && !isEmpty
everywhere, me might just as well squash them. When doing so, the only problem I have noticed is that some tests break, such as when demangling Zinvalid
which technically has no basename, but with this new implementation hasBasename
returns true.
Overall, I still think it's best to collapse everything into one method like you suggested 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for adjusting. Mostly looks good now
Though I don't think hasBasename
should allow for start == end
. That's the one range that all DemangledInfo
objects need to have to be considered valid. Could we keep that as end > start
? I don't think that should break anything. Probably worth adding a comment there why it's not >=
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Switched to >
instead of >=
for hasBasename
. This "fixed" the tests that I originally broke 👍
…lvm#144731) Upgrade the callees of `HandleFrameFormatVariable` (`GetDemangledTemplateArguments`, etc), to return a `llvm::Expected` instead of an `std::optional`. This patch also bundles the logic of validating the demangled name and information into a single reusable function to reduce code duplication.
…lvm#144731) Upgrade the callees of `HandleFrameFormatVariable` (`GetDemangledTemplateArguments`, etc), to return a `llvm::Expected` instead of an `std::optional`. This patch also bundles the logic of validating the demangled name and information into a single reusable function to reduce code duplication.
…lvm#144731) Upgrade the callees of `HandleFrameFormatVariable` (`GetDemangledTemplateArguments`, etc), to return a `llvm::Expected` instead of an `std::optional`. This patch also bundles the logic of validating the demangled name and information into a single reusable function to reduce code duplication.
Upgrade the callees of
HandleFrameFormatVariable
(GetDemangledTemplateArguments
, etc), to return allvm::Expected
instead of anstd::optional
.This patch also bundles the logic of validating the demangled name and information into a single reusable function to reduce code duplication.