Commit 8897adf8 authored by Mathias LANG's avatar Mathias LANG Committed by Rasmus Thomsen

community/dmd: update to 2.095.0 & use libunwind

The vfork() definition in the patch has been upstreamed in v2.095.0,
so the diff can be removed.
This also brings in the support for libunwind which is in the process
of being upstreamed (the first few commits are already in master),
and is necessary as libexecinfo is quite buggy and leads to a lot
of test failures/crashes.
The upstream code supports both llvm-libunwind and libunwind,
but for the sake of coverage this goes with the former,
as the later will probably be covered by LDC.
parent 8ed91692
Pipeline #66814 failed with stages
in 40 seconds
From be152f1b496517b10a590573ca7694924c2c3175 Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Sat, 2 Jan 2021 11:40:21 +0900
Subject: Encapsulate backtrace-buffering into its own struct
This makes it reusable by other backtrace providers,
and just generally makes the code easier to follow.
---
src/core/internal/backtrace/dwarf.d | 73 +++++++++++++++++++----------
1 file changed, 48 insertions(+), 25 deletions(-)
diff --git a/src/core/internal/backtrace/dwarf.d b/src/core/internal/backtrace/dwarf.d
index dc1425a7d..ba059db30 100644
--- a/druntime/src/core/internal/backtrace/dwarf.d
+++ b/druntime/src/core/internal/backtrace/dwarf.d
@@ -183,34 +183,13 @@ int traceHandlerOpApplyImpl(const(void*)[] callstack, scope int delegate(ref siz
if (debugLineSectionData)
resolveAddresses(debugLineSectionData, locations[], image.baseAddress);
- char[1536] buffer = void;
- size_t bufferLength;
- scope sink = (scope const char[] data) {
- // We cannot write anymore
- if (bufferLength > buffer.length)
- return;
-
- if (bufferLength + data.length > buffer.length)
- {
- buffer[bufferLength .. $] = data[0 .. buffer.length - bufferLength];
- buffer[$ - 3 .. $] = "...";
- // +1 is a marker for the '...', otherwise if the symbol
- // name was to exactly fill the buffer,
- // we'd discard anything else without printing the '...'.
- bufferLength = buffer.length + 1;
- return;
- }
-
- buffer[bufferLength .. bufferLength + data.length] = data;
- bufferLength += data.length;
- };
-
+ TraceInfoBuffer buffer;
foreach (idx, const ref loc; locations)
{
- bufferLength = 0;
- loc.toString(sink);
+ buffer.reset();
+ loc.toString(&buffer.put);
- auto lvalue = buffer[0 .. bufferLength > $ ? $ : bufferLength];
+ auto lvalue = buffer[];
if (auto ret = dg(idx, lvalue))
return ret;
@@ -226,6 +205,50 @@ int traceHandlerOpApplyImpl(const(void*)[] callstack, scope int delegate(ref siz
: processCallstack(null);
}
+struct TraceInfoBuffer
+{
+ private char[1536] buf = void;
+ private size_t position;
+
+ // BUG: https://issues.dlang.org/show_bug.cgi?id=21285
+ @safe pure nothrow @nogc
+ {
+ ///
+ inout(char)[] opSlice() inout return
+ {
+ return this.buf[0 .. this.position > $ ? $ : this.position];
+ }
+
+ ///
+ void reset()
+ {
+ this.position = 0;
+ }
+ }
+
+ /// Used as `sink` argument to `Location.toString`
+ void put(scope const char[] data)
+ {
+ // We cannot write anymore
+ if (this.position > this.buf.length)
+ return;
+
+ if (this.position + data.length > this.buf.length)
+ {
+ this.buf[this.position .. $] = data[0 .. this.buf.length - this.position];
+ this.buf[$ - 3 .. $] = "...";
+ // +1 is a marker for the '...', otherwise if the symbol
+ // name was to exactly fill the buffer,
+ // we'd discard anything else without printing the '...'.
+ this.position = this.buf.length + 1;
+ return;
+ }
+
+ this.buf[this.position .. this.position + data.length] = data;
+ this.position += data.length;
+ }
+}
+
private:
// the lifetime of the Location data is bound to the lifetime of debugLineSectionData
--
2.24.3 (Apple Git-128)
From f594fe5b5d32acdad92a8fd100ffaf7fac6a11aa Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Sat, 2 Jan 2021 14:29:12 +0900
Subject: Separate backtrace's processCallback into its own function
So that it can be called from another overload of `traceHandlerOpApplyImpl`.
---
src/core/internal/backtrace/dwarf.d | 62 +++++++++++++++--------------
1 file changed, 32 insertions(+), 30 deletions(-)
diff --git a/src/core/internal/backtrace/dwarf.d b/src/core/internal/backtrace/dwarf.d
index ba059db30..d443319f5 100644
--- a/druntime/src/core/internal/backtrace/dwarf.d
+++ b/druntime/src/core/internal/backtrace/dwarf.d
@@ -169,40 +169,19 @@ int traceHandlerOpApplyImpl(const(void*)[] callstack, scope int delegate(ref siz
auto image = Image.openSelf();
- int processCallstack(const(ubyte)[] debugLineSectionData)
+ // find address -> file, line mapping using dwarf debug_line
+ Array!Location locations;
+ locations.length = callstack.length;
+ foreach (size_t i; 0 .. callstack.length)
{
- // find address -> file, line mapping using dwarf debug_line
- Array!Location locations;
- locations.length = callstack.length;
- foreach (size_t i; 0 .. callstack.length)
- {
- locations[i].address = callstack[i];
- locations[i].procedure = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
- }
-
- if (debugLineSectionData)
- resolveAddresses(debugLineSectionData, locations[], image.baseAddress);
-
- TraceInfoBuffer buffer;
- foreach (idx, const ref loc; locations)
- {
- buffer.reset();
- loc.toString(&buffer.put);
-
- auto lvalue = buffer[];
- if (auto ret = dg(idx, lvalue))
- return ret;
-
- if (loc.procedure == "_Dmain")
- break;
- }
-
- return 0;
+ locations[i].address = callstack[i];
+ locations[i].procedure = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
}
return image.isValid
- ? image.processDebugLineSectionData(&processCallstack)
- : processCallstack(null);
+ ? image.processDebugLineSectionData(
+ (line) => locations[].processCallstack(line, image.baseAddress, dg))
+ : locations[].processCallstack(null, image.baseAddress, dg);
}
struct TraceInfoBuffer
@@ -251,6 +230,29 @@ struct TraceInfoBuffer
private:
+int processCallstack(Location[] locations, const(ubyte)[] debugLineSectionData,
+ size_t baseAddress, scope int delegate(ref size_t, ref const(char[])) dg)
+{
+ if (debugLineSectionData)
+ resolveAddresses(debugLineSectionData, locations, baseAddress);
+
+ TraceInfoBuffer buffer;
+ foreach (idx, const ref loc; locations)
+ {
+ buffer.reset();
+ loc.toString(&buffer.put);
+
+ auto lvalue = buffer[];
+ if (auto ret = dg(idx, lvalue))
+ return ret;
+
+ if (loc.procedure == "_Dmain")
+ break;
+ }
+
+ return 0;
+}
+
// the lifetime of the Location data is bound to the lifetime of debugLineSectionData
void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow
{
--
2.24.3 (Apple Git-128)
From 1258ef6469ae3544e639f838e252a4af6853c44b Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Mon, 3 Aug 2020 18:00:07 +0900
Subject: backtrace: Add documentation for resolveAddresses
---
src/core/internal/backtrace/dwarf.d | 19 ++++++++++++++++++-
1 file changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/core/internal/backtrace/dwarf.d b/src/core/internal/backtrace/dwarf.d
index d443319f5..e82d4fd73 100644
--- a/druntime/src/core/internal/backtrace/dwarf.d
+++ b/druntime/src/core/internal/backtrace/dwarf.d
@@ -253,7 +253,24 @@ int processCallstack(Location[] locations, const(ubyte)[] debugLineSectionData,
return 0;
}
-// the lifetime of the Location data is bound to the lifetime of debugLineSectionData
+/**
+ * Resolve the addresses of `locations` using `debugLineSectionData`
+ *
+ * Runs the DWARF state machine on `debugLineSectionData`,
+ * assuming it represents a debugging program describing the addresses
+ * in a continous and increasing manner.
+ *
+ * After this function successfully completes, `locations` will contains
+ * file / lines informations.
+ *
+ * Note that the lifetime of the `Location` data is bound to the lifetime
+ * of `debugLineSectionData`.
+ *
+ * Params:
+ * debugLineSectionData = A DWARF program to feed the state machine
+ * locations = The locations to resolve
+ * baseAddress = The offset to apply to every address
+ */
void resolveAddresses(const(ubyte)[] debugLineSectionData, Location[] locations, size_t baseAddress) @nogc nothrow
{
debug(DwarfDebugMachine) import core.stdc.stdio;
--
2.24.3 (Apple Git-128)
From 553a557270a9373a48bc9bde7f2bcb562d38d9ba Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Mon, 3 Aug 2020 22:36:13 +0900
Subject: Trivial: (Backtrace) Use if condition instead of ternary
---
src/core/internal/backtrace/dwarf.d | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/src/core/internal/backtrace/dwarf.d b/src/core/internal/backtrace/dwarf.d
index e82d4fd73..dfecc7eab 100644
--- a/druntime/src/core/internal/backtrace/dwarf.d
+++ b/druntime/src/core/internal/backtrace/dwarf.d
@@ -178,10 +178,11 @@ int traceHandlerOpApplyImpl(const(void*)[] callstack, scope int delegate(ref siz
locations[i].procedure = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
}
- return image.isValid
- ? image.processDebugLineSectionData(
- (line) => locations[].processCallstack(line, image.baseAddress, dg))
- : locations[].processCallstack(null, image.baseAddress, dg);
+ if (!image.isValid())
+ return locations[].processCallstack(null, image.baseAddress, dg);
+
+ return image.processDebugLineSectionData(
+ (line) => locations[].processCallstack(line, image.baseAddress, dg));
}
struct TraceInfoBuffer
--
2.24.3 (Apple Git-128)
From 9d2ac4c4035083b94f2517fccfd13722b637f453 Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Mon, 4 Jan 2021 22:08:36 +0900
Subject: Only encapsulate the call to backtrace in 'hasExecInfo'
The execinfo functionality really only concerns itself with backtrace,
which provide addresses and function names.
However, the general ability to iterate over locations and fill them
with file/line informations, as well as the utilities tied to it
(e.g. TraceInfoBuffer) can be used by other implementations.
---
src/core/internal/backtrace/dwarf.d | 42 +++++++++++++++--------------
1 file changed, 22 insertions(+), 20 deletions(-)
diff --git a/src/core/internal/backtrace/dwarf.d b/src/core/internal/backtrace/dwarf.d
index dfecc7eab..8f082c783 100644
--- a/druntime/src/core/internal/backtrace/dwarf.d
+++ b/druntime/src/core/internal/backtrace/dwarf.d
@@ -52,8 +52,6 @@ module core.internal.backtrace.dwarf;
import core.internal.execinfo;
import core.internal.string;
-static if (hasExecinfo):
-
version (OSX)
version = Darwin;
else version (iOS)
@@ -159,30 +157,34 @@ struct Location
}
}
-int traceHandlerOpApplyImpl(const(void*)[] callstack, scope int delegate(ref size_t, ref const(char[])) dg)
+static if (hasExecinfo)
{
- import core.stdc.stdio : snprintf;
- import core.sys.posix.stdlib : free;
+ int traceHandlerOpApplyImpl(const(void*)[] callstack,
+ scope int delegate(ref size_t, ref const(char[])) dg)
+ {
+ import core.stdc.stdio : snprintf;
+ import core.sys.posix.stdlib : free;
- const char** frameList = backtrace_symbols(callstack.ptr, cast(int) callstack.length);
- scope(exit) free(cast(void*) frameList);
+ const char** frameList = backtrace_symbols(callstack.ptr, cast(int) callstack.length);
+ scope(exit) free(cast(void*) frameList);
- auto image = Image.openSelf();
+ auto image = Image.openSelf();
- // find address -> file, line mapping using dwarf debug_line
- Array!Location locations;
- locations.length = callstack.length;
- foreach (size_t i; 0 .. callstack.length)
- {
- locations[i].address = callstack[i];
- locations[i].procedure = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
- }
+ // find address -> file, line mapping using dwarf debug_line
+ Array!Location locations;
+ locations.length = callstack.length;
+ foreach (size_t i; 0 .. callstack.length)
+ {
+ locations[i].address = callstack[i];
+ locations[i].procedure = getMangledSymbolName(frameList[i][0 .. strlen(frameList[i])]);
+ }
- if (!image.isValid())
- return locations[].processCallstack(null, image.baseAddress, dg);
+ if (!image.isValid())
+ return locations[].processCallstack(null, image.baseAddress, dg);
- return image.processDebugLineSectionData(
- (line) => locations[].processCallstack(line, image.baseAddress, dg));
+ return image.processDebugLineSectionData(
+ (line) => locations[].processCallstack(line, image.baseAddress, dg));
+ }
}
struct TraceInfoBuffer
--
2.24.3 (Apple Git-128)
From e0698ab30245a2b4a1ef8da904c03e5336e0746b Mon Sep 17 00:00:00 2001
From: Geod24 <pro.mathias.lang@gmail.com>
Date: Tue, 1 Dec 2020 17:11:51 +0100
Subject: Add internal bindings for libunwind
As mentioned in the module DDOC, those bindings are only intended
to be used internally for the backtracing code,
hence only a couple functions and the related types were added.
---
mak/COPY | 1 +
mak/DOCS | 1 +
mak/SRCS | 1 +
src/core/internal/backtrace/libunwind.d | 281 ++++++++++++++++++++++++
4 files changed, 284 insertions(+)
create mode 100644 src/core/internal/backtrace/libunwind.d
diff --git a/mak/COPY b/mak/COPY
index 6e70fafcb..fa97e1207 100644
--- a/druntime/mak/COPY
+++ b/druntime/mak/COPY
@@ -53,6 +53,7 @@ COPY=\
\
$(IMPDIR)\core\internal\backtrace\dwarf.d \
$(IMPDIR)\core\internal\backtrace\elf.d \
+ $(IMPDIR)\core\internal\backtrace\libunwind.d \
$(IMPDIR)\core\internal\backtrace\macho.d \
\
$(IMPDIR)\core\internal\elf\dl.d \
diff --git a/mak/DOCS b/mak/DOCS
index 164464815..90b16a19e 100644
--- a/druntime/mak/DOCS
+++ b/druntime/mak/DOCS
@@ -87,6 +87,7 @@ DOCS=\
\
$(DOCDIR)\core_internal_backtrace_dwarf.html \
$(DOCDIR)\core_internal_backtrace_elf.html \
+ $(DOCDIR)\core_internal_backtrace_libunwind.html \
$(DOCDIR)\core_internal_backtrace_macho.html \
\
$(DOCDIR)\core_internal_container_array.html \
diff --git a/mak/SRCS b/mak/SRCS
index 9d08ddad5..05cb923e1 100644
--- a/druntime/mak/SRCS
+++ b/druntime/mak/SRCS
@@ -53,6 +53,7 @@ SRCS=\
\
src\core\internal\backtrace\dwarf.d \
src\core\internal\backtrace\elf.d \
+ src\core\internal\backtrace\libunwind.d \
src\core\internal\backtrace\macho.d \
\
src\core\internal\container\array.d \
diff --git a/src/core/internal/backtrace/libunwind.d b/src/core/internal/backtrace/libunwind.d
new file mode 100644
index 000000000..fff25bb11
--- /dev/null
+++ b/druntime/src/core/internal/backtrace/libunwind.d
@@ -0,0 +1,281 @@
+/**
+ * Basic D language bindings for libunwind
+ *
+ * There are two available libunwind: The "upstream" one, inherited
+ * from HP, which is maintained as a GNU project,
+ * and the LLVM one, part of llvm-project, and the default on Mac OSX.
+ *
+ * They offer a similar interface for C[++] users, but this is based on
+ * defines and other header attributes which we cannot reproduce in D.
+ * Thus, this header contains bindings for both libraries, and the right
+ * version needs to be selected when compiling Druntime.
+ *
+ * Authors: Mathias 'Geod24' Lang
+ * Copyright: D Language Foundation - 2020
+ * See_Also:
+ * - https://www.nongnu.org/libunwind/man/libunwind(3).html
+ * - https://clang.llvm.org/docs/Toolchain.html#unwind-library
+ */
+module core.internal.backtrace.libunwind;
+
+// Libunwind supports Windows as well, but we currently use a different
+// mechanism for Windows, so the bindings haven't been brought in yet.
+version (Posix):
+
+import core.stdc.inttypes;
+
+version (OSX)
+ version = Darwin;
+else version (iOS)
+ version = Darwin;
+else version (TVOS)
+ version = Darwin;
+else version (WatchOS)
+ version = Darwin;
+
+version (Darwin)
+ version = DRuntime_Use_LLVM_Libunwind;
+
+extern(C):
+@system:
+@nogc:
+nothrow:
+
+/*
+ * Bindings for libunwind.h
+ */
+alias unw_word_t = uintptr_t;
+
+///
+struct unw_context_t
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ ulong[_LIBUNWIND_CONTEXT_SIZE] data = void;
+ else
+ unw_word_t[UNW_TDEP_CURSOR_LEN] opaque = void;
+}
+
+///
+struct unw_cursor_t
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ ulong[_LIBUNWIND_CURSOR_SIZE] data = void;
+ else
+ unw_tdep_context_t opaque = void;
+}
+
+///
+struct unw_proc_info_t
+{
+ unw_word_t start_ip; /* start address of function */
+ unw_word_t end_ip; /* address after end of function */
+ unw_word_t lsda; /* address of language specific data area, */
+ /* or zero if not used */
+ unw_word_t handler; /* personality routine, or zero if not used */
+ unw_word_t gp; /* not used */
+ unw_word_t flags; /* not used */
+ uint format; /* compact unwind encoding, or zero if none */
+ uint unwind_info_size; /* size of DWARF unwind info, or zero if none */
+ // Note: It's a `void*` with LLVM and a `unw_word_t` with upstream
+ unw_word_t unwind_info; /* address of DWARF unwind info, or zero */
+ // Note: upstream might not have this member at all, or it might be a single
+ // byte, however we never pass an array of this type, so this is safe to
+ // just use the bigger (LLVM's) value.
+ unw_word_t extra; /* mach_header of mach-o image containing func */
+}
+
+/// Initialize the context at the current call site
+int unw_getcontext(unw_context_t*);
+/// Initialize a cursor at the call site
+int unw_init_local(unw_cursor_t*, unw_context_t*);
+/// Goes one level up in the call chain
+int unw_step(unw_cursor_t*);
+/// Get infos about the current procedure (function)
+int unw_get_proc_info(unw_cursor_t*, unw_proc_info_t*);
+/// Get the name of the current procedure (function)
+int unw_get_proc_name(unw_cursor_t*, char*, size_t, unw_word_t*);
+
+private:
+
+// The API between libunwind and llvm-libunwind is almost the same,
+// at least for our use case, and only the struct size change,
+// so handle the difference here.
+// Upstream: https://github.com/libunwind/libunwind/tree/master/include
+// LLVM: https://github.com/llvm/llvm-project/blob/20c926e0797e074bfb946d2c8ce002888ebc2bcd/libunwind/include/__libunwind_config.h#L29-L141
+version (X86)
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ {
+ enum _LIBUNWIND_CONTEXT_SIZE = 8;
+ enum _LIBUNWIND_CURSOR_SIZE = 15;
+ }
+ else
+ {
+ import core.sys.posix.ucontext;
+ alias unw_tdep_context_t = ucontext_t;
+ enum UNW_TDEP_CURSOR_LEN = 127;
+ }
+}
+else version (X86_64)
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ {
+ version (Win64)
+ {
+ enum _LIBUNWIND_CONTEXT_SIZE = 54;
+// # ifdef __SEH__
+// # define _LIBUNWIND_CURSOR_SIZE 204
+ enum _LIBUNWIND_CURSOR_SIZE = 66;
+ } else {
+ enum _LIBUNWIND_CONTEXT_SIZE = 21;
+ enum _LIBUNWIND_CURSOR_SIZE = 33;
+ }
+ }
+ else
+ {
+ import core.sys.posix.ucontext;
+ alias unw_tdep_context_t = ucontext_t;
+ enum UNW_TDEP_CURSOR_LEN = 127;
+ }
+}
+else version (PPC64)
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ {
+ enum _LIBUNWIND_CONTEXT_SIZE = 167;
+ enum _LIBUNWIND_CURSOR_SIZE = 179;
+ }
+ else
+ {
+ import core.sys.posix.ucontext;
+ alias unw_tdep_context_t = ucontext_t;
+ enum UNW_TDEP_CURSOR_LEN = 280;
+ }
+}
+else version (PPC)
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ {
+ enum _LIBUNWIND_CONTEXT_SIZE = 117;
+ enum _LIBUNWIND_CURSOR_SIZE = 124;
+ }
+ else
+ {
+ import core.sys.posix.ucontext;
+ alias unw_tdep_context_t = ucontext_t;
+ enum UNW_TDEP_CURSOR_LEN = 280;
+ }
+}
+else version (AArch64)
+{
+ version (DRuntime_Use_LLVM_Libunwind)
+ {
+ enum _LIBUNWIND_CONTEXT_SIZE = 66;
+// # if defined(__SEH__)
+// # define _LIBUNWIND_CURSOR_SIZE 164
+ enum _LIBUNWIND_CURSOR_SIZE = 78;
+ }
+ else
+ {
+ enum UNW_TDEP_CURSOR_LEN = 250;
+
+ version (linux)
+ {
+ import core.sys.posix.signal : sigset_t, stack_t;
+
+ // libunwind has some special tweaking to reduce
+ // the size of ucontext_t on Linux.
+ struct unw_sigcontext
+ {
+ ulong fault_address;
+ ulong[31] regs;
+ ulong sp;
+ ulong pc;
+ ulong pstate;
+ align(16) ubyte[(66 * 8)] reserved;
+ }
+
+ struct unw_tdep_context_t
+ {
+ c_ulong uc_flags;
+ void* uc_link;
+ stack_t uc_stack;
+ sigset_t uc_sigmask;