diff options
Diffstat (limited to 'lldb')
99 files changed, 1905 insertions, 367 deletions
diff --git a/lldb/bindings/interface/SBThreadExtensions.i b/lldb/bindings/interface/SBThreadExtensions.i index 267faad..4ec9f10 100644 --- a/lldb/bindings/interface/SBThreadExtensions.i +++ b/lldb/bindings/interface/SBThreadExtensions.i @@ -45,6 +45,9 @@ STRING_EXTENSION_OUTSIDE(SBThread) frames.append(frame) return frames + def get_stop_description(self): + return self.GetStopDescription(1024) + def get_stop_reason_data(self): return [ self.GetStopReasonDataAtIndex(idx) @@ -69,6 +72,7 @@ STRING_EXTENSION_OUTSIDE(SBThread) name = property(GetName, None, doc='''A read only property that returns the name of this thread as a string.''') queue = property(GetQueueName, None, doc='''A read only property that returns the dispatch queue name of this thread as a string.''') queue_id = property(GetQueueID, None, doc='''A read only property that returns the dispatch queue id of this thread as an integer.''') + stop_description = property(get_stop_description, None, doc='''A read only property that returns a string describing the reason this thread stopped.''') stop_reason = property(GetStopReason, None, doc='''A read only property that returns an lldb enumeration value (see enumerations that start with "lldb.eStopReason") that represents the reason this thread stopped.''') stop_reason_data = property(get_stop_reason_data, None, doc='''A read only property that returns the stop reason data as a list.''') is_suspended = property(IsSuspended, None, doc='''A read only property that returns a boolean value that indicates if this thread is suspended.''') diff --git a/lldb/bindings/python/python-extensions.swig b/lldb/bindings/python/python-extensions.swig index 4ba1607..40fa768 100644 --- a/lldb/bindings/python/python-extensions.swig +++ b/lldb/bindings/python/python-extensions.swig @@ -594,6 +594,7 @@ def is_numeric_type(basic_type): if basic_type == eBasicTypeFloat: return (True,True) if basic_type == eBasicTypeDouble: return (True,True) if basic_type == eBasicTypeLongDouble: return (True,True) + if basic_type == eBasicTypeFloat128: return (True,True) if basic_type == eBasicTypeFloatComplex: return (True,True) if basic_type == eBasicTypeDoubleComplex: return (True,True) if basic_type == eBasicTypeLongDoubleComplex: return (True,True) diff --git a/lldb/docs/python_api_enums.rst b/lldb/docs/python_api_enums.rst index b6a2497..a43a47b 100644 --- a/lldb/docs/python_api_enums.rst +++ b/lldb/docs/python_api_enums.rst @@ -321,6 +321,7 @@ Format .. py:data:: eFormatInstruction .. py:data:: eFormatVoid .. py:data:: eFormatUnicode8 +.. py:data:: eFormatFloat128 .. _DescriptionLevel: @@ -1045,6 +1046,7 @@ BasicType .. py:data:: eBasicTypeObjCSel .. py:data:: eBasicTypeNullPtr .. py:data:: eBasicTypeOther +.. py:data:: eBasicTypeFloat128 .. _TraceType: diff --git a/lldb/docs/resources/lldbgdbremote.md b/lldb/docs/resources/lldbgdbremote.md index 41628cf..36b95f1 100644 --- a/lldb/docs/resources/lldbgdbremote.md +++ b/lldb/docs/resources/lldbgdbremote.md @@ -1998,22 +1998,6 @@ threads (live system debug) / cores (JTAG) in your program have stopped and allows LLDB to display and control your program correctly. -## qWasmCallStack - -Get the Wasm call stack for the given thread id. This returns a hex-encoded -list of PC values, one for each frame of the call stack. To match the Wasm -specification, the addresses are encoded in little endian byte order, even if -the endian of the Wasm runtime's host is not little endian. - -``` -send packet: $qWasmCallStack:202dbe040#08 -read packet: $9c01000000000040e501000000000040fe01000000000040# -``` - -**Priority to Implement:** Only required for Wasm support. This packed is -supported by the [WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) -and [V8](https://v8.dev) Wasm runtimes. - ## qWatchpointSupportInfo Get the number of hardware watchpoints available on the remote target. @@ -2479,3 +2463,70 @@ omitting them will work fine; these numbers are always base 16. The length of the payload is not provided. A reliable, 8-bit clean, transport layer is assumed. + +## Wasm Packets + +The packet below are supported by the +[WAMR](https://github.com/bytecodealliance/wasm-micro-runtime) and +[V8](https://v8.dev) Wasm runtimes. + + +### qWasmCallStack + +Get the Wasm call stack for the given thread id. This returns a hex-encoded +list of PC values, one for each frame of the call stack. To match the Wasm +specification, the addresses are encoded in little endian byte order, even if +the endian of the Wasm runtime's host is not little endian. + +``` +send packet: $qWasmCallStack:202dbe040#08 +read packet: $9c01000000000040e501000000000040fe01000000000040# +``` + +**Priority to Implement:** Only required for Wasm support. Necessary to show +stack traces. + +### qWasmGlobal + +Get the value of a Wasm global variable for the given frame index at the given +variable index. The indexes are encoded as base 10. The result is a hex-encoded +address from where to read the value. + +``` +send packet: $qWasmGlobal:0;2#cb +read packet: $e0030100#b9 +``` + +**Priority to Implement:** Only required for Wasm support. Necessary to show +variables. + + +### qWasmLocal + +Get the value of a Wasm function argument or local variable for the given frame +index at the given variable index. The indexes are encoded as base 10. The +result is a hex-encoded address from where to read the value. + + +``` +send packet: $qWasmLocal:0;2#cb +read packet: $e0030100#b9 +``` + +**Priority to Implement:** Only required for Wasm support. Necessary to show +variables. + + +### qWasmStackValue + +Get the value of a Wasm local variable from the Wasm operand stack, for the +given frame index at the given variable index. The indexes are encoded as base +10. The result is a hex-encoded address from where to read value. + +``` +send packet: $qWasmStackValue:0;2#cb +read packet: $e0030100#b9 +``` + +**Priority to Implement:** Only required for Wasm support. Necessary to show +variables. diff --git a/lldb/docs/use/formatting.rst b/lldb/docs/use/formatting.rst index 39ccfed..c5a880c 100644 --- a/lldb/docs/use/formatting.rst +++ b/lldb/docs/use/formatting.rst @@ -344,19 +344,19 @@ E.g., the following setting would reconstruct the entire function name (and is L :: - (lldb) settings set plugin.cplusplus.dislpay.function-name-format "${function.return-left}${function.scope}${function.basename}${function.template-arguments}${function.formatted-arguments}${function.qualifiers}${function.return-right}${function.suffix}" + (lldb) settings set plugin.cplusplus.display.function-name-format "${function.return-left}${function.scope}${function.basename}${function.template-arguments}${function.formatted-arguments}${function.qualifiers}${function.return-right}${function.suffix}" If a user wanted to only print the name and arguments of a C++ function one could do: :: - (lldb) settings set plugin.cplusplus.dislpay.function-name-format "${function.scope}${function.basename}${function.formatted-arguments}" + (lldb) settings set plugin.cplusplus.display.function-name-format "${function.scope}${function.basename}${function.formatted-arguments}" Then the following would highlight just the basename in green: :: - (lldb) settings set plugin.cplusplus.dislpay.function-name-format "${function.scope}${ansi.fg.yellow}${function.basename}${ansi.normal}${function.formatted-arguments}" + (lldb) settings set plugin.cplusplus.display.function-name-format "${function.scope}${ansi.fg.yellow}${function.basename}${ansi.normal}${function.formatted-arguments}" The ``${function.name-with-args}`` by default asks the language plugin whether it supports a language-specific ``function-name-format`` (e.g., the ``plugin.cplusplus.display.function-name-format`` for C++), and if it does, uses it. Otherwise it will display the demangled function name. diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 8bb55c9..8513e14 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -86,7 +86,8 @@ struct ModuleFunctionSearchOptions { /// /// The module will parse more detailed information as more queries are made. class Module : public std::enable_shared_from_this<Module>, - public SymbolContextScope { + public SymbolContextScope, + public UserID { public: class LookupInfo; // Static functions that can track the lifetime of module objects. This is diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h index d5e291f3..6ecdcf1 100644 --- a/lldb/include/lldb/Core/ModuleList.h +++ b/lldb/include/lldb/Core/ModuleList.h @@ -352,6 +352,14 @@ public: // UUID values is very efficient and accurate. lldb::ModuleSP FindModule(const UUID &uuid) const; + /// Find a module by LLDB-specific unique identifier. + /// + /// \param[in] uid The UID of the module assigned to it on construction. + /// + /// \returns ModuleSP of module with \c uid. Returns nullptr if no such + /// module could be found. + lldb::ModuleSP FindModule(lldb::user_id_t uid) const; + /// Finds the first module whose file specification matches \a module_spec. lldb::ModuleSP FindFirstModule(const ModuleSpec &module_spec) const; diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h index 8de9364..20067f4 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -13,6 +13,7 @@ #include <string> #include <vector> +#include "llvm/Support/FormatProviders.h" #include "lldb/Expression/ExpressionTypeSystemHelper.h" #include "lldb/lldb-forward.h" @@ -96,6 +97,62 @@ protected: ///invalid. }; +/// Holds parsed information about a function call label that +/// LLDB attaches as an AsmLabel to function AST nodes it parses +/// from debug-info. +/// +/// The format being: +/// +/// <prefix>:<module uid>:<symbol uid>:<name> +/// +/// The label string needs to stay valid for the entire lifetime +/// of this object. +struct FunctionCallLabel { + /// Unique identifier of the lldb_private::Module + /// which contains the symbol identified by \c symbol_id. + lldb::user_id_t module_id; + + /// Unique identifier of the function symbol on which to + /// perform the function call. For example, for DWARF this would + /// be the DIE UID. + lldb::user_id_t symbol_id; + + /// Name to use when searching for the function symbol in + /// \c module_id. For most function calls this will be a + /// mangled name. In cases where a mangled name can't be used, + /// this will be the function name. + /// + /// NOTE: kept as last element so we don't have to worry about + /// ':' in the mangled name when parsing the label. + llvm::StringRef lookup_name; + + /// Decodes the specified function \c label into a \c FunctionCallLabel. + static llvm::Expected<FunctionCallLabel> fromString(llvm::StringRef label); + + /// Encode this FunctionCallLabel into its string representation. + /// + /// The representation roundtrips through \c fromString: + /// \code{.cpp} + /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov"; + /// FunctionCallLabel label = *fromString(label); + /// + /// assert (label.toString() == encoded); + /// assert (*fromString(label.toString()) == label); + /// \endcode + std::string toString() const; +}; + +/// LLDB attaches this prefix to mangled names of functions that get called +/// from JITted expressions. +inline constexpr llvm::StringRef FunctionCallLabelPrefix = "$__lldb_func"; + } // namespace lldb_private +namespace llvm { +template <> struct format_provider<lldb_private::FunctionCallLabel> { + static void format(const lldb_private::FunctionCallLabel &label, + raw_ostream &OS, StringRef Style); +}; +} // namespace llvm + #endif // LLDB_EXPRESSION_EXPRESSION_H diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index e95f955..bbc615d 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -18,6 +18,7 @@ #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SourceModule.h" +#include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" @@ -328,6 +329,18 @@ public: GetMangledNamesForFunction(const std::string &scope_qualified_name, std::vector<ConstString> &mangled_names); + /// Resolves the function corresponding to the specified LLDB function + /// call \c label. + /// + /// \param[in] label The FunctionCallLabel to be resolved. + /// + /// \returns An llvm::Error if the specified \c label couldn't be resolved. + /// Returns the resolved function (as a SymbolContext) otherwise. + virtual llvm::Expected<SymbolContext> + ResolveFunctionCallLabel(const FunctionCallLabel &label) { + return llvm::createStringError("Not implemented"); + } + virtual void GetTypes(lldb_private::SymbolContextScope *sc_scope, lldb::TypeClass type_mask, lldb_private::TypeList &type_list) = 0; diff --git a/lldb/include/lldb/Symbol/TypeSystem.h b/lldb/include/lldb/Symbol/TypeSystem.h index cb1f013..16a2e0b 100644 --- a/lldb/include/lldb/Symbol/TypeSystem.h +++ b/lldb/include/lldb/Symbol/TypeSystem.h @@ -310,7 +310,8 @@ public: // Exploring the type - virtual const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) = 0; + virtual const llvm::fltSemantics & + GetFloatTypeSemantics(size_t byte_size, lldb::Format format) = 0; virtual llvm::Expected<uint64_t> GetBitSize(lldb::opaque_compiler_type_t type, diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 7e66e31..dc75d98 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -100,6 +100,7 @@ public: void SetStopOnSharedLibraryEvents(bool stop); bool GetDisableLangRuntimeUnwindPlans() const; void SetDisableLangRuntimeUnwindPlans(bool disable); + void DisableLanguageRuntimeUnwindPlansCallback(); bool GetDetachKeepsStopped() const; void SetDetachKeepsStopped(bool keep_stopped); bool GetWarningsOptimization() const; diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 171a650..c63c1f0 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -198,11 +198,15 @@ enum Format { ///< character arrays that can contain non printable ///< characters eFormatAddressInfo, ///< Describe what an address points to (func + offset - ///< with file/line, symbol + offset, data, etc) - eFormatHexFloat, ///< ISO C99 hex float string - eFormatInstruction, ///< Disassemble an opcode - eFormatVoid, ///< Do not print this + ///< with file/line, symbol + offset, data, etc) + eFormatHexFloat, ///< ISO C99 hex float string + eFormatInstruction, ///< Disassemble an opcode + eFormatVoid, ///< Do not print this eFormatUnicode8, + eFormatFloat128, ///< Disambiguate between 128-bit `long double` (which uses + ///< `eFormatFloat`) and `__float128` (which uses + ///< `eFormatFloat128`). If the value being formatted is not + ///< 128 bits, then this is identical to `eFormatFloat`. kNumFormats }; @@ -838,7 +842,8 @@ enum BasicType { eBasicTypeObjCClass, eBasicTypeObjCSel, eBasicTypeNullPtr, - eBasicTypeOther + eBasicTypeOther, + eBasicTypeFloat128 }; /// Deprecated diff --git a/lldb/packages/Python/lldbsuite/support/temp_file.py b/lldb/packages/Python/lldbsuite/support/temp_file.py new file mode 100644 index 0000000..a21e212 --- /dev/null +++ b/lldb/packages/Python/lldbsuite/support/temp_file.py @@ -0,0 +1,23 @@ +""" +Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +See https://llvm.org/LICENSE.txt for license information. +SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +""" + +import os +import tempfile + + +class OnDiskTempFile: + def __init__(self, delete=True): + self.path = None + + def __enter__(self): + fd, path = tempfile.mkstemp() + os.close(fd) + self.path = path + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + if os.path.exists(self.path): + os.remove(self.path) diff --git a/lldb/packages/Python/lldbsuite/test/builders/builder.py b/lldb/packages/Python/lldbsuite/test/builders/builder.py index ada6f9f..96c7b39 100644 --- a/lldb/packages/Python/lldbsuite/test/builders/builder.py +++ b/lldb/packages/Python/lldbsuite/test/builders/builder.py @@ -26,7 +26,7 @@ class Builder: def getTriple(self, arch): """Returns the triple for the given architecture or None.""" - return None + return configuration.triple def getExtraMakeArgs(self): """ @@ -37,6 +37,9 @@ class Builder: def getArchCFlags(self, architecture): """Returns the ARCH_CFLAGS for the make system.""" + triple = self.getTriple(architecture) + if triple: + return ["ARCH_CFLAGS=-target {}".format(triple)] return [] def getMake(self, test_subdir, test_name): diff --git a/lldb/packages/Python/lldbsuite/test/configuration.py b/lldb/packages/Python/lldbsuite/test/configuration.py index b2d91fd2..5e38109 100644 --- a/lldb/packages/Python/lldbsuite/test/configuration.py +++ b/lldb/packages/Python/lldbsuite/test/configuration.py @@ -45,6 +45,9 @@ dsymutil = None sdkroot = None make_path = None +# Allow specifying a triple for cross compilation. +triple = None + # The overriden dwarf verison. # Don't use this to test the current compiler's # DWARF version, as this won't be set if the @@ -141,6 +144,7 @@ enabled_plugins = [] # Typical values include Debug, Release, RelWithDebInfo and MinSizeRel cmake_build_type = None + def shouldSkipBecauseOfCategories(test_categories): if use_categories: if ( diff --git a/lldb/packages/Python/lldbsuite/test/decorators.py b/lldb/packages/Python/lldbsuite/test/decorators.py index a5f5837..bd10bcc 100644 --- a/lldb/packages/Python/lldbsuite/test/decorators.py +++ b/lldb/packages/Python/lldbsuite/test/decorators.py @@ -20,6 +20,7 @@ from . import configuration from . import test_categories from . import lldbtest_config from lldbsuite.support import funcutils +from lldbsuite.support import temp_file from lldbsuite.test import lldbplatform from lldbsuite.test import lldbplatformutil @@ -94,22 +95,23 @@ def _match_decorator_property(expected, actual): def _compiler_supports( - compiler, flag, source="int main() {}", output_file=tempfile.NamedTemporaryFile() + compiler, flag, source="int main() {}", output_file=temp_file.OnDiskTempFile() ): """Test whether the compiler supports the given flag.""" - if platform.system() == "Darwin": - compiler = "xcrun " + compiler - try: - cmd = "echo '%s' | %s %s -x c -o %s -" % ( - source, - compiler, - flag, - output_file.name, - ) - subprocess.check_call(cmd, shell=True) - except subprocess.CalledProcessError: - return False - return True + with output_file: + if platform.system() == "Darwin": + compiler = "xcrun " + compiler + try: + cmd = "echo '%s' | %s %s -x c -o %s -" % ( + source, + compiler, + flag, + output_file.path, + ) + subprocess.check_call(cmd, shell=True) + except subprocess.CalledProcessError: + return False + return True def expectedFailureIf(condition, bugnumber=None): @@ -876,19 +878,19 @@ def skipUnlessSupportedTypeAttribute(attr): def compiler_doesnt_support_struct_attribute(): compiler_path = lldbplatformutil.getCompiler() - f = tempfile.NamedTemporaryFile() - cmd = [lldbplatformutil.getCompiler(), "-x", "c++", "-c", "-o", f.name, "-"] - p = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True, - ) - stdout, stderr = p.communicate("struct __attribute__((%s)) Test {};" % attr) - if attr in stderr: - return "Compiler does not support attribute %s" % (attr) - return None + with temp_file.OnDiskTempFile() as f: + cmd = [lldbplatformutil.getCompiler(), "-x", "c++", "-c", "-o", f.path, "-"] + p = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + stdout, stderr = p.communicate("struct __attribute__((%s)) Test {};" % attr) + if attr in stderr: + return "Compiler does not support attribute %s" % (attr) + return None return skipTestIfFn(compiler_doesnt_support_struct_attribute) @@ -902,21 +904,21 @@ def skipUnlessHasCallSiteInfo(func): if not compiler.startswith("clang"): return "Test requires clang as compiler" - f = tempfile.NamedTemporaryFile() - cmd = ( - "echo 'int main() {}' | " - "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.name) - ) - if os.popen(cmd).close() is not None: - return "Compiler can't compile with call site info enabled" + with temp_file.OnDiskTempFile() as f: + cmd = ( + "echo 'int main() {}' | " + "%s -g -glldb -O1 -S -emit-llvm -x c -o %s -" % (compiler_path, f.path) + ) + if os.popen(cmd).close() is not None: + return "Compiler can't compile with call site info enabled" - with open(f.name, "r") as ir_output_file: - buf = ir_output_file.read() + with open(f.path, "r") as ir_output_file: + buf = ir_output_file.read() - if "DIFlagAllCallsDescribed" not in buf: - return "Compiler did not introduce DIFlagAllCallsDescribed IR flag" + if "DIFlagAllCallsDescribed" not in buf: + return "Compiler did not introduce DIFlagAllCallsDescribed IR flag" - return None + return None return skipTestIfFn(is_compiler_clang_with_call_site_info)(func) @@ -957,7 +959,7 @@ def skipUnlessUndefinedBehaviorSanitizer(func): ) # We need to write out the object into a named temp file for inspection. - outputf = tempfile.NamedTemporaryFile() + outputf = temp_file.OnDiskTempFile() # Try to compile with ubsan turned on. if not _compiler_supports( @@ -969,7 +971,7 @@ def skipUnlessUndefinedBehaviorSanitizer(func): return "Compiler cannot compile with -fsanitize=undefined" # Check that we actually see ubsan instrumentation in the binary. - cmd = "nm %s" % outputf.name + cmd = "nm %s" % outputf.path with os.popen(cmd) as nm_output: if "___ubsan_handle_divrem_overflow" not in nm_output.read(): return "Division by zero instrumentation is missing" @@ -1037,40 +1039,37 @@ def skipUnlessAArch64MTELinuxCompiler(func): def is_toolchain_with_mte(): compiler_path = lldbplatformutil.getCompiler() - f = tempfile.NamedTemporaryFile(delete=False) - if lldbplatformutil.getPlatform() == "windows": - return "MTE tests are not compatible with 'windows'" - - # Note hostos may be Windows. - f.close() + with temp_file.OnDiskTempFile() as f: + if lldbplatformutil.getPlatform() == "windows": + return "MTE tests are not compatible with 'windows'" + + cmd = f"{compiler_path} -x c -o {f.path} -" + if ( + subprocess.run( + cmd, shell=True, input="int main() {}".encode() + ).returncode + != 0 + ): + # Cannot compile at all, don't skip the test + # so that we report the broken compiler normally. + return None - cmd = f"{compiler_path} -x c -o {f.name} -" - if ( - subprocess.run(cmd, shell=True, input="int main() {}".encode()).returncode - != 0 - ): - os.remove(f.name) - # Cannot compile at all, don't skip the test - # so that we report the broken compiler normally. + # We need the Linux headers and ACLE MTE intrinsics + test_src = """ + #include <asm/hwcap.h> + #include <arm_acle.h> + #ifndef HWCAP2_MTE + #error + #endif + int main() { + void* ptr = __arm_mte_create_random_tag((void*)(0), 0); + }""" + cmd = f"{compiler_path} -march=armv8.5-a+memtag -x c -o {f.path} -" + res = subprocess.run(cmd, shell=True, input=test_src.encode()) + if res.returncode != 0: + return "Toolchain does not support MTE" return None - # We need the Linux headers and ACLE MTE intrinsics - test_src = """ - #include <asm/hwcap.h> - #include <arm_acle.h> - #ifndef HWCAP2_MTE - #error - #endif - int main() { - void* ptr = __arm_mte_create_random_tag((void*)(0), 0); - }""" - cmd = f"{compiler_path} -march=armv8.5-a+memtag -x c -o {f.name} -" - res = subprocess.run(cmd, shell=True, input=test_src.encode()) - os.remove(f.name) - if res.returncode != 0: - return "Toolchain does not support MTE" - return None - return skipTestIfFn(is_toolchain_with_mte)(func) diff --git a/lldb/packages/Python/lldbsuite/test/dotest.py b/lldb/packages/Python/lldbsuite/test/dotest.py index 24236e7..47a3c2e 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest.py +++ b/lldb/packages/Python/lldbsuite/test/dotest.py @@ -43,6 +43,7 @@ from . import lldbtest_config from . import test_categories from . import test_result from ..support import seven +from ..support import temp_file def is_exe(fpath): @@ -321,8 +322,13 @@ def parseOptionsAndInitTestdirs(): logging.error("No SDK found with the name %s; aborting...", args.apple_sdk) sys.exit(-1) + if args.triple: + configuration.triple = args.triple + if args.arch: configuration.arch = args.arch + elif args.triple: + configuration.arch = args.triple.split("-")[0] else: configuration.arch = platform_machine @@ -780,8 +786,8 @@ def canRunLibcxxTests(): return True, "libc++ always present" if platform == "linux": - with tempfile.NamedTemporaryFile() as f: - cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.name, "-"] + with temp_file.OnDiskTempFile() as f: + cmd = [configuration.compiler, "-xc++", "-stdlib=libc++", "-o", f.path, "-"] p = subprocess.Popen( cmd, stdin=subprocess.PIPE, @@ -840,8 +846,8 @@ def canRunMsvcStlTests(): if platform != "windows": return False, f"Don't know how to build with MSVC's STL on {platform}" - with tempfile.NamedTemporaryFile() as f: - cmd = [configuration.compiler, "-xc++", "-o", f.name, "-E", "-"] + with temp_file.OnDiskTempFile() as f: + cmd = [configuration.compiler, "-xc++", "-o", f.path, "-E", "-"] p = subprocess.Popen( cmd, stdin=subprocess.PIPE, diff --git a/lldb/packages/Python/lldbsuite/test/dotest_args.py b/lldb/packages/Python/lldbsuite/test/dotest_args.py index e9c2138..fce9e41 100644 --- a/lldb/packages/Python/lldbsuite/test/dotest_args.py +++ b/lldb/packages/Python/lldbsuite/test/dotest_args.py @@ -58,6 +58,14 @@ def create_parser(): """Specify the path to sysroot. This overrides apple_sdk sysroot.""" ), ) + group.add_argument( + "--triple", + metavar="triple", + dest="triple", + help=textwrap.dedent( + """Specify the target triple. Used for cross compilation.""" + ), + ) if sys.platform == "darwin": group.add_argument( "--apple-sdk", diff --git a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules index 8521ca5..e72ffd1 100644 --- a/lldb/packages/Python/lldbsuite/test/make/Makefile.rules +++ b/lldb/packages/Python/lldbsuite/test/make/Makefile.rules @@ -149,6 +149,16 @@ else endif #---------------------------------------------------------------------- +# Use LLD when cross compiling on Darwin. +#---------------------------------------------------------------------- +ifeq "$(HOST_OS)" "Darwin" + ifneq (,$(filter $(OS), Android FreeBSD Linux NetBSD Windows_NT)) + LDFLAGS += -fuse-ld=lld + endif +endif + + +#---------------------------------------------------------------------- # ARCHFLAG is the flag used to tell the compiler which architecture # to compile for. The default is the flag that clang accepts. #---------------------------------------------------------------------- diff --git a/lldb/scripts/framework-header-fix.py b/lldb/scripts/framework-header-fix.py index aa034db..36c5c67 100755 --- a/lldb/scripts/framework-header-fix.py +++ b/lldb/scripts/framework-header-fix.py @@ -112,7 +112,7 @@ def main(): # but passing them in with dashes for this script causes argparse to think that they're # arguments in and of themself, so they need to passed in without dashes. if args.unifdef_guards: - unifdef_guards = ["-" + guard for guard in args.unifdef_guards] + unifdef_guards = ["-U" + guard for guard in args.unifdef_guards] # Create the framework's header dir if it doesn't already exist if not os.path.exists(os.path.dirname(output_file_path)): diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp index a2c004d..d7589cc 100644 --- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp +++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp @@ -18,11 +18,14 @@ #include "lldb/Interpreter/OptionGroupValueObjectDisplay.h" #include "lldb/Target/StackFrame.h" #include "lldb/Utility/ConstString.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" #include "lldb/ValueObject/ValueObject.h" #include "lldb/lldb-defines.h" #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" #include <regex> @@ -132,27 +135,22 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, }; // Dump `valobj` according to whether `po` was requested or not. - auto dump_val_object = [&](ValueObject &valobj) { + auto dump_val_object = [&](ValueObject &valobj) -> Error { if (is_po) { StreamString temp_result_stream; - if (llvm::Error error = valobj.Dump(temp_result_stream, dump_options)) { - result.AppendError(toString(std::move(error))); - return; - } + if (Error err = valobj.Dump(temp_result_stream, dump_options)) + return err; llvm::StringRef output = temp_result_stream.GetString(); maybe_add_hint(output); result.GetOutputStream() << output; } else { - llvm::Error error = - valobj.Dump(result.GetOutputStream(), dump_options); - if (error) { - result.AppendError(toString(std::move(error))); - return; - } + if (Error err = valobj.Dump(result.GetOutputStream(), dump_options)) + return err; } m_interpreter.PrintWarningsIfNecessary(result.GetOutputStream(), m_cmd_name); result.SetStatus(eReturnStatusSuccessFinishResult); + return Error::success(); }; // First, try `expr` as a _limited_ frame variable expression path: only the @@ -186,8 +184,13 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, expr); } - dump_val_object(*valobj_sp); - return; + Error err = dump_val_object(*valobj_sp); + if (!err) + return; + + // Dump failed, continue on to expression evaluation. + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), + "could not print frame variable '{1}': {0}", expr); } } @@ -196,8 +199,14 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, if (auto *state = target.GetPersistentExpressionStateForLanguage(language)) if (auto var_sp = state->GetVariable(expr)) if (auto valobj_sp = var_sp->GetValueObject()) { - dump_val_object(*valobj_sp); - return; + Error err = dump_val_object(*valobj_sp); + if (!err) + return; + + // Dump failed, continue on to expression evaluation. + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), std::move(err), + "could not print persistent variable '{1}': {0}", + expr); } // Third, and lastly, try `expr` as a source expression to evaluate. @@ -248,10 +257,12 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command, result.AppendNoteWithFormatv("ran `expression {0}{1}`", flags, expr); } - if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) - dump_val_object(*valobj_sp); - else + if (valobj_sp->GetError().GetError() != UserExpression::kNoResult) { + if (Error err = dump_val_object(*valobj_sp)) + result.SetError(std::move(err)); + } else { result.SetStatus(eReturnStatusSuccessFinishNoResult); + } if (suppress_result) if (auto result_var_sp = diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 5792c13..af1ff3e 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -156,6 +156,7 @@ public: case eFormatBinary: case eFormatFloat: + case eFormatFloat128: case eFormatOctal: case eFormatDecimal: case eFormatEnum: @@ -1356,6 +1357,7 @@ protected: switch (m_format_options.GetFormat()) { case kNumFormats: case eFormatFloat: // TODO: add support for floats soon + case eFormatFloat128: case eFormatCharPrintable: case eFormatBytesWithASCII: case eFormatComplex: diff --git a/lldb/source/Core/DumpDataExtractor.cpp b/lldb/source/Core/DumpDataExtractor.cpp index 7214073..37dffc7 100644 --- a/lldb/source/Core/DumpDataExtractor.cpp +++ b/lldb/source/Core/DumpDataExtractor.cpp @@ -318,14 +318,15 @@ static void printMemoryTags(const DataExtractor &DE, Stream *s, } static const llvm::fltSemantics &GetFloatSemantics(const TargetSP &target_sp, - size_t byte_size) { + size_t byte_size, + lldb::Format format) { if (target_sp) { auto type_system_or_err = target_sp->GetScratchTypeSystemForLanguage(eLanguageTypeC); if (!type_system_or_err) llvm::consumeError(type_system_or_err.takeError()); else if (auto ts = *type_system_or_err) - return ts->GetFloatTypeSemantics(byte_size); + return ts->GetFloatTypeSemantics(byte_size, format); } // No target, just make a reasonable guess switch(byte_size) { @@ -335,7 +336,13 @@ static const llvm::fltSemantics &GetFloatSemantics(const TargetSP &target_sp, return llvm::APFloat::IEEEsingle(); case 8: return llvm::APFloat::IEEEdouble(); - } + case 16: + if (format == eFormatFloat128) { + return llvm::APFloat::IEEEquad(); + } + // Otherwise it's ambigious whether a 16-byte float is a float128 or a + // target-specific long double. + } return llvm::APFloat::Bogus(); } @@ -653,6 +660,7 @@ lldb::offset_t lldb_private::DumpDataExtractor( } } break; + case eFormatFloat128: case eFormatFloat: { TargetSP target_sp; if (exe_scope) @@ -666,7 +674,7 @@ lldb::offset_t lldb_private::DumpDataExtractor( const unsigned format_precision = 0; const llvm::fltSemantics &semantics = - GetFloatSemantics(target_sp, item_byte_size); + GetFloatSemantics(target_sp, item_byte_size, item_format); // Recalculate the byte size in case of a difference. This is possible // when item_byte_size is 16 (128-bit), because you could get back the diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index 90997da..f27a95d 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -130,8 +130,10 @@ Module *Module::GetAllocatedModuleAtIndex(size_t idx) { return nullptr; } +static std::atomic<lldb::user_id_t> g_unique_id = 1; + Module::Module(const ModuleSpec &module_spec) - : m_unwind_table(*this), m_file_has_changed(false), + : UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false), m_first_file_changed_log(false) { // Scope for locker below... { @@ -236,7 +238,8 @@ Module::Module(const ModuleSpec &module_spec) Module::Module(const FileSpec &file_spec, const ArchSpec &arch, ConstString object_name, lldb::offset_t object_offset, const llvm::sys::TimePoint<> &object_mod_time) - : m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), + : UserID(g_unique_id++), + m_mod_time(FileSystem::Instance().GetModificationTime(file_spec)), m_arch(arch), m_file(file_spec), m_object_name(object_name), m_object_offset(object_offset), m_object_mod_time(object_mod_time), m_unwind_table(*this), m_file_has_changed(false), @@ -257,7 +260,7 @@ Module::Module(const FileSpec &file_spec, const ArchSpec &arch, } Module::Module() - : m_unwind_table(*this), m_file_has_changed(false), + : UserID(g_unique_id++), m_unwind_table(*this), m_file_has_changed(false), m_first_file_changed_log(false) { std::lock_guard<std::recursive_mutex> guard( GetAllocationModuleCollectionMutex()); diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index d2e5be8..01f46b6 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -584,6 +584,20 @@ ModuleSP ModuleList::FindModule(const UUID &uuid) const { return module_sp; } +ModuleSP ModuleList::FindModule(lldb::user_id_t uid) const { + ModuleSP module_sp; + ForEach([&](const ModuleSP &m) { + if (m->GetID() == uid) { + module_sp = m; + return IterationAction::Stop; + } + + return IterationAction::Continue; + }); + + return module_sp; +} + void ModuleList::FindTypes(Module *search_first, const TypeQuery &query, TypeResults &results) const { std::lock_guard<std::recursive_mutex> guard(m_modules_mutex); diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index 7862fb8..8595f81 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -72,6 +72,7 @@ static constexpr FormatInfo g_format_infos[] = { {eFormatInstruction, 'i', "instruction"}, {eFormatVoid, 'v', "void"}, {eFormatUnicode8, 'u', "unicode8"}, + {eFormatFloat128, '\0', "float128"}, }; static_assert((sizeof(g_format_infos) / sizeof(g_format_infos[0])) == diff --git a/lldb/source/DataFormatters/VectorType.cpp b/lldb/source/DataFormatters/VectorType.cpp index 8a842b8..c2355fb 100644 --- a/lldb/source/DataFormatters/VectorType.cpp +++ b/lldb/source/DataFormatters/VectorType.cpp @@ -55,6 +55,8 @@ static CompilerType GetCompilerTypeForFormat(lldb::Format format, case lldb::eFormatFloat: return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat); + case lldb::eFormatFloat128: + return type_system->GetBasicTypeFromAST(lldb::eBasicTypeFloat128); case lldb::eFormatHex: case lldb::eFormatHexUppercase: diff --git a/lldb/source/Expression/Expression.cpp b/lldb/source/Expression/Expression.cpp index 93f585e..796851f 100644 --- a/lldb/source/Expression/Expression.cpp +++ b/lldb/source/Expression/Expression.cpp @@ -10,6 +10,11 @@ #include "lldb/Target/ExecutionContextScope.h" #include "lldb/Target/Target.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + using namespace lldb_private; Expression::Expression(Target &target) @@ -26,3 +31,47 @@ Expression::Expression(ExecutionContextScope &exe_scope) m_jit_end_addr(LLDB_INVALID_ADDRESS) { assert(m_target_wp.lock()); } + +llvm::Expected<FunctionCallLabel> +lldb_private::FunctionCallLabel::fromString(llvm::StringRef label) { + llvm::SmallVector<llvm::StringRef, 4> components; + label.split(components, ":", /*MaxSplit=*/3); + + if (components.size() != 4) + return llvm::createStringError("malformed function call label."); + + if (components[0] != FunctionCallLabelPrefix) + return llvm::createStringError(llvm::formatv( + "expected function call label prefix '{0}' but found '{1}' instead.", + FunctionCallLabelPrefix, components[0])); + + llvm::StringRef module_label = components[1]; + llvm::StringRef die_label = components[2]; + + lldb::user_id_t module_id = 0; + if (!llvm::to_integer(module_label, module_id)) + return llvm::createStringError( + llvm::formatv("failed to parse module ID from '{0}'.", module_label)); + + lldb::user_id_t die_id; + if (!llvm::to_integer(die_label, die_id)) + return llvm::createStringError( + llvm::formatv("failed to parse symbol ID from '{0}'.", die_label)); + + return FunctionCallLabel{/*.module_id=*/module_id, + /*.symbol_id=*/die_id, + /*.lookup_name=*/components[3]}; +} + +std::string lldb_private::FunctionCallLabel::toString() const { + return llvm::formatv("{0}:{1:x}:{2:x}:{3}", FunctionCallLabelPrefix, + module_id, symbol_id, lookup_name) + .str(); +} + +void llvm::format_provider<FunctionCallLabel>::format( + const FunctionCallLabel &label, raw_ostream &OS, StringRef Style) { + OS << llvm::formatv("FunctionCallLabel{ module_id: {0:x}, symbol_id: {1:x}, " + "lookup_name: {2} }", + label.module_id, label.symbol_id, label.lookup_name); +} diff --git a/lldb/source/Expression/IRExecutionUnit.cpp b/lldb/source/Expression/IRExecutionUnit.cpp index 6f812b9..5e40df2 100644 --- a/lldb/source/Expression/IRExecutionUnit.cpp +++ b/lldb/source/Expression/IRExecutionUnit.cpp @@ -13,6 +13,7 @@ #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Support/Error.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" @@ -20,6 +21,7 @@ #include "lldb/Core/Disassembler.h" #include "lldb/Core/Module.h" #include "lldb/Core/Section.h" +#include "lldb/Expression/Expression.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/ObjectFileJIT.h" #include "lldb/Host/HostInfo.h" @@ -36,6 +38,7 @@ #include "lldb/Utility/LLDBAssert.h" #include "lldb/Utility/LLDBLog.h" #include "lldb/Utility/Log.h" +#include "lldb/lldb-defines.h" #include <optional> @@ -771,6 +774,40 @@ private: lldb::addr_t m_best_internal_load_address = LLDB_INVALID_ADDRESS; }; +/// Returns address of the function referred to by the special function call +/// label \c label. +static llvm::Expected<lldb::addr_t> +ResolveFunctionCallLabel(const FunctionCallLabel &label, + const lldb_private::SymbolContext &sc, + bool &symbol_was_missing_weak) { + symbol_was_missing_weak = false; + + if (!sc.target_sp) + return llvm::createStringError("target not available."); + + auto module_sp = sc.target_sp->GetImages().FindModule(label.module_id); + if (!module_sp) + return llvm::createStringError( + llvm::formatv("failed to find module by UID {0}", label.module_id)); + + auto *symbol_file = module_sp->GetSymbolFile(); + if (!symbol_file) + return llvm::createStringError( + llvm::formatv("no SymbolFile found on module {0:x}.", module_sp.get())); + + auto sc_or_err = symbol_file->ResolveFunctionCallLabel(label); + if (!sc_or_err) + return llvm::joinErrors( + llvm::createStringError("failed to resolve function by UID"), + sc_or_err.takeError()); + + SymbolContextList sc_list; + sc_list.Append(*sc_or_err); + + LoadAddressResolver resolver(*sc.target_sp, symbol_was_missing_weak); + return resolver.Resolve(sc_list).value_or(LLDB_INVALID_ADDRESS); +} + lldb::addr_t IRExecutionUnit::FindInSymbols(const std::vector<ConstString> &names, const lldb_private::SymbolContext &sc, @@ -906,6 +943,34 @@ lldb::addr_t IRExecutionUnit::FindInUserDefinedSymbols( lldb::addr_t IRExecutionUnit::FindSymbol(lldb_private::ConstString name, bool &missing_weak) { + if (name.GetStringRef().starts_with(FunctionCallLabelPrefix)) { + auto label_or_err = FunctionCallLabel::fromString(name); + if (!label_or_err) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), label_or_err.takeError(), + "failed to create FunctionCallLabel from '{1}': {0}", + name.GetStringRef()); + return LLDB_INVALID_ADDRESS; + } + + if (auto addr_or_err = + ResolveFunctionCallLabel(*label_or_err, m_sym_ctx, missing_weak)) { + return *addr_or_err; + } else { + LLDB_LOG_ERROR(GetLog(LLDBLog::Expressions), addr_or_err.takeError(), + "Failed to resolve function call label '{1}': {0}", + name.GetStringRef()); + + // Fall back to lookup by name despite error in resolving the label. + // May happen in practice if the definition of a function lives in + // a different lldb_private::Module than it's declaration. Meaning + // we couldn't pin-point it using the information encoded in the label. + name.SetString(label_or_err->lookup_name); + } + } + + // TODO: now with function call labels, do we still need to + // generate alternate manglings? + std::vector<ConstString> candidate_C_names; std::vector<ConstString> candidate_CPlusPlus_names; diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp index fa74e88..9140483 100644 --- a/lldb/source/Expression/IRInterpreter.cpp +++ b/lldb/source/Expression/IRInterpreter.cpp @@ -259,7 +259,9 @@ public: break; case Value::FunctionVal: if (const Function *constant_func = dyn_cast<Function>(constant)) { - lldb_private::ConstString name(constant_func->getName()); + lldb_private::ConstString name( + llvm::GlobalValue::dropLLVMManglingEscape( + constant_func->getName())); bool missing_weak = false; lldb::addr_t addr = m_execution_unit.FindSymbol(name, missing_weak); if (addr == LLDB_INVALID_ADDRESS) diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index 5992b54..510f9c7 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -82,10 +82,11 @@ int __pthread_fchdir(int fildes); using namespace lldb; using namespace lldb_private; -#if !defined(__APPLE__) -// The system log is currently only meaningful on Darwin, where this means -// os_log. The meaning of a "system log" isn't as clear on other platforms, and -// therefore we don't providate a default implementation. Vendors are free to +#if !defined(__APPLE__) && !defined(_WIN32) +// The system log is currently only meaningful on Darwin and Windows. +// On Darwin, this means os_log. On Windows this means Events Viewer. +// The meaning of a "system log" isn't as clear on other platforms, and +// therefore we don't providate a default implementation. Vendors are free // to implement this function if they have a use for it. void Host::SystemLog(Severity severity, llvm::StringRef message) {} #endif diff --git a/lldb/source/Host/windows/Host.cpp b/lldb/source/Host/windows/Host.cpp index a7369e7..4e747f7 100644 --- a/lldb/source/Host/windows/Host.cpp +++ b/lldb/source/Host/windows/Host.cpp @@ -22,7 +22,9 @@ #include "lldb/Utility/StreamString.h" #include "lldb/Utility/StructuredData.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/ManagedStatic.h" // Windows includes #include <tlhelp32.h> @@ -302,3 +304,64 @@ Environment Host::GetEnvironment() { } return env; } + +/// Manages the lifecycle of a Windows Event's Source. +/// The destructor will call DeregisterEventSource. +/// This class is meant to be used with \ref llvm::ManagedStatic. +class WindowsEventLog { +public: + WindowsEventLog() : handle(RegisterEventSource(nullptr, L"lldb")) {} + + ~WindowsEventLog() { + if (handle) + DeregisterEventSource(handle); + } + + HANDLE GetHandle() const { return handle; } + +private: + HANDLE handle; +}; + +static llvm::ManagedStatic<WindowsEventLog> event_log; + +static std::wstring AnsiToUtf16(const std::string &ansi) { + if (ansi.empty()) + return {}; + + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, nullptr, 0); + if (unicode_length == 0) + return {}; + + std::wstring unicode(unicode_length, L'\0'); + MultiByteToWideChar(CP_ACP, 0, ansi.c_str(), -1, &unicode[0], unicode_length); + return unicode; +} + +void Host::SystemLog(Severity severity, llvm::StringRef message) { + HANDLE h = event_log->GetHandle(); + if (!h) + return; + + std::wstring wide_message = AnsiToUtf16(message.str()); + if (wide_message.empty()) + return; + + LPCWSTR msg_ptr = wide_message.c_str(); + + WORD event_type; + switch (severity) { + case lldb::eSeverityWarning: + event_type = EVENTLOG_WARNING_TYPE; + break; + case lldb::eSeverityError: + event_type = EVENTLOG_ERROR_TYPE; + break; + case lldb::eSeverityInfo: + default: + event_type = EVENTLOG_INFORMATION_TYPE; + } + + ReportEventW(h, event_type, 0, 0, nullptr, 1, 0, &msg_ptr, nullptr); +} diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp index 9f77fbc..214e260 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionDeclMap.cpp @@ -1991,7 +1991,7 @@ void ClangExpressionDeclMap::AddContextClassType(NameSearchContext &context, const bool is_artificial = false; CXXMethodDecl *method_decl = m_clang_ast_context->AddMethodToCXXRecordType( - copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", nullptr, + copied_clang_type.GetOpaqueQualType(), "$__lldb_expr", /*asm_label=*/{}, method_type, lldb::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp index 3995bc0..e5a1d2d 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionParser.cpp @@ -894,7 +894,7 @@ ClangExpressionParser::ClangExpressionParser( m_llvm_context = std::make_unique<LLVMContext>(); m_code_generator.reset(CreateLLVMCodeGen( m_compiler->getDiagnostics(), module_name, - &m_compiler->getVirtualFileSystem(), m_compiler->getHeaderSearchOpts(), + m_compiler->getVirtualFileSystemPtr(), m_compiler->getHeaderSearchOpts(), m_compiler->getPreprocessorOpts(), m_compiler->getCodeGenOpts(), *m_llvm_context)); } diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp index 06f3a7e..ff9ed9c 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangExpressionSourceCode.cpp @@ -260,9 +260,8 @@ TokenVerifier::TokenVerifier(std::string body) { // Let's build the actual source code Clang needs and setup some utility // objects. - llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); DiagnosticOptions diags_opts; - DiagnosticsEngine diags(diag_ids, diags_opts); + DiagnosticsEngine diags(DiagnosticIDs::create(), diags_opts); clang::SourceManager SM(diags, file_mgr); auto buf = llvm::MemoryBuffer::getMemBuffer(body); diff --git a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp index 2f838b3..d54f072 100644 --- a/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp +++ b/lldb/source/Plugins/ExpressionParser/Clang/ClangModulesDeclVendor.cpp @@ -747,7 +747,7 @@ ClangModulesDeclVendor::Create(Target &target) { // Make sure clang uses the same VFS as LLDB. instance->createFileManager(FileSystem::Instance().GetVirtualFileSystem()); - instance->setDiagnostics(diagnostics_engine.get()); + instance->setDiagnostics(diagnostics_engine); std::unique_ptr<clang::FrontendAction> action(new clang::SyntaxOnlyAction); diff --git a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp index 2adde02..5e429a9 100644 --- a/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp +++ b/lldb/source/Plugins/Instruction/RISCV/EmulateInstructionRISCV.cpp @@ -1807,7 +1807,7 @@ RISCVSingleStepBreakpointLocationsPredictor::GetBreakpointLocations( Log *log = GetLog(LLDBLog::Unwind); LLDB_LOGF(log, "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " - "corresponding load reserve insturuction", + "corresponding load reserve instruction", __FUNCTION__); return {*pc + (inst->is_rvc ? 2u : 4u)}; } @@ -1839,7 +1839,7 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( EmulateInstructionRISCV *riscv_emulator = static_cast<EmulateInstructionRISCV *>(m_emulator_up.get()); - // Handle instructions between LR and SC. According to unprivilleged + // Handle instructions between LR and SC. According to unprivileged // RISC-V ISA there can be at most 16 instructions in the sequence. lldb::addr_t entry_pc = pc; // LR instruction address @@ -1872,7 +1872,7 @@ RISCVSingleStepBreakpointLocationsPredictor::HandleAtomicSequence( Log *log = GetLog(LLDBLog::Unwind); LLDB_LOGF(log, "RISCVSingleStepBreakpointLocationsPredictor::%s: can't find " - "corresponding store conditional insturuction", + "corresponding store conditional instruction", __FUNCTION__); return {entry_pc + (lr_inst->is_rvc ? 2u : 4u)}; } diff --git a/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp index 8cc5714..9cb5ea3 100644 --- a/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp +++ b/lldb/source/Plugins/Language/ClangCommon/ClangHighlighter.cpp @@ -162,9 +162,8 @@ void ClangHighlighter::Highlight(const HighlightStyle &options, // Let's build the actual source code Clang needs and setup some utility // objects. std::string full_source = previous_lines.str() + line.str(); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_ids(new DiagnosticIDs()); DiagnosticOptions diags_opts; - DiagnosticsEngine diags(diag_ids, diags_opts); + DiagnosticsEngine diags(DiagnosticIDs::create(), diags_opts); clang::SourceManager SM(diags, file_mgr); auto buf = llvm::MemoryBuffer::getMemBuffer(full_source); diff --git a/lldb/source/Plugins/Process/wasm/CMakeLists.txt b/lldb/source/Plugins/Process/wasm/CMakeLists.txt index ff8a3c7..779b97e 100644 --- a/lldb/source/Plugins/Process/wasm/CMakeLists.txt +++ b/lldb/source/Plugins/Process/wasm/CMakeLists.txt @@ -1,5 +1,6 @@ add_lldb_library(lldbPluginProcessWasm PLUGIN ProcessWasm.cpp + RegisterContextWasm.cpp ThreadWasm.cpp UnwindWasm.cpp diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp index 5eeabec..580e8c1 100644 --- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp @@ -131,3 +131,36 @@ ProcessWasm::GetWasmCallStack(lldb::tid_t tid) { return call_stack_pcs; } + +llvm::Expected<lldb::DataBufferSP> +ProcessWasm::GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index, + int index) { + StreamString packet; + switch (kind) { + case eWasmTagLocal: + packet.Printf("qWasmLocal:"); + break; + case eWasmTagGlobal: + packet.Printf("qWasmGlobal:"); + break; + case eWasmTagOperandStack: + packet.PutCString("qWasmStackValue:"); + break; + case eWasmTagNotAWasmLocation: + return llvm::createStringError("not a Wasm location"); + } + packet.Printf("%d;%d", frame_index, index); + + StringExtractorGDBRemote response; + if (m_gdb_comm.SendPacketAndWaitForResponse(packet.GetString(), response) != + GDBRemoteCommunication::PacketResult::Success) + return llvm::createStringError("failed to send Wasm variable"); + + if (!response.IsNormalResponse()) + return llvm::createStringError("failed to get response for Wasm variable"); + + WritableDataBufferSP buffer_sp( + new DataBufferHeap(response.GetStringRef().size() / 2, 0)); + response.GetHexBytes(buffer_sp->GetData(), '\xcc'); + return buffer_sp; +} diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h b/lldb/source/Plugins/Process/wasm/ProcessWasm.h index bab14a8..22effe7 100644 --- a/lldb/source/Plugins/Process/wasm/ProcessWasm.h +++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h @@ -10,6 +10,7 @@ #define LLDB_SOURCE_PLUGINS_PROCESS_WASM_PROCESSWASM_H #include "Plugins/Process/gdb-remote/ProcessGDBRemote.h" +#include "Utility/WasmVirtualRegisters.h" namespace lldb_private { namespace wasm { @@ -71,12 +72,19 @@ public: /// Retrieve the current call stack from the WebAssembly remote process. llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(lldb::tid_t tid); + /// Query the value of a WebAssembly variable from the WebAssembly + /// remote process. + llvm::Expected<lldb::DataBufferSP> + GetWasmVariable(WasmVirtualRegisterKinds kind, int frame_index, int index); + protected: std::shared_ptr<process_gdb_remote::ThreadGDBRemote> CreateThread(lldb::tid_t tid) override; private: friend class UnwindWasm; + friend class ThreadWasm; + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP &GetRegisterInfo() { return m_register_info_sp; } diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp new file mode 100644 index 0000000..b468171 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RegisterContextWasm.h" +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ProcessWasm.h" +#include "ThreadWasm.h" +#include "lldb/Utility/LLDBLog.h" +#include "lldb/Utility/Log.h" +#include "lldb/Utility/RegisterValue.h" +#include "llvm/Support/Error.h" +#include <memory> + +using namespace lldb; +using namespace lldb_private; +using namespace lldb_private::process_gdb_remote; +using namespace lldb_private::wasm; + +RegisterContextWasm::RegisterContextWasm( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + GDBRemoteDynamicRegisterInfoSP reg_info_sp) + : GDBRemoteRegisterContext(thread, concrete_frame_idx, reg_info_sp, false, + false) {} + +RegisterContextWasm::~RegisterContextWasm() = default; + +uint32_t RegisterContextWasm::ConvertRegisterKindToRegisterNumber( + lldb::RegisterKind kind, uint32_t num) { + return num; +} + +size_t RegisterContextWasm::GetRegisterCount() { + // Wasm has no registers. + return 0; +} + +const RegisterInfo *RegisterContextWasm::GetRegisterInfoAtIndex(size_t reg) { + uint32_t tag = GetWasmVirtualRegisterTag(reg); + if (tag == eWasmTagNotAWasmLocation) + return m_reg_info_sp->GetRegisterInfoAtIndex( + GetWasmVirtualRegisterIndex(reg)); + + auto it = m_register_map.find(reg); + if (it == m_register_map.end()) { + WasmVirtualRegisterKinds kind = static_cast<WasmVirtualRegisterKinds>(tag); + std::tie(it, std::ignore) = m_register_map.insert( + {reg, std::make_unique<WasmVirtualRegisterInfo>( + kind, GetWasmVirtualRegisterIndex(reg))}); + } + return it->second.get(); +} + +size_t RegisterContextWasm::GetRegisterSetCount() { return 0; } + +const RegisterSet *RegisterContextWasm::GetRegisterSet(size_t reg_set) { + // Wasm has no registers. + return nullptr; +} + +bool RegisterContextWasm::ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) { + // The only real registers is the PC. + if (reg_info->name) + return GDBRemoteRegisterContext::ReadRegister(reg_info, value); + + // Read the virtual registers. + ThreadWasm *thread = static_cast<ThreadWasm *>(&GetThread()); + ProcessWasm *process = static_cast<ProcessWasm *>(thread->GetProcess().get()); + if (!thread) + return false; + + uint32_t frame_index = m_concrete_frame_idx; + WasmVirtualRegisterInfo *wasm_reg_info = + static_cast<WasmVirtualRegisterInfo *>( + const_cast<RegisterInfo *>(reg_info)); + + llvm::Expected<DataBufferSP> maybe_buffer = process->GetWasmVariable( + wasm_reg_info->kind, frame_index, wasm_reg_info->index); + if (!maybe_buffer) { + LLDB_LOG_ERROR(GetLog(LLDBLog::Process), maybe_buffer.takeError(), + "Failed to read Wasm local: {0}"); + return false; + } + + DataBufferSP buffer_sp = *maybe_buffer; + DataExtractor reg_data(buffer_sp, process->GetByteOrder(), + process->GetAddressByteSize()); + wasm_reg_info->byte_size = buffer_sp->GetByteSize(); + wasm_reg_info->encoding = lldb::eEncodingUint; + + Status error = value.SetValueFromData( + *reg_info, reg_data, reg_info->byte_offset, /*partial_data_ok=*/false); + return error.Success(); +} + +void RegisterContextWasm::InvalidateAllRegisters() {} + +bool RegisterContextWasm::WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) { + // The only real registers is the PC. + if (reg_info->name) + return GDBRemoteRegisterContext::WriteRegister(reg_info, value); + return false; +} diff --git a/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h new file mode 100644 index 0000000..7e63eb8 --- /dev/null +++ b/lldb/source/Plugins/Process/wasm/RegisterContextWasm.h @@ -0,0 +1,69 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H +#define LLDB_SOURCE_PLUGINS_PROCESS_WASM_REGISTERCONTEXTWASM_H + +#include "Plugins/Process/gdb-remote/GDBRemoteRegisterContext.h" +#include "ThreadWasm.h" +#include "Utility/WasmVirtualRegisters.h" +#include "lldb/lldb-private-types.h" +#include <unordered_map> + +namespace lldb_private { +namespace wasm { + +class RegisterContextWasm; + +typedef std::shared_ptr<RegisterContextWasm> RegisterContextWasmSP; + +struct WasmVirtualRegisterInfo : public RegisterInfo { + WasmVirtualRegisterKinds kind; + uint32_t index; + + WasmVirtualRegisterInfo(WasmVirtualRegisterKinds kind, uint32_t index) + : RegisterInfo(), kind(kind), index(index) {} +}; + +class RegisterContextWasm + : public process_gdb_remote::GDBRemoteRegisterContext { +public: + RegisterContextWasm( + wasm::ThreadWasm &thread, uint32_t concrete_frame_idx, + process_gdb_remote::GDBRemoteDynamicRegisterInfoSP reg_info_sp); + + ~RegisterContextWasm() override; + + uint32_t ConvertRegisterKindToRegisterNumber(lldb::RegisterKind kind, + uint32_t num) override; + + void InvalidateAllRegisters() override; + + size_t GetRegisterCount() override; + + const RegisterInfo *GetRegisterInfoAtIndex(size_t reg) override; + + size_t GetRegisterSetCount() override; + + const RegisterSet *GetRegisterSet(size_t reg_set) override; + + bool ReadRegister(const RegisterInfo *reg_info, + RegisterValue &value) override; + + bool WriteRegister(const RegisterInfo *reg_info, + const RegisterValue &value) override; + +private: + std::unordered_map<size_t, std::unique_ptr<WasmVirtualRegisterInfo>> + m_register_map; +}; + +} // namespace wasm +} // namespace lldb_private + +#endif diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp index a6553ff..0666b75 100644 --- a/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.cpp @@ -9,6 +9,7 @@ #include "ThreadWasm.h" #include "ProcessWasm.h" +#include "RegisterContextWasm.h" #include "UnwindWasm.h" #include "lldb/Target/Target.h" @@ -32,3 +33,19 @@ llvm::Expected<std::vector<lldb::addr_t>> ThreadWasm::GetWasmCallStack() { } return llvm::createStringError("no process"); } + +lldb::RegisterContextSP +ThreadWasm::CreateRegisterContextForFrame(StackFrame *frame) { + uint32_t concrete_frame_idx = 0; + ProcessSP process_sp(GetProcess()); + ProcessWasm *wasm_process = static_cast<ProcessWasm *>(process_sp.get()); + + if (frame) + concrete_frame_idx = frame->GetConcreteFrameIndex(); + + if (concrete_frame_idx == 0) + return std::make_shared<RegisterContextWasm>( + *this, concrete_frame_idx, wasm_process->GetRegisterInfo()); + + return GetUnwinder().CreateRegisterContextForFrame(frame); +} diff --git a/lldb/source/Plugins/Process/wasm/ThreadWasm.h b/lldb/source/Plugins/Process/wasm/ThreadWasm.h index 1c90f58..c2f5762 100644 --- a/lldb/source/Plugins/Process/wasm/ThreadWasm.h +++ b/lldb/source/Plugins/Process/wasm/ThreadWasm.h @@ -25,6 +25,9 @@ public: /// Retrieve the current call stack from the WebAssembly remote process. llvm::Expected<std::vector<lldb::addr_t>> GetWasmCallStack(); + lldb::RegisterContextSP + CreateRegisterContextForFrame(StackFrame *frame) override; + protected: Unwind &GetUnwinder() override; diff --git a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp index 81c6731..591fded 100644 --- a/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp +++ b/lldb/source/Plugins/SymbolFile/CTF/SymbolFileCTF.cpp @@ -738,9 +738,29 @@ size_t SymbolFileCTF::ParseTypes(CompileUnit &cu) { LLDB_LOG(log, "Parsed {0} CTF types", m_ctf_types.size()); - for (lldb::user_id_t uid = 1; uid < type_uid; ++uid) + for (lldb::user_id_t uid = 1; uid < type_uid; ++uid) { ResolveTypeUID(uid); + // Remove the CTF type because we don't need it anymore, except for record + // types which we may need to complete later. + auto ctf_type_it = m_ctf_types.find(uid); + if (ctf_type_it != m_ctf_types.end()) { + CTFType *ctf_type = ctf_type_it->second.get(); + if (!llvm::isa<CTFRecord>(ctf_type)) + m_ctf_types.erase(uid); + } + } + +#ifndef NDEBUG + // Verify that the only CTF types left at this point are record types. + for (auto &t : m_ctf_types) { + CTFType *ctf_type = t.second.get(); + assert(ctf_type && "invalid type in m_ctf_types"); + assert(llvm::isa<CTFRecord>(ctf_type) && "leaking non record type"); + } + +#endif + LLDB_LOG(log, "Created {0} CTF types", m_types.size()); return m_types.size(); @@ -994,6 +1014,8 @@ lldb_private::Type *SymbolFileCTF::ResolveTypeUID(lldb::user_id_t type_uid) { CTFType *ctf_type = ctf_type_it->second.get(); assert(ctf_type && "m_ctf_types should only contain valid CTF types"); + assert(ctf_type->uid == type_uid && + "CTF type UID doesn't match UID in m_ctf_types"); Log *log = GetLog(LLDBLog::Symbols); @@ -1015,11 +1037,6 @@ lldb_private::Type *SymbolFileCTF::ResolveTypeUID(lldb::user_id_t type_uid) { m_types[type_uid] = type_sp; - // Except for record types which we'll need to complete later, we don't need - // the CTF type anymore. - if (!isa<CTFRecord>(ctf_type)) - m_ctf_types.erase(type_uid); - return type_sp.get(); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp index 4bfbb4d..9762ead 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/Module.h" #include "lldb/Symbol/Function.h" +#include "lldb/lldb-private-enumerations.h" #include "llvm/Support/DJB.h" using namespace lldb; @@ -275,7 +276,7 @@ void AppleDWARFIndex::GetNamespaces( void AppleDWARFIndex::GetFunctions( const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { if (!m_apple_names_up) return; @@ -288,15 +289,16 @@ void AppleDWARFIndex::GetFunctions( ReportInvalidDIERef(die_ref, name); continue; } - if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback)) + if (ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, callback) == + IterationAction::Stop) return; } } void AppleDWARFIndex::GetFunctions( const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) { - return GetGlobalVariables(regex, callback); + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { + return GetGlobalVariables(regex, IterationActionAdaptor(callback)); } void AppleDWARFIndex::Dump(Stream &s) { diff --git a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h index 73de75b..c0f0eb6 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/AppleDWARFIndex.h @@ -61,12 +61,13 @@ public: llvm::function_ref<bool(DWARFDIE die)> callback) override; void GetNamespaces(ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const Module::LookupInfo &lookup_info, - SymbolFileDWARF &dwarf, - const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; + void GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; void Dump(Stream &s) override; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp index ba65f50..781c1c6c 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserClang.cpp @@ -24,6 +24,7 @@ #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/Module.h" #include "lldb/Core/Value.h" +#include "lldb/Expression/Expression.h" #include "lldb/Host/Host.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/Function.h" @@ -249,6 +250,47 @@ static unsigned GetCXXMethodCVQuals(const DWARFDIE &subprogram, return cv_quals; } +static std::string MakeLLDBFuncAsmLabel(const DWARFDIE &die) { + char const *name = die.GetMangledName(/*substitute_name_allowed*/ false); + if (!name) + return {}; + + SymbolFileDWARF *dwarf = die.GetDWARF(); + if (!dwarf) + return {}; + + auto get_module_id = [&](SymbolFile *sym) { + if (!sym) + return LLDB_INVALID_UID; + + auto *obj = sym->GetMainObjectFile(); + if (!obj) + return LLDB_INVALID_UID; + + auto module_sp = obj->GetModule(); + if (!module_sp) + return LLDB_INVALID_UID; + + return module_sp->GetID(); + }; + + lldb::user_id_t module_id = get_module_id(dwarf->GetDebugMapSymfile()); + if (module_id == LLDB_INVALID_UID) + module_id = get_module_id(dwarf); + + if (module_id == LLDB_INVALID_UID) + return {}; + + const auto die_id = die.GetID(); + if (die_id == LLDB_INVALID_UID) + return {}; + + return FunctionCallLabel{/*module_id=*/module_id, + /*symbol_id=*/die_id, + /*.lookup_name=*/name} + .toString(); +} + TypeSP DWARFASTParserClang::ParseTypeFromClangModule(const SymbolContext &sc, const DWARFDIE &die, Log *log) { @@ -1231,7 +1273,7 @@ std::pair<bool, TypeSP> DWARFASTParserClang::ParseCXXMethod( clang::CXXMethodDecl *cxx_method_decl = m_ast.AddMethodToCXXRecordType( class_opaque_type.GetOpaqueQualType(), attrs.name.GetCString(), - attrs.mangled_name, clang_type, accessibility, attrs.is_virtual, + MakeLLDBFuncAsmLabel(die), clang_type, accessibility, attrs.is_virtual, is_static, attrs.is_inline, attrs.is_explicit, is_attr_used, attrs.is_artificial); @@ -1384,7 +1426,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), name, clang_type, attrs.storage, - attrs.is_inline); + attrs.is_inline, MakeLLDBFuncAsmLabel(die)); std::free(name_buf); if (has_template_params) { @@ -1394,7 +1436,7 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, ignore_containing_context ? m_ast.GetTranslationUnitDecl() : containing_decl_ctx, GetOwningClangModule(die), attrs.name.GetStringRef(), clang_type, - attrs.storage, attrs.is_inline); + attrs.storage, attrs.is_inline, /*asm_label=*/{}); clang::FunctionTemplateDecl *func_template_decl = m_ast.CreateFunctionTemplateDecl( containing_decl_ctx, GetOwningClangModule(die), @@ -1406,20 +1448,6 @@ DWARFASTParserClang::ParseSubroutine(const DWARFDIE &die, lldbassert(function_decl); if (function_decl) { - // Attach an asm(<mangled_name>) label to the FunctionDecl. - // This ensures that clang::CodeGen emits function calls - // using symbols that are mangled according to the DW_AT_linkage_name. - // If we didn't do this, the external symbols wouldn't exactly - // match the mangled name LLDB knows about and the IRExecutionUnit - // would have to fall back to searching object files for - // approximately matching function names. The motivating - // example is generating calls to ABI-tagged template functions. - // This is done separately for member functions in - // AddMethodToCXXRecordType. - if (attrs.mangled_name) - function_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - m_ast.getASTContext(), attrs.mangled_name, /*literal=*/false)); - LinkDeclContextToDIE(function_decl, die); const clang::FunctionProtoType *function_prototype( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp index 30c890d..a806506 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.cpp @@ -16,6 +16,7 @@ #include "lldb/Core/Mangled.h" #include "lldb/Core/Module.h" #include "lldb/Target/Language.h" +#include "lldb/lldb-private-enumerations.h" using namespace lldb_private; using namespace lldb; @@ -23,10 +24,10 @@ using namespace lldb_private::plugin::dwarf; DWARFIndex::~DWARFIndex() = default; -bool DWARFIndex::ProcessFunctionDIE( +IterationAction DWARFIndex::ProcessFunctionDIE( const Module::LookupInfo &lookup_info, DWARFDIE die, const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { llvm::StringRef name = lookup_info.GetLookupName().GetStringRef(); FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); @@ -43,7 +44,7 @@ bool DWARFIndex::ProcessFunctionDIE( if (!lookup_info.NameMatchesLookupInfo(name_to_match_against, lookup_info.GetLanguageType())) - return true; + return IterationAction::Continue; } // Exit early if we're searching exclusively for methods or selectors and @@ -51,12 +52,12 @@ bool DWARFIndex::ProcessFunctionDIE( uint32_t looking_for_nonmethods = name_type_mask & ~(eFunctionNameTypeMethod | eFunctionNameTypeSelector); if (!looking_for_nonmethods && parent_decl_ctx.IsValid()) - return true; + return IterationAction::Continue; // Otherwise, we need to also check that the context matches. If it does not // match, we do nothing. if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, die)) - return true; + return IterationAction::Continue; // In case of a full match, we just insert everything we find. if (name_type_mask & eFunctionNameTypeFull && die.GetMangledName() == name) @@ -79,7 +80,7 @@ bool DWARFIndex::ProcessFunctionDIE( return callback(die); } - return true; + return IterationAction::Continue; } DWARFIndex::DIERefCallbackImpl::DIERefCallbackImpl( diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h index 15d8503..3578824 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFIndex.h @@ -16,6 +16,7 @@ #include "lldb/Core/Module.h" #include "lldb/Target/Statistics.h" +#include "lldb/lldb-private-enumerations.h" namespace lldb_private::plugin { namespace dwarf { @@ -82,10 +83,10 @@ public: virtual void GetFunctions(const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0; virtual void GetFunctions(const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) = 0; + llvm::function_ref<IterationAction(DWARFDIE die)> callback) = 0; virtual void Dump(Stream &s) = 0; @@ -101,9 +102,10 @@ protected: /// the function given by "die" matches search criteria given by /// "parent_decl_ctx" and "name_type_mask", it calls the callback with the /// given die. - bool ProcessFunctionDIE(const Module::LookupInfo &lookup_info, DWARFDIE die, - const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback); + IterationAction ProcessFunctionDIE( + const Module::LookupInfo &lookup_info, DWARFDIE die, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback); class DIERefCallbackImpl { public: @@ -140,6 +142,25 @@ protected: bool ProcessNamespaceDieMatchParents( const CompilerDeclContext &parent_decl_ctx, DWARFDIE die, llvm::function_ref<bool(DWARFDIE die)> callback); + + /// Helper to convert callbacks that return an \c IterationAction + /// to a callback that returns a \c bool, where \c true indicates + /// we should continue iterating. This will be used to incrementally + /// migrate the callbacks to return an \c IterationAction. + /// + /// FIXME: remove once all callbacks in the DWARFIndex APIs return + /// IterationAction. + struct IterationActionAdaptor { + IterationActionAdaptor( + llvm::function_ref<IterationAction(DWARFDIE die)> callback) + : m_callback_ref(callback) {} + + bool operator()(DWARFDIE die) { + return m_callback_ref(std::move(die)) == IterationAction::Continue; + } + + llvm::function_ref<IterationAction(DWARFDIE die)> m_callback_ref; + }; }; } // namespace dwarf } // namespace lldb_private::plugin diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp index ff1a76b..3ae9fcc 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Module.h" #include "lldb/Utility/RegularExpression.h" #include "lldb/Utility/Stream.h" +#include "lldb/lldb-private-enumerations.h" #include "llvm/ADT/Sequence.h" #include <optional> @@ -607,7 +608,7 @@ void DebugNamesDWARFIndex::GetNamespacesWithParents( void DebugNamesDWARFIndex::GetFunctions( const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { ConstString name = lookup_info.GetLookupName(); std::set<DWARFDebugInfoEntry *> seen; for (const DebugNames::Entry &entry : @@ -617,12 +618,12 @@ void DebugNamesDWARFIndex::GetFunctions( continue; if (DWARFDIE die = GetDIE(entry)) { - if (!ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, - [&](DWARFDIE die) { - if (!seen.insert(die.GetDIE()).second) - return true; - return callback(die); - })) + if (ProcessFunctionDIE(lookup_info, die, parent_decl_ctx, + [&](DWARFDIE die) { + if (!seen.insert(die.GetDIE()).second) + return IterationAction::Continue; + return callback(die); + }) == IterationAction::Stop) return; } } @@ -632,7 +633,7 @@ void DebugNamesDWARFIndex::GetFunctions( void DebugNamesDWARFIndex::GetFunctions( const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { for (const DebugNames::NameIndex &ni: *m_debug_names_up) { for (DebugNames::NameTableEntry nte: ni) { if (!regex.Execute(nte.getString())) @@ -645,7 +646,7 @@ void DebugNamesDWARFIndex::GetFunctions( if (tag != DW_TAG_subprogram && tag != DW_TAG_inlined_subroutine) continue; - if (!ProcessEntry(*entry_or, callback)) + if (!ProcessEntry(*entry_or, IterationActionAdaptor(callback))) return; } MaybeLogLookupError(entry_or.takeError(), ni, nte.getString()); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h index ab6cde1..2105919 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/DebugNamesDWARFIndex.h @@ -58,12 +58,13 @@ public: void GetNamespacesWithParents( ConstString name, const CompilerDeclContext &parent_decl_ctx, llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const Module::LookupInfo &lookup_info, - SymbolFileDWARF &dwarf, - const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; + void GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; void Dump(Stream &s) override; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp index c858ce2..f96ac7e 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.cpp @@ -21,6 +21,7 @@ #include "lldb/Utility/DataExtractor.h" #include "lldb/Utility/Stream.h" #include "lldb/Utility/Timer.h" +#include "lldb/lldb-private-enumerations.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ThreadPool.h" #include <atomic> @@ -471,60 +472,62 @@ void ManualDWARFIndex::GetNamespaces( void ManualDWARFIndex::GetFunctions( const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { Index(); ConstString name = lookup_info.GetLookupName(); FunctionNameType name_type_mask = lookup_info.GetNameTypeMask(); if (name_type_mask & eFunctionNameTypeFull) { if (!m_set.function_fullnames.Find( - name, DIERefCallback( - [&](DWARFDIE die) { - if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, - die)) - return true; - return callback(die); - }, - name.GetStringRef()))) + name, DIERefCallback(IterationActionAdaptor([&](DWARFDIE die) { + if (!SymbolFileDWARF::DIEInDeclContext( + parent_decl_ctx, die)) + return IterationAction::Continue; + return callback(die); + }), + name.GetStringRef()))) return; } if (name_type_mask & eFunctionNameTypeBase) { if (!m_set.function_basenames.Find( - name, DIERefCallback( - [&](DWARFDIE die) { - if (!SymbolFileDWARF::DIEInDeclContext(parent_decl_ctx, - die)) - return true; - return callback(die); - }, - name.GetStringRef()))) + name, DIERefCallback(IterationActionAdaptor([&](DWARFDIE die) { + if (!SymbolFileDWARF::DIEInDeclContext( + parent_decl_ctx, die)) + return IterationAction::Continue; + return callback(die); + }), + name.GetStringRef()))) return; } if (name_type_mask & eFunctionNameTypeMethod && !parent_decl_ctx.IsValid()) { if (!m_set.function_methods.Find( - name, DIERefCallback(callback, name.GetStringRef()))) + name, DIERefCallback(IterationActionAdaptor(callback), + name.GetStringRef()))) return; } if (name_type_mask & eFunctionNameTypeSelector && !parent_decl_ctx.IsValid()) { if (!m_set.function_selectors.Find( - name, DIERefCallback(callback, name.GetStringRef()))) + name, DIERefCallback(IterationActionAdaptor(callback), + name.GetStringRef()))) return; } } void ManualDWARFIndex::GetFunctions( const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) { + llvm::function_ref<IterationAction(DWARFDIE die)> callback) { Index(); - if (!m_set.function_basenames.Find(regex, - DIERefCallback(callback, regex.GetText()))) + if (!m_set.function_basenames.Find( + regex, + DIERefCallback(IterationActionAdaptor(callback), regex.GetText()))) return; - if (!m_set.function_fullnames.Find(regex, - DIERefCallback(callback, regex.GetText()))) + if (!m_set.function_fullnames.Find( + regex, + DIERefCallback(IterationActionAdaptor(callback), regex.GetText()))) return; } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h index 04627b0..5685ba4 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/ManualDWARFIndex.h @@ -50,12 +50,13 @@ public: llvm::function_ref<bool(DWARFDIE die)> callback) override; void GetNamespaces(ConstString name, llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const Module::LookupInfo &lookup_info, - SymbolFileDWARF &dwarf, - const CompilerDeclContext &parent_decl_ctx, - llvm::function_ref<bool(DWARFDIE die)> callback) override; - void GetFunctions(const RegularExpression ®ex, - llvm::function_ref<bool(DWARFDIE die)> callback) override; + void GetFunctions( + const Module::LookupInfo &lookup_info, SymbolFileDWARF &dwarf, + const CompilerDeclContext &parent_decl_ctx, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; + void GetFunctions( + const RegularExpression ®ex, + llvm::function_ref<IterationAction(DWARFDIE die)> callback) override; void Dump(Stream &s) override; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 41ab8d1..a3ba061 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -74,6 +74,7 @@ #include "ManualDWARFIndex.h" #include "SymbolFileDWARFDebugMap.h" #include "SymbolFileDWARFDwo.h" +#include "lldb/lldb-private-enumerations.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugAbbrev.h" @@ -2475,6 +2476,55 @@ bool SymbolFileDWARF::ResolveFunction(const DWARFDIE &orig_die, return false; } +llvm::Expected<SymbolContext> +SymbolFileDWARF::ResolveFunctionCallLabel(const FunctionCallLabel &label) { + std::lock_guard<std::recursive_mutex> guard(GetModuleMutex()); + + DWARFDIE die = GetDIE(label.symbol_id); + if (!die.IsValid()) + return llvm::createStringError( + llvm::formatv("invalid DIE ID in {0}", label)); + + // Label was created using a declaration DIE. Need to fetch the definition + // to resolve the function call. + if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) { + Module::LookupInfo info(ConstString(label.lookup_name), + lldb::eFunctionNameTypeFull, + lldb::eLanguageTypeUnknown); + + m_index->GetFunctions(info, *this, {}, [&](DWARFDIE entry) { + if (entry.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return IterationAction::Continue; + + // We don't check whether the specification DIE for this function + // corresponds to the declaration DIE because the declaration might be in + // a type-unit but the definition in the compile-unit (and it's + // specifcation would point to the declaration in the compile-unit). We + // rely on the mangled name within the module to be enough to find us the + // unique definition. + die = entry; + return IterationAction::Stop; + }); + + if (die.GetAttributeValueAsUnsigned(llvm::dwarf::DW_AT_declaration, 0)) + return llvm::createStringError( + llvm::formatv("failed to find definition DIE for {0}", label)); + } + + SymbolContextList sc_list; + if (!ResolveFunction(die, /*include_inlines=*/false, sc_list)) + return llvm::createStringError( + llvm::formatv("failed to resolve function for {0}", label)); + + if (sc_list.IsEmpty()) + return llvm::createStringError( + llvm::formatv("failed to find function for {0}", label)); + + assert(sc_list.GetSize() == 1); + + return sc_list[0]; +} + bool SymbolFileDWARF::DIEInDeclContext(const CompilerDeclContext &decl_ctx, const DWARFDIE &die, bool only_root_namespaces) { @@ -2539,7 +2589,7 @@ void SymbolFileDWARF::FindFunctions(const Module::LookupInfo &lookup_info, m_index->GetFunctions(lookup_info, *this, parent_decl_ctx, [&](DWARFDIE die) { if (resolved_dies.insert(die.GetDIE()).second) ResolveFunction(die, include_inlines, sc_list); - return true; + return IterationAction::Continue; }); // With -gsimple-template-names, a templated type's DW_AT_name will not // contain the template parameters. Try again stripping '<' and anything @@ -2556,7 +2606,7 @@ void SymbolFileDWARF::FindFunctions(const Module::LookupInfo &lookup_info, [&](DWARFDIE die) { if (resolved_dies.insert(die.GetDIE()).second) ResolveFunction(die, include_inlines, sc_list); - return true; + return IterationAction::Continue; }); } } @@ -2592,7 +2642,7 @@ void SymbolFileDWARF::FindFunctions(const RegularExpression ®ex, m_index->GetFunctions(regex, [&](DWARFDIE die) { if (resolved_dies.insert(die.GetDIE()).second) ResolveFunction(die, include_inlines, sc_list); - return true; + return IterationAction::Continue; }); } diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h index 56d8ccb..3ec538d 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.h @@ -436,6 +436,9 @@ protected: DIEArray MergeBlockAbstractParameters(const DWARFDIE &block_die, DIEArray &&variable_dies); + llvm::Expected<SymbolContext> + ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + // Given a die_offset, figure out the symbol context representing that die. bool ResolveFunction(const DWARFDIE &die, bool include_inlines, SymbolContextList &sc_list); diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index dd94f0b..9d7452a 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -1602,3 +1602,14 @@ void SymbolFileDWARFDebugMap::GetCompileOptions( return IterationAction::Continue; }); } + +llvm::Expected<SymbolContext> SymbolFileDWARFDebugMap::ResolveFunctionCallLabel( + const FunctionCallLabel &label) { + const uint64_t oso_idx = GetOSOIndexFromUserID(label.symbol_id); + SymbolFileDWARF *oso_dwarf = GetSymbolFileByOSOIndex(oso_idx); + if (!oso_dwarf) + return llvm::createStringError(llvm::formatv( + "couldn't find symbol file for {0} in debug-map.", label)); + + return oso_dwarf->ResolveFunctionCallLabel(label); +} diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h index f074b17..e1f1df23 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.h @@ -144,6 +144,9 @@ public: void GetCompileOptions(std::unordered_map<lldb::CompUnitSP, Args> &args) override; + llvm::Expected<SymbolContext> + ResolveFunctionCallLabel(const FunctionCallLabel &label) override; + protected: enum { kHaveInitializedOSOs = (1 << 0), kNumFlags }; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp index 702ec5e..8137622 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/PdbAstBuilder.cpp @@ -88,7 +88,7 @@ struct CreateMethodDecl : public TypeVisitorCallbacks { MethodOptions::CompilerGenerated; function_decl = m_clang.AddMethodToCXXRecordType( parent_ty, proc_name, - /*mangled_name=*/nullptr, func_ct, /*access=*/access_type, + /*asm_label=*/{}, func_ct, /*access=*/access_type, /*is_virtual=*/is_virtual, /*is_static=*/is_static, /*is_inline=*/false, /*is_explicit=*/false, /*is_attr_used=*/false, /*is_artificial=*/is_artificial); @@ -903,7 +903,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, if (!function_decl) { function_decl = m_clang.AddMethodToCXXRecordType( parent_opaque_ty, func_name, - /*mangled_name=*/nullptr, func_ct, + /*asm_label=*/{}, func_ct, /*access=*/lldb::AccessType::eAccessPublic, /*is_virtual=*/false, /*is_static=*/false, /*is_inline=*/false, /*is_explicit=*/false, @@ -913,7 +913,7 @@ PdbAstBuilder::CreateFunctionDecl(PdbCompilandSymId func_id, } else { function_decl = m_clang.CreateFunctionDeclaration( parent, OptionalClangModuleID(), func_name, func_ct, func_storage, - is_inline); + is_inline, /*asm_label=*/{}); CreateFunctionParameters(func_id, *function_decl, param_count); } return function_decl; diff --git a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp index 807ee5b..1c575e9 100644 --- a/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp +++ b/lldb/source/Plugins/SymbolFile/NativePDB/UdtRecordCompleter.cpp @@ -111,9 +111,8 @@ void UdtRecordCompleter::AddMethod(llvm::StringRef name, TypeIndex type_idx, bool is_artificial = (options & MethodOptions::CompilerGenerated) == MethodOptions::CompilerGenerated; m_ast_builder.clang().AddMethodToCXXRecordType( - derived_opaque_ty, name.data(), nullptr, method_ct, - access_type, attrs.isVirtual(), attrs.isStatic(), false, false, false, - is_artificial); + derived_opaque_ty, name.data(), /*asm_label=*/{}, method_ct, access_type, + attrs.isVirtual(), attrs.isStatic(), false, false, false, is_artificial); m_cxx_record_map[derived_opaque_ty].insert({name, method_ct}); } diff --git a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp index 0090d8f..8b8eac6e 100644 --- a/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp +++ b/lldb/source/Plugins/SymbolFile/PDB/PDBASTParser.cpp @@ -954,7 +954,8 @@ PDBASTParser::GetDeclForSymbol(const llvm::pdb::PDBSymbol &symbol) { auto decl = m_ast.CreateFunctionDeclaration( decl_context, OptionalClangModuleID(), name, - type->GetForwardCompilerType(), storage, func->hasInlineAttribute()); + type->GetForwardCompilerType(), storage, func->hasInlineAttribute(), + /*asm_label=*/{}); std::vector<clang::ParmVarDecl *> params; if (std::unique_ptr<PDBSymbolTypeFunctionSig> sig = func->getSignature()) { @@ -1446,7 +1447,7 @@ PDBASTParser::AddRecordMethod(lldb_private::SymbolFile &symbol_file, // TODO: get mangled name for the method. return m_ast.AddMethodToCXXRecordType( record_type.GetOpaqueQualType(), name.c_str(), - /*mangled_name*/ nullptr, method_comp_type, access, method.isVirtual(), + /*asm_label=*/{}, method_comp_type, access, method.isVirtual(), method.isStatic(), method.hasInlineAttribute(), /*is_explicit*/ false, // FIXME: Need this field in CodeView. /*is_attr_used*/ false, diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp index 256952dc..9301f92 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.cpp @@ -60,6 +60,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" +#include "lldb/Expression/Expression.h" #include "lldb/Host/StreamFile.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" @@ -665,10 +666,9 @@ void TypeSystemClang::CreateASTContext() { m_file_manager_up = std::make_unique<clang::FileManager>( file_system_options, FileSystem::Instance().GetVirtualFileSystem()); - llvm::IntrusiveRefCntPtr<DiagnosticIDs> diag_id_sp(new DiagnosticIDs()); m_diagnostic_options_up = std::make_unique<DiagnosticOptions>(); - m_diagnostics_engine_up = - std::make_unique<DiagnosticsEngine>(diag_id_sp, *m_diagnostic_options_up); + m_diagnostics_engine_up = std::make_unique<DiagnosticsEngine>( + DiagnosticIDs::create(), *m_diagnostic_options_up); m_source_manager_up = std::make_unique<clang::SourceManager>( *m_diagnostics_engine_up, *m_file_manager_up); @@ -796,6 +796,8 @@ TypeSystemClang::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, return GetType(ast.LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.HalfTy)) return GetType(ast.HalfTy); + if (QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); break; case eEncodingVector: @@ -957,6 +959,13 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( if (type_name == "long double" && QualTypeMatchesBitSize(bit_size, ast, ast.LongDoubleTy)) return GetType(ast.LongDoubleTy); + // As Rust currently uses `TypeSystemClang`, match `f128` here as well so it + // doesn't get misinterpreted as `long double` on targets where they are + // the same size but different formats. + if ((type_name == "__float128" || type_name == "_Float128" || + type_name == "f128") && + QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); // Fall back to not requiring a name match if (QualTypeMatchesBitSize(bit_size, ast, ast.FloatTy)) return GetType(ast.FloatTy); @@ -966,6 +975,8 @@ CompilerType TypeSystemClang::GetBuiltinTypeForDWARFEncodingAndBitSize( return GetType(ast.LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast.HalfTy)) return GetType(ast.HalfTy); + if (QualTypeMatchesBitSize(bit_size, ast, ast.Float128Ty)) + return GetType(ast.Float128Ty); break; case DW_ATE_signed: @@ -2055,6 +2066,8 @@ TypeSystemClang::GetOpaqueCompilerType(clang::ASTContext *ast, return ast->DoubleTy.getAsOpaquePtr(); case eBasicTypeLongDouble: return ast->LongDoubleTy.getAsOpaquePtr(); + case eBasicTypeFloat128: + return ast->Float128Ty.getAsOpaquePtr(); case eBasicTypeFloatComplex: return ast->getComplexType(ast->FloatTy).getAsOpaquePtr(); case eBasicTypeDoubleComplex: @@ -2137,7 +2150,7 @@ std::string TypeSystemClang::GetTypeNameForDecl(const NamedDecl *named_decl, FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_clang_type, - clang::StorageClass storage, bool is_inline) { + clang::StorageClass storage, bool is_inline, llvm::StringRef asm_label) { FunctionDecl *func_decl = nullptr; ASTContext &ast = getASTContext(); if (!decl_ctx) @@ -2158,6 +2171,21 @@ FunctionDecl *TypeSystemClang::CreateFunctionDeclaration( func_decl->setConstexprKind(isConstexprSpecified ? ConstexprSpecKind::Constexpr : ConstexprSpecKind::Unspecified); + + // Attach an asm(<mangled_name>) label to the FunctionDecl. + // This ensures that clang::CodeGen emits function calls + // using symbols that are mangled according to the DW_AT_linkage_name. + // If we didn't do this, the external symbols wouldn't exactly + // match the mangled name LLDB knows about and the IRExecutionUnit + // would have to fall back to searching object files for + // approximately matching function names. The motivating + // example is generating calls to ABI-tagged template functions. + // This is done separately for member functions in + // AddMethodToCXXRecordType. + if (!asm_label.empty()) + func_decl->addAttr(clang::AsmLabelAttr::CreateImplicit(ast, asm_label, + /*literal=*/true)); + SetOwningModule(func_decl, owning_module); decl_ctx->addDecl(func_decl); @@ -4728,19 +4756,24 @@ CompilerType TypeSystemClang::CreateGenericFunctionPrototype() { // Exploring the type const llvm::fltSemantics & -TypeSystemClang::GetFloatTypeSemantics(size_t byte_size) { +TypeSystemClang::GetFloatTypeSemantics(size_t byte_size, lldb::Format format) { clang::ASTContext &ast = getASTContext(); const size_t bit_size = byte_size * 8; if (bit_size == ast.getTypeSize(ast.FloatTy)) return ast.getFloatTypeSemantics(ast.FloatTy); else if (bit_size == ast.getTypeSize(ast.DoubleTy)) return ast.getFloatTypeSemantics(ast.DoubleTy); + else if (format == eFormatFloat128 && + bit_size == ast.getTypeSize(ast.Float128Ty)) + return ast.getFloatTypeSemantics(ast.Float128Ty); else if (bit_size == ast.getTypeSize(ast.LongDoubleTy) || bit_size == llvm::APFloat::semanticsSizeInBits( ast.getFloatTypeSemantics(ast.LongDoubleTy))) return ast.getFloatTypeSemantics(ast.LongDoubleTy); else if (bit_size == ast.getTypeSize(ast.HalfTy)) return ast.getFloatTypeSemantics(ast.HalfTy); + else if (bit_size == ast.getTypeSize(ast.Float128Ty)) + return ast.getFloatTypeSemantics(ast.Float128Ty); return llvm::APFloatBase::Bogus(); } @@ -5218,6 +5251,8 @@ lldb::Format TypeSystemClang::GetFormat(lldb::opaque_compiler_type_t type) { case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eFormatFloat; + case clang::BuiltinType::Float128: + return lldb::eFormatFloat128; default: return lldb::eFormatHex; } @@ -5515,6 +5550,8 @@ TypeSystemClang::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { return eBasicTypeDouble; case clang::BuiltinType::LongDouble: return eBasicTypeLongDouble; + case clang::BuiltinType::Float128: + return eBasicTypeFloat128; case clang::BuiltinType::NullPtr: return eBasicTypeNullPtr; @@ -6076,6 +6113,7 @@ uint32_t TypeSystemClang::GetNumPointeeChildren(clang::QualType type) { case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: + case clang::BuiltinType::Float128: case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::ObjCId: @@ -7651,7 +7689,7 @@ TypeSystemClang::CreateParameterDeclarations( clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_clang_type, + llvm::StringRef asm_label, const CompilerType &method_clang_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial) { if (!type || !method_clang_type.IsValid() || name.empty()) @@ -7784,10 +7822,9 @@ clang::CXXMethodDecl *TypeSystemClang::AddMethodToCXXRecordType( if (is_attr_used) cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(getASTContext())); - if (mangled_name != nullptr) { + if (!asm_label.empty()) cxx_method_decl->addAttr(clang::AsmLabelAttr::CreateImplicit( - getASTContext(), mangled_name, /*literal=*/false)); - } + getASTContext(), asm_label, /*literal=*/true)); // Parameters on member function declarations in DWARF generally don't // have names, so we omit them when creating the ParmVarDecls. @@ -8720,6 +8757,7 @@ bool TypeSystemClang::DumpTypeValue( case eFormatHex: case eFormatHexUppercase: case eFormatFloat: + case eFormatFloat128: case eFormatOctal: case eFormatOSType: case eFormatUnsigned: @@ -9030,6 +9068,21 @@ ConstString TypeSystemClang::DeclGetName(void *opaque_decl) { return ConstString(); } +static ConstString +ExtractMangledNameFromFunctionCallLabel(llvm::StringRef label) { + auto label_or_err = FunctionCallLabel::fromString(label); + if (!label_or_err) { + llvm::consumeError(label_or_err.takeError()); + return {}; + } + + llvm::StringRef mangled = label_or_err->lookup_name; + if (Mangled::IsMangledName(mangled)) + return ConstString(mangled); + + return {}; +} + ConstString TypeSystemClang::DeclGetMangledName(void *opaque_decl) { clang::NamedDecl *nd = llvm::dyn_cast_or_null<clang::NamedDecl>( static_cast<clang::Decl *>(opaque_decl)); @@ -9041,6 +9094,13 @@ ConstString TypeSystemClang::DeclGetMangledName(void *opaque_decl) { if (!mc || !mc->shouldMangleCXXName(nd)) return {}; + // We have an LLDB FunctionCallLabel instead of an ordinary mangled name. + // Extract the mangled name out of this label. + if (const auto *label = nd->getAttr<AsmLabelAttr>()) + if (ConstString mangled = + ExtractMangledNameFromFunctionCallLabel(label->getLabel())) + return mangled; + llvm::SmallVector<char, 1024> buf; llvm::raw_svector_ostream llvm_ostrm(buf); if (llvm::isa<clang::CXXConstructorDecl>(nd)) { diff --git a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h index 63dee9d..5431d12 100644 --- a/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h +++ b/lldb/source/Plugins/TypeSystem/Clang/TypeSystemClang.h @@ -477,7 +477,7 @@ public: clang::FunctionDecl *CreateFunctionDeclaration( clang::DeclContext *decl_ctx, OptionalClangModuleID owning_module, llvm::StringRef name, const CompilerType &function_Type, - clang::StorageClass storage, bool is_inline); + clang::StorageClass storage, bool is_inline, llvm::StringRef asm_label); CompilerType CreateFunctionType(const CompilerType &result_type, @@ -823,7 +823,8 @@ public: // Exploring the type - const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size) override; + const llvm::fltSemantics &GetFloatTypeSemantics(size_t byte_size, + lldb::Format format) override; llvm::Expected<uint64_t> GetByteSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { @@ -1001,7 +1002,7 @@ public: clang::CXXMethodDecl *AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, llvm::StringRef name, - const char *mangled_name, const CompilerType &method_type, + llvm::StringRef asm_label, const CompilerType &method_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial); diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 2aa02fd..ff9e5fc 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -166,6 +166,9 @@ ProcessProperties::ProcessProperties(lldb_private::Process *process) m_collection_sp->SetValueChangedCallback( ePropertyPythonOSPluginPath, [this] { m_process->LoadOperatingSystemPlugin(true); }); + m_collection_sp->SetValueChangedCallback( + ePropertyDisableLangRuntimeUnwindPlans, + [this] { DisableLanguageRuntimeUnwindPlansCallback(); }); } m_experimental_properties_up = @@ -280,6 +283,15 @@ void ProcessProperties::SetDisableLangRuntimeUnwindPlans(bool disable) { m_process->Flush(); } +void ProcessProperties::DisableLanguageRuntimeUnwindPlansCallback() { + if (!m_process) + return; + for (auto thread_sp : m_process->Threads()) { + thread_sp->ClearStackFrames(); + thread_sp->DiscardThreadPlans(/*force*/ true); + } +} + bool ProcessProperties::GetDetachKeepsStopped() const { const uint32_t idx = ePropertyDetachKeepsStopped; return GetPropertyAtIndexAs<bool>( diff --git a/lldb/source/ValueObject/ValueObject.cpp b/lldb/source/ValueObject/ValueObject.cpp index 84ad130..3878442 100644 --- a/lldb/source/ValueObject/ValueObject.cpp +++ b/lldb/source/ValueObject/ValueObject.cpp @@ -1466,8 +1466,9 @@ bool ValueObject::DumpPrintableRepresentation( (custom_format == eFormatComplexFloat) || (custom_format == eFormatDecimal) || (custom_format == eFormatHex) || (custom_format == eFormatHexUppercase) || - (custom_format == eFormatFloat) || (custom_format == eFormatOctal) || - (custom_format == eFormatOSType) || + (custom_format == eFormatFloat) || + (custom_format == eFormatFloat128) || + (custom_format == eFormatOctal) || (custom_format == eFormatOSType) || (custom_format == eFormatUnicode16) || (custom_format == eFormatUnicode32) || (custom_format == eFormatUnsigned) || diff --git a/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py b/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py index 8179d52..67dfbea 100644 --- a/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py +++ b/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py @@ -50,11 +50,11 @@ class TestStepOverWatchpoint(TestBase): lldb.eStopReasonWatchpoint, STOPPED_DUE_TO_WATCHPOINT, ) - self.assertEqual(thread.GetStopDescription(20), "watchpoint 1") + self.assertEqual(thread.stop_description, "watchpoint 1") process.Continue() self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) - self.assertEqual(thread.GetStopDescription(20), "step over") + self.assertEqual(thread.stop_description, "step over") self.step_inst_for_watchpoint(1) @@ -89,11 +89,11 @@ class TestStepOverWatchpoint(TestBase): lldb.eStopReasonWatchpoint, STOPPED_DUE_TO_WATCHPOINT, ) - self.assertEqual(thread.GetStopDescription(20), "watchpoint 1") + self.assertEqual(thread.stop_description, "watchpoint 1") process.Continue() self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED) - self.assertEqual(thread.GetStopDescription(20), "step over") + self.assertEqual(thread.stop_description, "step over") self.step_inst_for_watchpoint(1) @@ -106,7 +106,7 @@ class TestStepOverWatchpoint(TestBase): if stop_reason == lldb.eStopReasonWatchpoint: self.assertFalse(watchpoint_hit, "Watchpoint already hit.") expected_stop_desc = "watchpoint %d" % wp_id - actual_stop_desc = self.thread().GetStopDescription(20) + actual_stop_desc = self.thread().stop_description self.assertEqual( actual_stop_desc, expected_stop_desc, "Watchpoint ID didn't match." ) diff --git a/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py b/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py index ff834b5..a0251d4 100644 --- a/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py +++ b/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py @@ -35,7 +35,7 @@ class TestWatchpointCount(TestBase): self.assertStopReason( stop_reason, lldb.eStopReasonWatchpoint, "watchpoint for x1 not hit" ) - stop_reason_descr = thread.GetStopDescription(256) + stop_reason_descr = thread.stop_description self.assertEqual(stop_reason_descr, "watchpoint 1") process.Continue() @@ -43,5 +43,5 @@ class TestWatchpointCount(TestBase): self.assertStopReason( stop_reason, lldb.eStopReasonWatchpoint, "watchpoint for x2 not hit" ) - stop_reason_descr = thread.GetStopDescription(256) + stop_reason_descr = thread.stop_description self.assertEqual(stop_reason_descr, "watchpoint 2") diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py b/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py index 12b464d..67c5d7d 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestGDBRemoteClient.py @@ -594,7 +594,7 @@ class TestGDBRemoteClient(GDBRemoteTestBase): process = self.connect(target) self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal) - self.assertEqual(process.threads[0].GetStopDescription(100), "signal SIGBUS") + self.assertEqual(process.threads[0].stop_description, "signal SIGBUS") def test_signal_lldb_old(self): class MyResponder(MockGDBServerResponder): @@ -620,7 +620,7 @@ class TestGDBRemoteClient(GDBRemoteTestBase): process = self.connect(target) self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal) - self.assertEqual(process.threads[0].GetStopDescription(100), "signal SIGUSR1") + self.assertEqual(process.threads[0].stop_description, "signal SIGUSR1") def test_signal_lldb(self): class MyResponder(MockGDBServerResponder): @@ -643,7 +643,7 @@ class TestGDBRemoteClient(GDBRemoteTestBase): process = self.connect(target) self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal) - self.assertEqual(process.threads[0].GetStopDescription(100), "signal SIGUSR1") + self.assertEqual(process.threads[0].stop_description, "signal SIGUSR1") def do_siginfo_test(self, platform, target_yaml, raw_data, expected): class MyResponder(MockGDBServerResponder): diff --git a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py index 445f422..73c81ef 100644 --- a/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py +++ b/lldb/test/API/functionalities/gdb_remote_client/TestWasm.py @@ -1,12 +1,15 @@ import lldb +import os import binascii from lldbsuite.test.lldbtest import * from lldbsuite.test.decorators import * from lldbsuite.test.gdbclientutils import * from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase -LLDB_INVALID_ADDRESS = lldb.LLDB_INVALID_ADDRESS -load_address = 0x400000000 +MODULE_ID = 4 +LOAD_ADDRESS = MODULE_ID << 32 +WASM_LOCAL_ADDR = 0x103E0 + def format_register_value(val): """ @@ -23,12 +26,59 @@ def format_register_value(val): return result +class WasmStackFrame: + def __init__(self, address): + self._address = address + + def __str__(self): + return format_register_value(LOAD_ADDRESS | self._address) + + +class WasmCallStack: + def __init__(self, wasm_stack_frames): + self._wasm_stack_frames = wasm_stack_frames + + def __str__(self): + result = "" + for frame in self._wasm_stack_frames: + result += str(frame) + return result + + +class FakeMemory: + def __init__(self, start_addr, end_addr): + self._base_addr = start_addr + self._memory = bytearray(end_addr - start_addr) + self._memoryview = memoryview(self._memory) + + def store_bytes(self, addr, bytes_obj): + assert addr > self._base_addr + assert addr < self._base_addr + len(self._memoryview) + offset = addr - self._base_addr + chunk = self._memoryview[offset : offset + len(bytes_obj)] + for i in range(len(bytes_obj)): + chunk[i] = bytes_obj[i] + + def get_bytes(self, addr, length): + assert addr > self._base_addr + assert addr < self._base_addr + len(self._memoryview) + + offset = addr - self._base_addr + return self._memoryview[offset : offset + length] + + def contains(self, addr): + return addr - self._base_addr < len(self._memoryview) + + class MyResponder(MockGDBServerResponder): - current_pc = load_address + 0x0A + current_pc = LOAD_ADDRESS | 0x01AD - def __init__(self, obj_path, module_name=""): + def __init__(self, obj_path, module_name="", wasm_call_stacks=[], memory=None): self._obj_path = obj_path self._module_name = module_name or obj_path + self._wasm_call_stacks = wasm_call_stacks + self._call_stack_request_count = 0 + self._memory = memory MockGDBServerResponder.__init__(self) def respond(self, packet): @@ -36,6 +86,8 @@ class MyResponder(MockGDBServerResponder): return self.qRegisterInfo(packet[13:]) if packet.startswith("qWasmCallStack"): return self.qWasmCallStack() + if packet.startswith("qWasmLocal"): + return self.qWasmLocal(packet) return MockGDBServerResponder.respond(self, packet) def qSupported(self, client_supported): @@ -71,28 +123,61 @@ class MyResponder(MockGDBServerResponder): if obj == "libraries": xml = ( '<library-list><library name="%s"><section address="%d"/></library></library-list>' - % (self._module_name, load_address) + % (self._module_name, LOAD_ADDRESS) ) return xml, False else: return None, False def readMemory(self, addr, length): - if addr < load_address: + if self._memory and self._memory.contains(addr): + chunk = self._memory.get_bytes(addr, length) + return chunk.hex() + if addr < LOAD_ADDRESS: return "E02" result = "" with open(self._obj_path, mode="rb") as file: file_content = bytearray(file.read()) - addr_from = addr - load_address + if addr >= LOAD_ADDRESS + len(file_content): + return "E03" + addr_from = addr - LOAD_ADDRESS addr_to = addr_from + min(length, len(file_content) - addr_from) for i in range(addr_from, addr_to): result += format(file_content[i], "02x") file.close() return result + def setBreakpoint(self, packet): + bp_data = packet[1:].split(",") + self._bp_address = bp_data[1] + return "OK" + + def qfThreadInfo(self): + return "m1" + + def cont(self): + # Continue execution. Simulates running the Wasm engine until a breakpoint is hit. + return ( + "T05thread-pcs:" + + format(int(self._bp_address, 16) & 0x3FFFFFFFFFFFFFFF, "x") + + ";thread:1" + ) + def qWasmCallStack(self): - # Return two 64-bit addresses: 0x40000000000001B3, 0x40000000000001FE - return "b301000000000040fe01000000000040" + if len(self._wasm_call_stacks) == 0: + return "" + result = str(self._wasm_call_stacks[self._call_stack_request_count]) + self._call_stack_request_count += 1 + return result + + def qWasmLocal(self, packet): + # Format: qWasmLocal:frame_index;index + data = packet.split(":") + data = data[1].split(";") + frame_index, local_index = data + if frame_index == "0" and local_index == "2": + return format_register_value(WASM_LOCAL_ADDR) + return "E03" class TestWasm(GDBRemoteTestBase): @@ -124,35 +209,35 @@ class TestWasm(GDBRemoteTestBase): code_section = module.GetSectionAtIndex(0) self.assertEqual("code", code_section.GetName()) self.assertEqual( - load_address | code_section.GetFileOffset(), + LOAD_ADDRESS | code_section.GetFileOffset(), code_section.GetLoadAddress(target), ) debug_info_section = module.GetSectionAtIndex(1) self.assertEqual(".debug_info", debug_info_section.GetName()) self.assertEqual( - load_address | debug_info_section.GetFileOffset(), + LOAD_ADDRESS | debug_info_section.GetFileOffset(), debug_info_section.GetLoadAddress(target), ) debug_abbrev_section = module.GetSectionAtIndex(2) self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName()) self.assertEqual( - load_address | debug_abbrev_section.GetFileOffset(), + LOAD_ADDRESS | debug_abbrev_section.GetFileOffset(), debug_abbrev_section.GetLoadAddress(target), ) debug_line_section = module.GetSectionAtIndex(3) self.assertEqual(".debug_line", debug_line_section.GetName()) self.assertEqual( - load_address | debug_line_section.GetFileOffset(), + LOAD_ADDRESS | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target), ) debug_str_section = module.GetSectionAtIndex(4) self.assertEqual(".debug_str", debug_str_section.GetName()) self.assertEqual( - load_address | debug_line_section.GetFileOffset(), + LOAD_ADDRESS | debug_line_section.GetFileOffset(), debug_line_section.GetLoadAddress(target), ) @@ -194,97 +279,103 @@ class TestWasm(GDBRemoteTestBase): code_section = module.GetSectionAtIndex(0) self.assertEqual("code", code_section.GetName()) self.assertEqual( - load_address | code_section.GetFileOffset(), + LOAD_ADDRESS | code_section.GetFileOffset(), code_section.GetLoadAddress(target), ) debug_info_section = module.GetSectionAtIndex(1) self.assertEqual(".debug_info", debug_info_section.GetName()) self.assertEqual( - LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target) + lldb.LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target) ) debug_abbrev_section = module.GetSectionAtIndex(2) self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName()) self.assertEqual( - LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target) + lldb.LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target) ) debug_line_section = module.GetSectionAtIndex(3) self.assertEqual(".debug_line", debug_line_section.GetName()) self.assertEqual( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) + lldb.LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) ) debug_str_section = module.GetSectionAtIndex(4) self.assertEqual(".debug_str", debug_str_section.GetName()) self.assertEqual( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) + lldb.LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) ) @skipIfAsan @skipIfXmlSupportMissing - def test_load_module_from_file(self): - """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module from a file""" - - yaml_path = "test_wasm_embedded_debug_sections.yaml" - yaml_base, ext = os.path.splitext(yaml_path) + def test_simple_wasm_debugging_session(self): + """Test connecting to a WebAssembly engine via GDB-remote, loading a + Wasm module with embedded DWARF symbols, setting a breakpoint and + checking the debuggee state""" + + # simple.yaml was created by compiling simple.c to wasm and using + # obj2yaml on the output. + # + # $ clang -target wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -O0 -g -o simple.wasm simple.c + # $ obj2yaml simple.wasm -o simple.yaml + yaml_path = "simple.yaml" + yaml_base, _ = os.path.splitext(yaml_path) obj_path = self.getBuildArtifact(yaml_base) self.yaml2obj(yaml_path, obj_path) - self.server.responder = MyResponder(obj_path) + # Create a fake call stack. + call_stacks = [ + WasmCallStack( + [WasmStackFrame(0x019C), WasmStackFrame(0x01E5), WasmStackFrame(0x01FE)] + ), + ] + + # Create fake memory for our wasm locals. + self.memory = FakeMemory(0x10000, 0x20000) + self.memory.store_bytes( + WASM_LOCAL_ADDR, + bytes.fromhex( + "0000000000000000020000000100000000000000020000000100000000000000" + ), + ) + + self.server.responder = MyResponder( + obj_path, "test_wasm", call_stacks, self.memory + ) target = self.dbg.CreateTarget("") + breakpoint = target.BreakpointCreateByName("add") process = self.connect(target, "wasm") lldbutil.expect_state_changes( self, self.dbg.GetListener(), process, [lldb.eStateStopped] ) + location = breakpoint.GetLocationAtIndex(0) + self.assertTrue(location and location.IsEnabled(), VALID_BREAKPOINT_LOCATION) + num_modules = target.GetNumModules() self.assertEqual(1, num_modules) - module = target.GetModuleAtIndex(0) - num_sections = module.GetNumSections() - self.assertEqual(5, num_sections) - - code_section = module.GetSectionAtIndex(0) - self.assertEqual("code", code_section.GetName()) - self.assertEqual( - load_address | code_section.GetFileOffset(), - code_section.GetLoadAddress(target), - ) - - debug_info_section = module.GetSectionAtIndex(1) - self.assertEqual(".debug_info", debug_info_section.GetName()) - self.assertEqual( - LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target) - ) - - debug_abbrev_section = module.GetSectionAtIndex(2) - self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName()) - self.assertEqual( - LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target) - ) - - debug_line_section = module.GetSectionAtIndex(3) - self.assertEqual(".debug_line", debug_line_section.GetName()) - self.assertEqual( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) - ) - - debug_str_section = module.GetSectionAtIndex(4) - self.assertEqual(".debug_str", debug_str_section.GetName()) - self.assertEqual( - LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target) - ) - thread = process.GetThreadAtIndex(0) self.assertTrue(thread.IsValid()) - frame = thread.GetFrameAtIndex(0) - self.assertTrue(frame.IsValid()) - self.assertEqual(frame.GetPC(), 0x40000000000001B3) - - frame = thread.GetFrameAtIndex(1) - self.assertTrue(frame.IsValid()) - self.assertEqual(frame.GetPC(), 0x40000000000001FE) + # Check that our frames match our fake call stack. + frame0 = thread.GetFrameAtIndex(0) + self.assertTrue(frame0.IsValid()) + self.assertEqual(frame0.GetPC(), LOAD_ADDRESS | 0x019C) + self.assertIn("add", frame0.GetFunctionName()) + + frame1 = thread.GetFrameAtIndex(1) + self.assertTrue(frame1.IsValid()) + self.assertEqual(frame1.GetPC(), LOAD_ADDRESS | 0x01E5) + self.assertIn("main", frame1.GetFunctionName()) + + # Check that we can resolve local variables. + a = frame0.FindVariable("a") + self.assertTrue(a.IsValid()) + self.assertEqual(a.GetValueAsUnsigned(), 1) + + b = frame0.FindVariable("b") + self.assertTrue(b.IsValid()) + self.assertEqual(b.GetValueAsUnsigned(), 2) diff --git a/lldb/test/API/functionalities/gdb_remote_client/simple.c b/lldb/test/API/functionalities/gdb_remote_client/simple.c new file mode 100644 index 0000000..62ca1fe --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/simple.c @@ -0,0 +1,10 @@ +int add(int a, int b) { + // Break here + return a + b; +} + +int main() { + int i = 1; + int j = 2; + return add(i, j); +} diff --git a/lldb/test/API/functionalities/gdb_remote_client/simple.yaml b/lldb/test/API/functionalities/gdb_remote_client/simple.yaml new file mode 100644 index 0000000..cf1b7d8 --- /dev/null +++ b/lldb/test/API/functionalities/gdb_remote_client/simple.yaml @@ -0,0 +1,228 @@ +--- !WASM +FileHeader: + Version: 0x1 +Sections: + - Type: TYPE + Signatures: + - Index: 0 + ParamTypes: [] + ReturnTypes: [] + - Index: 1 + ParamTypes: + - I32 + - I32 + ReturnTypes: + - I32 + - Index: 2 + ParamTypes: [] + ReturnTypes: + - I32 + - Type: FUNCTION + FunctionTypes: [ 0, 1, 2, 1 ] + - Type: TABLE + Tables: + - Index: 0 + ElemType: FUNCREF + Limits: + Flags: [ HAS_MAX ] + Minimum: 0x1 + Maximum: 0x1 + - Type: MEMORY + Memories: + - Minimum: 0x2 + - Type: GLOBAL + Globals: + - Index: 0 + Type: I32 + Mutable: true + InitExpr: + Opcode: I32_CONST + Value: 66560 + - Index: 1 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 1024 + - Index: 2 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 1024 + - Index: 3 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 1024 + - Index: 4 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 66560 + - Index: 5 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 1024 + - Index: 6 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 66560 + - Index: 7 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 131072 + - Index: 8 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 0 + - Index: 9 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 1 + - Index: 10 + Type: I32 + Mutable: false + InitExpr: + Opcode: I32_CONST + Value: 65536 + - Type: EXPORT + Exports: + - Name: memory + Kind: MEMORY + Index: 0 + - Name: __wasm_call_ctors + Kind: FUNCTION + Index: 0 + - Name: add + Kind: FUNCTION + Index: 1 + - Name: __original_main + Kind: FUNCTION + Index: 2 + - Name: main + Kind: FUNCTION + Index: 3 + - Name: __main_void + Kind: FUNCTION + Index: 2 + - Name: __indirect_function_table + Kind: TABLE + Index: 0 + - Name: __dso_handle + Kind: GLOBAL + Index: 1 + - Name: __data_end + Kind: GLOBAL + Index: 2 + - Name: __stack_low + Kind: GLOBAL + Index: 3 + - Name: __stack_high + Kind: GLOBAL + Index: 4 + - Name: __global_base + Kind: GLOBAL + Index: 5 + - Name: __heap_base + Kind: GLOBAL + Index: 6 + - Name: __heap_end + Kind: GLOBAL + Index: 7 + - Name: __memory_base + Kind: GLOBAL + Index: 8 + - Name: __table_base + Kind: GLOBAL + Index: 9 + - Name: __wasm_first_page_end + Kind: GLOBAL + Index: 10 + - Type: CODE + Functions: + - Index: 0 + Locals: [] + Body: 0B + - Index: 1 + Locals: + - Type: I32 + Count: 1 + Body: 23808080800041106B21022002200036020C20022001360208200228020C20022802086A0F0B + - Index: 2 + Locals: + - Type: I32 + Count: 2 + Body: 23808080800041106B210020002480808080002000410036020C2000410136020820004102360204200028020820002802041081808080002101200041106A24808080800020010F0B + - Index: 3 + Locals: [] + Body: 1082808080000F0B + - Type: CUSTOM + Name: .debug_abbrev + Payload: 011101250E1305030E10171B0E110155170000022E01110112064018030E3A0B3B0B271949133F1900000305000218030E3A0B3B0B49130000042E01110112064018030E3A0B3B0B49133F1900000534000218030E3A0B3B0B49130000062400030E3E0B0B0B000000 + - Type: CUSTOM + Name: .debug_info + Payload: 940000000400000000000401620000001D0055000000000000000D000000000000000000000002050000002900000004ED00029F510000000101900000000302910C60000000010190000000030291085E00000001019000000000042F0000004C00000004ED00009F04000000010690000000050291080B0000000107900000000502910409000000010890000000000600000000050400 + - Type: CUSTOM + Name: .debug_ranges + Payload: 050000002E0000002F0000007B0000000000000000000000 + - Type: CUSTOM + Name: .debug_str + Payload: 696E74006D61696E006A0069002F55736572732F6A6F6E61732F7761736D2D6D6963726F2D72756E74696D652F70726F647563742D6D696E692F706C6174666F726D732F64617277696E2F6275696C64006164640073696D706C652E630062006100636C616E672076657273696F6E2032322E302E306769742028676974406769746875622E636F6D3A4A4465766C696567686572652F6C6C766D2D70726F6A6563742E67697420343161363839613132323834633834623632383933393461356338306264636534383733656466302900 + - Type: CUSTOM + Name: .debug_line + Payload: 62000000040020000000010101FB0E0D0001010101000000010000010073696D706C652E6300000000000005020500000001050A0A08AE050E0658050C5805032002020001010005022F0000001705070A08BB75050E7505110658050A58050382020F000101 + - Type: CUSTOM + Name: name + FunctionNames: + - Index: 0 + Name: __wasm_call_ctors + - Index: 1 + Name: add + - Index: 2 + Name: __original_main + - Index: 3 + Name: main + GlobalNames: + - Index: 0 + Name: __stack_pointer + - Type: CUSTOM + Name: producers + Languages: + - Name: C11 + Version: '' + Tools: + - Name: clang + Version: '22.0.0git' + - Type: CUSTOM + Name: target_features + Features: + - Prefix: USED + Name: bulk-memory + - Prefix: USED + Name: bulk-memory-opt + - Prefix: USED + Name: call-indirect-overlong + - Prefix: USED + Name: multivalue + - Prefix: USED + Name: mutable-globals + - Prefix: USED + Name: nontrapping-fptoint + - Prefix: USED + Name: reference-types + - Prefix: USED + Name: sign-ext +... diff --git a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py index 0d06a9d..dc555dd 100644 --- a/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py +++ b/lldb/test/API/functionalities/plugins/python_os_plugin/stepping_plugin_threads/TestOSPluginStepping.py @@ -123,5 +123,5 @@ class TestOSPluginStepping(TestBase): os_thread = self.get_os_thread() self.assertTrue(os_thread.IsValid(), "The OS thread is back after continue") self.assertIn( - "step out", os_thread.GetStopDescription(100), "Completed step out plan" + "step out", os_thread.stop_description, "Completed step out plan" ) diff --git a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py index 8776d72..4b7d24e 100644 --- a/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py +++ b/lldb/test/API/functionalities/postmortem/minidump-new/TestMiniDumpNew.py @@ -117,7 +117,7 @@ class MiniDumpNewTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonSignal) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertIn("SIGSEGV", stop_description) @skipIfLLVMTargetMissing("X86") @@ -153,7 +153,7 @@ class MiniDumpNewTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonNone) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertEqual(stop_description, "") def test_snapshot_minidump_null_exn_code(self): @@ -164,7 +164,7 @@ class MiniDumpNewTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonNone) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertEqual(stop_description, "") def check_register_unsigned(self, set, name, expected): @@ -198,7 +198,7 @@ class MiniDumpNewTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonNone) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertEqual(stop_description, "") registers = thread.GetFrameAtIndex(0).GetRegisters() # Verify the GPR registers are all correct @@ -261,7 +261,7 @@ class MiniDumpNewTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonNone) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertEqual(stop_description, "") registers = thread.GetFrameAtIndex(0).GetRegisters() # Verify the GPR registers are all correct @@ -522,7 +522,7 @@ class MiniDumpNewTestCase(TestBase): for i in range(2): thread = self.process.GetThreadAtIndex(i) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonSignal) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertIn("SIGSEGV", stop_description) def test_breakpoint_on_minidump(self): @@ -539,7 +539,7 @@ class MiniDumpNewTestCase(TestBase): process = target.LoadCore(core) self.assertTrue(process, VALID_PROCESS) thread = process.GetThreadAtIndex(0) - stop_reason = thread.GetStopDescription(256) + stop_reason = thread.stop_description self.assertIn("breakpoint 1.1", stop_reason) finally: if os.path.isfile(core): diff --git a/lldb/test/API/functionalities/postmortem/minidump/TestMiniDump.py b/lldb/test/API/functionalities/postmortem/minidump/TestMiniDump.py index 8fe5d2a..362b219 100644 --- a/lldb/test/API/functionalities/postmortem/minidump/TestMiniDump.py +++ b/lldb/test/API/functionalities/postmortem/minidump/TestMiniDump.py @@ -32,7 +32,7 @@ class MiniDumpTestCase(TestBase): self.assertEqual(self.process.GetNumThreads(), 1) thread = self.process.GetThreadAtIndex(0) self.assertStopReason(thread.GetStopReason(), lldb.eStopReasonException) - stop_description = thread.GetStopDescription(256) + stop_description = thread.stop_description self.assertIn("0xc0000005", stop_description) def test_modules_in_mini_dump(self): diff --git a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py index 736bb69..ee5ae32 100644 --- a/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py +++ b/lldb/test/API/functionalities/scripted_process/stack_core_scripted_process.py @@ -204,9 +204,7 @@ class StackCoreScriptedThread(ScriptedThread): if self.is_stopped: if "arm64" in self.scripted_process.arch: stop_reason["type"] = lldb.eStopReasonException - stop_reason["data"][ - "desc" - ] = self.corefile_thread.GetStopDescription(100) + stop_reason["data"]["desc"] = self.corefile_thread.stop_description elif self.scripted_process.arch == "x86_64": stop_reason["type"] = lldb.eStopReasonSignal stop_reason["data"]["signal"] = signal.SIGTRAP diff --git a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py index 5276369..343236a 100644 --- a/lldb/test/API/functionalities/step_scripted/TestStepScripted.py +++ b/lldb/test/API/functionalities/step_scripted/TestStepScripted.py @@ -41,7 +41,7 @@ class StepScriptedTestCase(TestBase): frame = thread.GetFrameAtIndex(0) self.assertEqual("main", frame.GetFunctionName()) - stop_desc = thread.GetStopDescription(1000) + stop_desc = thread.stop_description self.assertIn("Stepping out from", stop_desc, "Got right description") def run_until_branch_instruction(self): @@ -153,7 +153,7 @@ class StepScriptedTestCase(TestBase): self.assertTrue(foo_val.GetValueDidChange(), "Foo changed") # And we should have a reasonable stop description: - desc = thread.GetStopDescription(1000) + desc = thread.stop_description self.assertIn("Stepped until foo changed", desc, "Got right stop description") def test_stop_others_from_command(self): diff --git a/lldb/test/API/functionalities/tail_call_frames/cross_dso/TestCrossDSOTailCalls.py b/lldb/test/API/functionalities/tail_call_frames/cross_dso/TestCrossDSOTailCalls.py index 7c3d09b..0ca2f9e5 100644 --- a/lldb/test/API/functionalities/tail_call_frames/cross_dso/TestCrossDSOTailCalls.py +++ b/lldb/test/API/functionalities/tail_call_frames/cross_dso/TestCrossDSOTailCalls.py @@ -11,7 +11,6 @@ class TestCrossDSOTailCalls(TestBase): @skipIf(compiler="clang", compiler_version=["<", "10.0"]) @skipIf(dwarf_version=["<", "4"]) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265") - @expectedFailureAll(archs=["arm$", "arm64", "aarch64"], bugnumber="llvm.org/PR44561") def test_cross_dso_tail_calls(self): self.build() exe = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/functionalities/tail_call_frames/cross_object/TestCrossObjectTailCalls.py b/lldb/test/API/functionalities/tail_call_frames/cross_object/TestCrossObjectTailCalls.py index 180f4d3..b5de75e 100644 --- a/lldb/test/API/functionalities/tail_call_frames/cross_object/TestCrossObjectTailCalls.py +++ b/lldb/test/API/functionalities/tail_call_frames/cross_object/TestCrossObjectTailCalls.py @@ -11,7 +11,6 @@ class TestCrossObjectTailCalls(TestBase): @skipIf(compiler="clang", compiler_version=["<", "10.0"]) @skipIf(dwarf_version=["<", "4"]) @expectedFailureAll(oslist=["windows"], bugnumber="llvm.org/pr26265") - @expectedFailureAll(archs=["arm$", "arm64", "aarch64"], bugnumber="llvm.org/PR44561") def test_cross_object_tail_calls(self): self.build() exe = self.getBuildArtifact("a.out") diff --git a/lldb/test/API/functionalities/tsan/multiple/TestTsanMultiple.py b/lldb/test/API/functionalities/tsan/multiple/TestTsanMultiple.py index 435e180..aa2d1d9 100644 --- a/lldb/test/API/functionalities/tsan/multiple/TestTsanMultiple.py +++ b/lldb/test/API/functionalities/tsan/multiple/TestTsanMultiple.py @@ -49,7 +49,7 @@ class TsanMultipleTestCase(TestBase): stop_description = ( self.dbg.GetSelectedTarget() .process.GetSelectedThread() - .GetStopDescription(100) + .stop_description ) self.assertTrue( diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/Makefile b/lldb/test/API/lang/cpp/expr-definition-in-dylib/Makefile new file mode 100644 index 0000000..82daeb1 --- /dev/null +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/Makefile @@ -0,0 +1,6 @@ +CXX_SOURCES := main.cpp + +DYLIB_CXX_SOURCES := lib.cpp +DYLIB_NAME := lib + +include Makefile.rules diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py new file mode 100644 index 0000000..02c34b3 --- /dev/null +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/TestExprDefinitionInDylib.py @@ -0,0 +1,33 @@ +import lldb +from lldbsuite.test.decorators import * +from lldbsuite.test.lldbtest import * +from lldbsuite.test import lldbutil + + +class ExprDefinitionInDylibTestCase(TestBase): + NO_DEBUG_INFO_TESTCASE = True + + @skipIfWindows + def test(self): + """ + Tests that we can call functions whose definition + is in a different LLDB module than it's declaration. + """ + self.build() + + target = self.dbg.CreateTarget(self.getBuildArtifact("a.out")) + self.assertTrue(target, VALID_TARGET) + + env = self.registerSharedLibrariesWithTarget(target, ["lib"]) + + breakpoint = lldbutil.run_break_set_by_file_and_line( + self, "main.cpp", line_number("main.cpp", "return") + ) + + process = target.LaunchSimple(None, env, self.get_process_working_directory()) + + self.assertIsNotNone( + lldbutil.get_one_thread_stopped_at_breakpoint_id(self.process(), breakpoint) + ) + + self.expect_expr("f.method()", result_value="-72", result_type="int") diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp new file mode 100644 index 0000000..ad148ce --- /dev/null +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.cpp @@ -0,0 +1,3 @@ +#include "lib.h" + +int Foo::method() { return -72; } diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h new file mode 100644 index 0000000..9568db2 --- /dev/null +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/lib.h @@ -0,0 +1,8 @@ +#ifndef LIB_H_IN +#define LIB_H_IN + +struct Foo { + int method(); +}; + +#endif // LIB_H_IN diff --git a/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp new file mode 100644 index 0000000..2fddb2b --- /dev/null +++ b/lldb/test/API/lang/cpp/expr-definition-in-dylib/main.cpp @@ -0,0 +1,6 @@ +#include "lib.h" + +int main() { + Foo f; + return f.method(); +} diff --git a/lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py b/lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py index c10d958..a850908 100644 --- a/lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py +++ b/lldb/test/API/macosx/abort_with_payload/TestAbortWithPayload.py @@ -61,7 +61,7 @@ class TestAbortWithPayload(TestBase): self.assertEqual(thread, sel_thread, "Selected the original thread") # Make sure the stop reason is right: self.assertEqual( - thread.GetStopDescription(100), + thread.stop_description, "abort with payload or reason", "Description was right", ) diff --git a/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py b/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py index ada74a1..e452bb5 100644 --- a/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py +++ b/lldb/test/API/macosx/corefile-exception-reason/TestCorefileExceptionReason.py @@ -44,7 +44,7 @@ class TestCorefileExceptionReason(TestBase): self.runCmd("fr v") self.assertEqual( - thread.GetStopDescription(256), "ESR_EC_DABORT_EL0 (fault address: 0x0)" + thread.stop_description, "ESR_EC_DABORT_EL0 (fault address: 0x0)" ) if self.TraceOn(): diff --git a/lldb/test/API/riscv/break-undecoded/TestBreakpointIllegal.py b/lldb/test/API/riscv/break-undecoded/TestBreakpointIllegal.py index 41e8901..5b00298 100644 --- a/lldb/test/API/riscv/break-undecoded/TestBreakpointIllegal.py +++ b/lldb/test/API/riscv/break-undecoded/TestBreakpointIllegal.py @@ -17,7 +17,7 @@ class TestBreakpointIllegal(TestBase): ) self.runCmd("thread step-inst") # we need to step more, as some compilers do not set appropriate debug info. - while cur_thread.GetStopDescription(256) == "instruction step into": + while cur_thread.stop_description == "instruction step into": self.runCmd("thread step-inst") # The stop reason of the thread should be illegal opcode. self.expect( @@ -34,7 +34,7 @@ class TestBreakpointIllegal(TestBase): ) self.runCmd("thread step-inst") # we need to step more, as some compilers do not set appropriate debug info. - while cur_thread.GetStopDescription(256) == "instruction step into": + while cur_thread.stop_description == "instruction step into": self.runCmd("thread step-inst") # The stop reason of the thread should be illegal opcode. self.expect( diff --git a/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c new file mode 100644 index 0000000..9f12c32 --- /dev/null +++ b/lldb/test/Shell/Recognizer/Inputs/ubsan_add_overflow.c @@ -0,0 +1,3 @@ +#include <limits.h> + +int main() { return INT_MAX + 1; } diff --git a/lldb/test/Shell/Recognizer/ubsan_add_overflow.test b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test new file mode 100644 index 0000000..a5e95cf --- /dev/null +++ b/lldb/test/Shell/Recognizer/ubsan_add_overflow.test @@ -0,0 +1,22 @@ +# UNSUPPORTED: system-windows + +# RUN: %clang_host -g -O0 %S/Inputs/ubsan_add_overflow.c -o %t.out \ +# RUN: -fsanitize=signed-integer-overflow -fsanitize-trap=signed-integer-overflow + +# RUN: %lldb -b -s %s %t.out | FileCheck %s + +run +# CHECK: thread #{{.*}} stop reason = Undefined Behavior Sanitizer: Integer addition overflowed +# CHECK-NEXT: frame #1: {{.*}}`main at ubsan_add_overflow.c + +bt +# CHECK: frame #0: {{.*}}`__clang_trap_msg$Undefined Behavior Sanitizer$Integer addition overflowed{{.*}} +# CHECK: frame #1: {{.*}}`main at ubsan_add_overflow.c + +frame info +# CHECK: frame #{{.*}}`main at ubsan_add_overflow.c + +frame recognizer info 0 +# CHECK: frame 0 is recognized by Verbose Trap StackFrame Recognizer + +quit diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test index 2b1818e..183ea3a 100644 --- a/lldb/test/Shell/Scripts/TestFrameworkFixScript.test +++ b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test @@ -1,6 +1,6 @@ # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/SBAddress.h | FileCheck %s diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test index ba18b4b..a4fffe4 100644 --- a/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test +++ b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test @@ -1,7 +1,7 @@ # REQUIRES: system-darwin # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/SBAddress.h | FileCheck %s diff --git a/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test index e2080ca..d7775c2 100644 --- a/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test +++ b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test @@ -1,6 +1,6 @@ # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_rpc -i %p/Inputs/RPC/RPCSBAddress.h -o %t/Outputs/RPCSBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_rpc -i %p/Inputs/RPC/RPCSBAddress.h -o %t/Outputs/RPCSBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/RPCSBAddress.h | FileCheck %s diff --git a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake index 6c363f4..2376e23 100644 --- a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake +++ b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake @@ -79,7 +79,7 @@ function(FixIncludePaths in subfolder out) add_custom_command(OUTPUT ${parked_header} COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py - -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} --unifdef_guards USWIG + -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} --unifdef_guards SWIG DEPENDS ${in} COMMENT "Fixing includes in ${in}" ) diff --git a/lldb/unittests/Core/DumpDataExtractorTest.cpp b/lldb/unittests/Core/DumpDataExtractorTest.cpp index 3d1e8bc..6302f1e 100644 --- a/lldb/unittests/Core/DumpDataExtractorTest.cpp +++ b/lldb/unittests/Core/DumpDataExtractorTest.cpp @@ -163,6 +163,9 @@ TEST_F(DumpDataExtractorTest, Formats) { TestDump(0xcafef00d, lldb::Format::eFormatHex, "0xcafef00d"); TestDump(0xcafef00d, lldb::Format::eFormatHexUppercase, "0xCAFEF00D"); TestDump(0.456, lldb::Format::eFormatFloat, "0.45600000000000002"); + TestDump(std::vector<uint64_t>{0x47ae147ae147ae14, 0x40011147ae147ae1}, + lldb::Format::eFormatFloat128, + "4.26999999999999999999999999999999963"); TestDump(9, lldb::Format::eFormatOctal, "011"); // Chars packed into an integer. TestDump<uint32_t>(0x4C4C4442, lldb::Format::eFormatOSType, "'LLDB'"); @@ -388,6 +391,9 @@ TEST_F(DumpDataExtractorTest, ItemByteSizeErrors) { TestDumpWithItemByteSize( 18, lldb::Format::eFormatFloat, "error: unsupported byte size (18) for float format"); + TestDumpWithItemByteSize( + 17, lldb::Format::eFormatFloat128, + "error: unsupported byte size (17) for float format"); // We want sizes to exactly match one of float/double. TestDumpWithItemByteSize( diff --git a/lldb/unittests/Expression/CMakeLists.txt b/lldb/unittests/Expression/CMakeLists.txt index 533cdc6..4c58b3c 100644 --- a/lldb/unittests/Expression/CMakeLists.txt +++ b/lldb/unittests/Expression/CMakeLists.txt @@ -4,6 +4,7 @@ add_lldb_unittest(ExpressionTests DiagnosticManagerTest.cpp DWARFExpressionTest.cpp CppModuleConfigurationTest.cpp + ExpressionTest.cpp LINK_LIBS lldbCore diff --git a/lldb/unittests/Expression/ExpressionTest.cpp b/lldb/unittests/Expression/ExpressionTest.cpp new file mode 100644 index 0000000..12f6dd5 --- /dev/null +++ b/lldb/unittests/Expression/ExpressionTest.cpp @@ -0,0 +1,122 @@ +//===-- ExpressionTest.cpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +#include "TestingSupport/TestUtilities.h" +#include "lldb/Expression/Expression.h" +#include "llvm/Testing/Support/Error.h" + +using namespace lldb_private; + +struct LabelTestCase { + llvm::StringRef encoded; + FunctionCallLabel label; + llvm::SmallVector<llvm::StringRef> error_pattern; +}; + +static LabelTestCase g_label_test_cases[] = { + // Failure modes + {"bar:0x0:0x0:_Z3foov", + {}, + {"expected function call label prefix '$__lldb_func' but found 'bar' " + "instead."}}, + {"$__lldb_func :0x0:0x0:_Z3foov", + {}, + {"expected function call label prefix '$__lldb_func' but found " + "'$__lldb_func ' instead."}}, + {"$__lldb_funcc:0x0:0x0:_Z3foov", + {}, + {"expected function call label prefix '$__lldb_func' but found " + "'$__lldb_funcc' instead."}}, + {"", {}, {"malformed function call label."}}, + {"foo", {}, {"malformed function call label."}}, + {"$__lldb_func", {}, {"malformed function call label."}}, + {"$__lldb_func:", {}, {"malformed function call label."}}, + {"$__lldb_func:0x0:0x0", {}, {"malformed function call label."}}, + {"$__lldb_func:abc:0x0:_Z3foov", + {}, + {"failed to parse module ID from 'abc'."}}, + {"$__lldb_func:-1:0x0:_Z3foov", + {}, + {"failed to parse module ID from '-1'."}}, + {"$__lldb_func:0x0invalid:0x0:_Z3foov", + {}, + {"failed to parse module ID from '0x0invalid'."}}, + {"$__lldb_func:0x0 :0x0:_Z3foov", + {}, + {"failed to parse module ID from '0x0 '."}}, + {"$__lldb_func:0x0:abc:_Z3foov", + {}, + {"failed to parse symbol ID from 'abc'."}}, + {"$__lldb_func:0x5:-1:_Z3foov", + {}, + {"failed to parse symbol ID from '-1'."}}, + {"$__lldb_func:0x5:0x0invalid:_Z3foov", + {}, + {"failed to parse symbol ID from '0x0invalid'."}}, + {"$__lldb_func:0x5:0x0 :_Z3foov", + {}, + {"failed to parse symbol ID from '0x0 '."}}, + {"$__lldb_func:0x0:0x0:_Z3foov", + { + /*.module_id=*/0x0, + /*.symbol_id=*/0x0, + /*.lookup_name=*/"_Z3foov", + }, + {}}, + {"$__lldb_func:0x0:0x0:abc:def:::a", + { + /*.module_id=*/0x0, + /*.symbol_id=*/0x0, + /*.lookup_name=*/"abc:def:::a", + }, + {}}, + {"$__lldb_func:0xd2:0xf0:$__lldb_func", + { + /*.module_id=*/0xd2, + /*.symbol_id=*/0xf0, + /*.lookup_name=*/"$__lldb_func", + }, + {}}, +}; + +struct ExpressionTestFixture : public testing::TestWithParam<LabelTestCase> {}; + +TEST_P(ExpressionTestFixture, FunctionCallLabel) { + const auto &[encoded, label, errors] = GetParam(); + + auto decoded_or_err = FunctionCallLabel::fromString(encoded); + if (!errors.empty()) { + EXPECT_THAT_EXPECTED( + decoded_or_err, + llvm::FailedWithMessageArray(testing::ElementsAreArray(errors))); + return; + } + + EXPECT_THAT_EXPECTED(decoded_or_err, llvm::Succeeded()); + + auto label_str = label.toString(); + EXPECT_EQ(decoded_or_err->toString(), encoded); + EXPECT_EQ(label_str, encoded); + + EXPECT_EQ(decoded_or_err->module_id, label.module_id); + EXPECT_EQ(decoded_or_err->symbol_id, label.symbol_id); + EXPECT_EQ(decoded_or_err->lookup_name, label.lookup_name); + + auto roundtrip_or_err = FunctionCallLabel::fromString(label_str); + EXPECT_THAT_EXPECTED(roundtrip_or_err, llvm::Succeeded()); + + EXPECT_EQ(roundtrip_or_err->module_id, label.module_id); + EXPECT_EQ(roundtrip_or_err->symbol_id, label.symbol_id); + EXPECT_EQ(roundtrip_or_err->lookup_name, label.lookup_name); +} + +INSTANTIATE_TEST_SUITE_P(FunctionCallLabelTest, ExpressionTestFixture, + testing::ValuesIn(g_label_test_cases)); diff --git a/lldb/unittests/Host/FileSystemTest.cpp b/lldb/unittests/Host/FileSystemTest.cpp index 58887f6..e3c2b5a 100644 --- a/lldb/unittests/Host/FileSystemTest.cpp +++ b/lldb/unittests/Host/FileSystemTest.cpp @@ -186,7 +186,7 @@ TEST(FileSystemTest, FileAndDirectoryComponents) { } static IntrusiveRefCntPtr<DummyFileSystem> GetSimpleDummyFS() { - IntrusiveRefCntPtr<DummyFileSystem> D(new DummyFileSystem()); + auto D = makeIntrusiveRefCnt<DummyFileSystem>(); D->addRegularFile("/foo"); D->addDirectory("/bar"); D->addSymlink("/baz"); diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp index 71930ab..b993b82 100644 --- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp +++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" +#include "llvm/IR/GlobalValue.h" #include "gtest/gtest.h" using namespace clang; @@ -76,6 +77,8 @@ TEST_F(TestTypeSystemClang, TestGetBasicTypeFromEnum) { context.getComplexType(context.FloatTy))); EXPECT_TRUE( context.hasSameType(GetBasicQualType(eBasicTypeHalf), context.HalfTy)); + EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeFloat128), + context.Float128Ty)); EXPECT_TRUE( context.hasSameType(GetBasicQualType(eBasicTypeInt), context.IntTy)); EXPECT_TRUE(context.hasSameType(GetBasicQualType(eBasicTypeInt128), @@ -869,7 +872,7 @@ TEST_F(TestTypeSystemClang, TestFunctionTemplateConstruction) { CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U); FunctionDecl *func = m_ast->CreateFunctionDeclaration( TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None, - false); + false, /*asm_label=*/{}); TypeSystemClang::TemplateParameterInfos empty_params; // Create the actual function template. @@ -900,7 +903,7 @@ TEST_F(TestTypeSystemClang, TestFunctionTemplateInRecordConstruction) { // 2. It is mirroring the behavior of DWARFASTParserClang::ParseSubroutine. FunctionDecl *func = m_ast->CreateFunctionDeclaration( TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None, - false); + false, /*asm_label=*/{}); TypeSystemClang::TemplateParameterInfos empty_params; // Create the actual function template. @@ -938,7 +941,7 @@ TEST_F(TestTypeSystemClang, TestDeletingImplicitCopyCstrDueToMoveCStr) { bool is_attr_used = false; bool is_artificial = false; m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, /*asm_label=*/{}, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); @@ -975,7 +978,7 @@ TEST_F(TestTypeSystemClang, TestNotDeletingUserCopyCstrDueToMoveCStr) { CompilerType function_type = m_ast->CreateFunctionType( return_type, args, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, /*asm_label=*/{}, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); } @@ -987,7 +990,7 @@ TEST_F(TestTypeSystemClang, TestNotDeletingUserCopyCstrDueToMoveCStr) { m_ast->CreateFunctionType(return_type, args, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), class_name, nullptr, function_type, + t.GetOpaqueQualType(), class_name, /*asm_label=*/{}, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); } @@ -1098,7 +1101,7 @@ TEST_F(TestTypeSystemClang, AddMethodToCXXRecordType_ParmVarDecls) { m_ast->CreateFunctionType(return_type, param_types, /*variadic=*/false, /*quals*/ 0U); m_ast->AddMethodToCXXRecordType( - t.GetOpaqueQualType(), "myFunc", nullptr, function_type, + t.GetOpaqueQualType(), "myFunc", /*asm_label=*/{}, function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, is_explicit, is_attr_used, is_artificial); @@ -1116,3 +1119,130 @@ TEST_F(TestTypeSystemClang, AddMethodToCXXRecordType_ParmVarDecls) { EXPECT_EQ(method_it->getParamDecl(0)->getDeclContext(), *method_it); EXPECT_EQ(method_it->getParamDecl(1)->getDeclContext(), *method_it); } + +TEST_F(TestTypeSystemClang, AsmLabel_CtorDtor) { + // Tests TypeSystemClang::DeclGetMangledName for constructors/destructors + // with and without AsmLabels. + + llvm::StringRef class_name = "S"; + CompilerType t = clang_utils::createRecord(*m_ast, class_name); + m_ast->StartTagDeclarationDefinition(t); + + CompilerType return_type = m_ast->GetBasicType(lldb::eBasicTypeVoid); + const bool is_virtual = false; + const bool is_static = false; + const bool is_inline = false; + const bool is_explicit = true; + const bool is_attr_used = false; + const bool is_artificial = false; + + CompilerType function_type = + m_ast->CreateFunctionType(return_type, {}, + /*variadic=*/false, /*quals*/ 0U); + auto *ctor_nolabel = m_ast->AddMethodToCXXRecordType( + t.GetOpaqueQualType(), "S", /*asm_label=*/{}, function_type, + lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, + is_explicit, is_attr_used, is_artificial); + + auto *dtor_nolabel = m_ast->AddMethodToCXXRecordType( + t.GetOpaqueQualType(), "~S", /*asm_label=*/{}, function_type, + lldb::AccessType::eAccessPublic, is_virtual, is_static, is_inline, + is_explicit, is_attr_used, is_artificial); + + auto *ctor = m_ast->AddMethodToCXXRecordType( + t.GetOpaqueQualType(), "S", /*asm_label=*/"$__lldb_func:0x0:0x0:S", + function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, + is_inline, is_explicit, is_attr_used, is_artificial); + + auto *dtor = m_ast->AddMethodToCXXRecordType( + t.GetOpaqueQualType(), "~S", /*asm_label=*/"$__lldb_func:0x0:0x0:~S", + function_type, lldb::AccessType::eAccessPublic, is_virtual, is_static, + is_inline, is_explicit, is_attr_used, is_artificial); + + m_ast->CompleteTagDeclarationDefinition(t); + + ASSERT_TRUE(ctor_nolabel); + ASSERT_TRUE(dtor_nolabel); + ASSERT_TRUE(ctor); + ASSERT_TRUE(dtor); + +#ifdef _WIN32 + EXPECT_STREQ(m_ast->DeclGetMangledName(ctor_nolabel).GetCString(), + "??0S@@QEAA@XZ"); + EXPECT_STREQ(m_ast->DeclGetMangledName(dtor_nolabel).GetCString(), + "??_DS@@QEAAXXZ"); +#else + EXPECT_STREQ(m_ast->DeclGetMangledName(ctor_nolabel).GetCString(), + "_ZN1SC1Ev"); + EXPECT_STREQ(m_ast->DeclGetMangledName(dtor_nolabel).GetCString(), + "_ZN1SD1Ev"); +#endif + + EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape( + m_ast->DeclGetMangledName(ctor).GetStringRef()) + .data(), + "$__lldb_func:0x0:0x0:S"); + EXPECT_STREQ(llvm::GlobalValue::dropLLVMManglingEscape( + m_ast->DeclGetMangledName(dtor).GetStringRef()) + .data(), + "$__lldb_func:0x0:0x0:~S"); +} + +struct AsmLabelTestCase { + llvm::StringRef mangled; + llvm::StringRef expected; +}; + +class TestTypeSystemClangAsmLabel + : public testing::TestWithParam<AsmLabelTestCase> { +public: + SubsystemRAII<FileSystem, HostInfo> subsystems; + + void SetUp() override { + m_holder = + std::make_unique<clang_utils::TypeSystemClangHolder>("test ASTContext"); + m_ast = m_holder->GetAST(); + } + + void TearDown() override { + m_ast = nullptr; + m_holder.reset(); + } + +protected: + TypeSystemClang *m_ast = nullptr; + std::unique_ptr<clang_utils::TypeSystemClangHolder> m_holder; +}; + +static AsmLabelTestCase g_asm_label_test_cases[] = { + {/*mangled=*/"$__lldb_func:0x0:0x0:_Z3foov", + /*expected=*/"_Z3foov"}, + {/*mangled=*/"$__lldb_func:0x0:0x0:foo", + /*expected=*/"$__lldb_func:0x0:0x0:foo"}, + {/*mangled=*/"foo", + /*expected=*/"foo"}, + {/*mangled=*/"_Z3foov", + /*expected=*/"_Z3foov"}, + {/*mangled=*/"$__lldb_func:", + /*expected=*/"$__lldb_func:"}, +}; + +TEST_P(TestTypeSystemClangAsmLabel, DeclGetMangledName) { + const auto &[mangled, expected] = GetParam(); + + CompilerType int_type = m_ast->GetBasicType(lldb::eBasicTypeInt); + clang::TranslationUnitDecl *TU = m_ast->GetTranslationUnitDecl(); + + // Prepare the declarations/types we need for the template. + CompilerType clang_type = m_ast->CreateFunctionType(int_type, {}, false, 0U); + FunctionDecl *func = m_ast->CreateFunctionDeclaration( + TU, OptionalClangModuleID(), "foo", clang_type, StorageClass::SC_None, + false, /*asm_label=*/mangled); + + ASSERT_EQ(llvm::GlobalValue::dropLLVMManglingEscape( + m_ast->DeclGetMangledName(func).GetStringRef()), + expected); +} + +INSTANTIATE_TEST_SUITE_P(AsmLabelTests, TestTypeSystemClangAsmLabel, + testing::ValuesIn(g_asm_label_test_cases)); |