-
Notifications
You must be signed in to change notification settings - Fork 344
[Syntax Highlighting] Add name and parameters syntax highlighting in Swift backtraces #10710
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
base: swift/release/6.2
Are you sure you want to change the base?
Changes from all commits
1fa228c
c9147b4
d8c0194
b10654b
315d438
d97d61a
715cfde
4b848aa
bf50a87
3a7d06c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1906,23 +1906,95 @@ SwiftLanguage::GetDemangledFunctionNameWithoutArguments(Mangled mangled) const { | |
return mangled_name; | ||
} | ||
|
||
static std::optional<llvm::StringRef> | ||
static llvm::Expected<std::pair<llvm::StringRef, DemangledNameInfo>> | ||
GetAndValidateInfo(const SymbolContext &sc) { | ||
Mangled mangled = sc.GetPossiblyInlinedFunctionName(); | ||
if (!mangled) | ||
return llvm::createStringError("Function does not have a mangled name."); | ||
|
||
auto demangled_name = mangled.GetDemangledName().GetStringRef(); | ||
if (demangled_name.empty()) | ||
return llvm::createStringError( | ||
"Function '%s' does not have a demangled name.", | ||
mangled.GetMangledName().AsCString("")); | ||
|
||
const std::optional<DemangledNameInfo> &info = mangled.GetDemangledInfo(); | ||
if (!info) | ||
return llvm::createStringError( | ||
"Function '%s' does not have demangled info.", demangled_name.data()); | ||
|
||
// Function without a basename is nonsense. | ||
if (!info->hasBasename()) | ||
return llvm::createStringError( | ||
"DemangledInfo for '%s does not have basename range.", | ||
demangled_name.data()); | ||
|
||
return std::make_pair(demangled_name, *info); | ||
} | ||
|
||
static llvm::Expected<llvm::StringRef> | ||
GetDemangledBasename(const SymbolContext &sc) { | ||
return std::nullopt; | ||
auto info_or_err = GetAndValidateInfo(sc); | ||
if (!info_or_err) | ||
return info_or_err.takeError(); | ||
|
||
auto [demangled_name, info] = *info_or_err; | ||
|
||
return demangled_name.slice(info.BasenameRange.first, | ||
info.BasenameRange.second); | ||
} | ||
|
||
static std::optional<llvm::StringRef> | ||
static llvm::Expected<llvm::StringRef> | ||
GetDemangledFunctionPrefix(const SymbolContext &sc) { | ||
return std::nullopt; | ||
auto info_or_err = GetAndValidateInfo(sc); | ||
if (!info_or_err) | ||
return info_or_err.takeError(); | ||
|
||
auto [demangled_name, info] = *info_or_err; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this missing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks! |
||
if (!info.hasPrefix()) | ||
return llvm::createStringError( | ||
"DemangledInfo for '%s does not have suffix range.", | ||
demangled_name.data()); | ||
|
||
return demangled_name.slice(info.PrefixRange.first, info.PrefixRange.second); | ||
} | ||
|
||
static std::optional<llvm::StringRef> | ||
static llvm::Expected<llvm::StringRef> | ||
GetDemangledFunctionSuffix(const SymbolContext &sc) { | ||
return std::nullopt; | ||
auto info_or_err = GetAndValidateInfo(sc); | ||
if (!info_or_err) | ||
return info_or_err.takeError(); | ||
|
||
auto [demangled_name, info] = *info_or_err; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this missing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, thanks! |
||
|
||
if (!info.hasSuffix()) | ||
return llvm::createStringError( | ||
"DemangledInfo for '%s does not have suffix range.", | ||
demangled_name.data()); | ||
|
||
return demangled_name.slice(info.SuffixRange.first, info.SuffixRange.second); | ||
} | ||
|
||
static bool PrintDemangledArgumentList(Stream &s, const SymbolContext &sc) { | ||
return false; | ||
assert(sc.symbol); | ||
|
||
auto info_or_err = GetAndValidateInfo(sc); | ||
if (!info_or_err) { | ||
LLDB_LOG_ERROR(GetLog(LLDBLog::Language), info_or_err.takeError(), | ||
"Failed to handle ${{function.formatted-arguments}} " | ||
"frame-format variable: {0}"); | ||
return false; | ||
} | ||
auto [demangled_name, info] = *info_or_err; | ||
|
||
if (!info.hasArguments()) | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we also returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We are also returning bool hasArguments() const {
return ArgumentsRange.second >= ArgumentsRange.first;
} Empty arguments do not cause a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aah forgot that we did that. Wish we renamed these APIs. But no need to do that as part of this PR :) |
||
|
||
s << demangled_name.slice(info.ArgumentsRange.first, | ||
info.ArgumentsRange.second); | ||
|
||
return true; | ||
} | ||
|
||
static VariableListSP GetFunctionVariableList(const SymbolContext &sc) { | ||
|
@@ -1941,11 +2013,15 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc, | |
Stream &s) { | ||
switch (type) { | ||
case FormatEntity::Entry::Type::FunctionBasename: { | ||
std::optional<llvm::StringRef> name = GetDemangledBasename(sc); | ||
if (!name) | ||
auto name_or_err = GetDemangledBasename(sc); | ||
if (!name_or_err) { | ||
LLDB_LOG_ERROR( | ||
GetLog(LLDBLog::Language), name_or_err.takeError(), | ||
"Failed to handle ${{function.basename}} frame-format variable: {0}"); | ||
return false; | ||
} | ||
|
||
s << *name; | ||
s << *name_or_err; | ||
|
||
return true; | ||
} | ||
|
@@ -1972,20 +2048,28 @@ bool SwiftLanguage::HandleFrameFormatVariable(const SymbolContext &sc, | |
return true; | ||
} | ||
case FormatEntity::Entry::Type::FunctionPrefix: { | ||
std::optional<llvm::StringRef> prefix = GetDemangledFunctionPrefix(sc); | ||
if (!prefix) | ||
auto prefix_or_err = GetDemangledFunctionPrefix(sc); | ||
if (!prefix_or_err) { | ||
LLDB_LOG_ERROR( | ||
GetLog(LLDBLog::Language), prefix_or_err.takeError(), | ||
"Failed to handle ${{function.prefix}} frame-format variable: {0}"); | ||
return false; | ||
} | ||
|
||
s << *prefix; | ||
s << *prefix_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) { | ||
LLDB_LOG_ERROR( | ||
GetLog(LLDBLog::Language), suffix_or_err.takeError(), | ||
"Failed to handle ${{function.suffix}} frame-format variable: {0}"); | ||
return false; | ||
} | ||
|
||
s << *suffix; | ||
s << *suffix_or_err; | ||
|
||
return true; | ||
} | ||
|
@@ -2019,7 +2103,7 @@ class PluginProperties : public Properties { | |
} | ||
|
||
FormatEntity::Entry GetFunctionNameFormat() const { | ||
return GetPropertyAtIndexAs<const FormatEntity::Entry>( | ||
return GetPropertyAtIndexAs<FormatEntity::Entry>( | ||
ePropertyFunctionNameFormat, {}); | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
//===-- SwiftMangled.h ------------------------------------------*- C++ -*-===// | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors | ||
// Licensed under Apache License v2.0 with Runtime Library Exception | ||
// | ||
// See https://swift.org/LICENSE.txt for license information | ||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#ifndef liblldb_SwiftMangled_h_ | ||
#define liblldb_SwiftMangled_h_ | ||
|
||
#include "lldb/Core/DemangledNameInfo.h" | ||
#include "swift/Demangling/Demangle.h" | ||
|
||
using namespace swift::Demangle; | ||
|
||
/// A NodePrinter class with range tracking capabilities. | ||
/// | ||
/// When used instead of a regular NodePrinter, this class will store additional | ||
/// range information of the demangled name in the `info` attribute, such as the | ||
/// range of the name of a method. | ||
class TrackingNodePrinter : public NodePrinter { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a oxygen comment here explaining very briefly what this class is used for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed 👍 |
||
public: | ||
TrackingNodePrinter(DemangleOptions options) : NodePrinter(options) {} | ||
|
||
lldb_private::DemangledNameInfo getInfo() { return info; } | ||
|
||
private: | ||
lldb_private::DemangledNameInfo info; | ||
std::optional<unsigned> parametersDepth; | ||
|
||
void startName() { | ||
if (!info.hasBasename()) | ||
info.BasenameRange.first = getStreamLength(); | ||
} | ||
|
||
void endName() { | ||
if (!info.hasBasename()) | ||
info.BasenameRange.second = getStreamLength(); | ||
} | ||
|
||
void startParameters(unsigned depth) { | ||
if (parametersDepth || !info.hasBasename() || info.hasArguments()) { | ||
return; | ||
} | ||
info.ArgumentsRange.first = getStreamLength(); | ||
parametersDepth = depth; | ||
} | ||
|
||
void endParameters(unsigned depth) { | ||
if (!parametersDepth || *parametersDepth != depth || info.hasArguments()) { | ||
return; | ||
} | ||
info.ArgumentsRange.second = getStreamLength(); | ||
} | ||
|
||
bool shouldTrackNameRange(NodePointer Node) const { | ||
Michael137 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
assert(Node); | ||
switch (Node->getKind()) { | ||
case Node::Kind::Function: | ||
case Node::Kind::Constructor: | ||
case Node::Kind::Allocator: | ||
case Node::Kind::ExplicitClosure: | ||
return true; | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
void printFunctionName(bool hasName, llvm::StringRef &OverwriteName, | ||
llvm::StringRef &ExtraName, bool MultiWordName, | ||
int &ExtraIndex, NodePointer Entity, | ||
unsigned int depth) override { | ||
if (shouldTrackNameRange(Entity)) | ||
startName(); | ||
NodePrinter::printFunctionName(hasName, OverwriteName, ExtraName, | ||
MultiWordName, ExtraIndex, Entity, depth); | ||
if (shouldTrackNameRange(Entity)) | ||
endName(); | ||
} | ||
|
||
void printFunctionParameters(NodePointer LabelList, NodePointer ParameterType, | ||
unsigned depth, bool showTypes) override { | ||
startParameters(depth); | ||
NodePrinter::printFunctionParameters(LabelList, ParameterType, depth, | ||
showTypes); | ||
endParameters(depth); | ||
} | ||
}; | ||
|
||
#endif // liblldb_SwiftMangled_h_ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -200,13 +200,35 @@ class SwiftLanguageRuntime : public LanguageRuntime { | |
IsSwiftAsyncAwaitResumePartialFunctionSymbol(llvm::StringRef name); | ||
|
||
enum DemangleMode { eSimplified, eTypeName, eDisplayTypeName }; | ||
|
||
/// Demangle a symbol to a string. | ||
/// | ||
/// \param symbol The mangled symbol to demangle. | ||
/// \param mode The `DemangleMode` to use when demangling. | ||
/// \param sc The associated `SymbolContext`. | ||
/// \param exe_ctx The associated `ExecutionContext`. | ||
/// | ||
/// \return The demangled symbol. | ||
static std::string | ||
DemangleSymbolAsString(llvm::StringRef symbol, DemangleMode mode, | ||
const SymbolContext *sc = nullptr, | ||
const ExecutionContext *exe_ctx = nullptr); | ||
|
||
static std::string GetParentNameIfClosure(Function &func); | ||
|
||
/// Demangle a symbol to a string with additional range information. | ||
/// | ||
/// \param symbol The mangled symbol to demangle. | ||
/// \param mode The `DemangleMode` to use when demangling. | ||
/// \param sc The associated `SymbolContext`. | ||
/// \param exe_ctx The associated `ExecutionContext`. | ||
/// | ||
/// \return The demangled symbol as well as range tracking information. | ||
static std::pair<std::string, DemangledNameInfo> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doxygen comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed 👍 |
||
TrackedDemangleSymbolAsString(llvm::StringRef symbol, DemangleMode mode, | ||
const SymbolContext *sc = nullptr, | ||
const ExecutionContext *exe_ctx = nullptr); | ||
|
||
/// Demangle a symbol to a swift::Demangle node tree. | ||
/// | ||
/// This is a central point of access, for purposes such as logging. | ||
|
@@ -885,6 +907,11 @@ class SwiftLanguageRuntime : public LanguageRuntime { | |
|
||
/// Swift native NSError isa. | ||
std::optional<lldb::addr_t> m_SwiftNativeNSErrorISA; | ||
|
||
static std::pair<std::string, std::optional<DemangledNameInfo>> | ||
DemangleSymbolAsString(llvm::StringRef symbol, DemangleMode mode, | ||
bool tracking, const SymbolContext *sc, | ||
const ExecutionContext *exe_ctx); | ||
}; | ||
|
||
/// The target specific register numbers used for async unwinding. | ||
|
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.
because otherwise this will crash on a nullptr
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 👍