Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Enable demangling of C++ symbols in the stacktrace
Browse files Browse the repository at this point in the history
This allows you to view demangled and pretty printed C++ symbols
in the stacktrace.
Is especially useful considering that D is getting more and more support for C++.

Now the C++ symbols in the stacktrace will be printed like this:
	test.d:7 [C++] Test<int>::SomeName(int, long, int) [0x55711efee36f]
        test.d:18 [C++] testcpp() [0x557228e17978]

Signed-off-by: Ernesto Castellotti <[email protected]>
  • Loading branch information
Ernesto Castellotti committed Apr 1, 2020
1 parent 2a0cf25 commit 18fd3ec
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 2 deletions.
51 changes: 51 additions & 0 deletions changelog/cpptrace.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
Added experimental `C++` symbol demanling in the stacktrace

This feature is availableby passing the following switches to your executable (not to the compiler):
- `--DRT-cpptrace=enable:y`: Enable demangling of C++ symbols
- `--DRT-cpptrace=prefix:<your_prefix>`: Change the prefix preceding the demangled C++ name (by default would be `[C++]`)
- `--DRT-cpptrace=noprefix:n`: Disable adding the prefix to C ++ demangle names

To use this function in the Posix platform you will need to link your executable to the phobos shared library, compile the program by passing -defaultlib=libphobos2.so to DMD.

Example:

```
module cpp_trace.d

extern(C++) void f1()
{
throw new Exception("exception");
}

void main()
{
try
{
f1();
}
catch (Exception e)
{
import core.stdc.stdio;
auto str = e.toString();
printf("%.*s\n", cast(int)str.length, str.ptr);
}
}
```

If you run the executable with the following switches `./<executable> --DRT-cpptrace=enable:y`, you would get this output:

```
object.Exception@cpp_trace.d(3): exception
----------------
src/cpp_trace.d:5 [C++] f1() [ADDR]
src/cpp_trace.d:12 _Dmain [ADDR]
```

Instead, if you run the executable with the following switches `./<executable> --DRT-cpptrace=enable:y --DRT-cpptrace=prefix:CPP`, you would get this output:

```
object.Exception@cpp_trace.d(5): exception
----------------
src/cpp_trace.d:5 [CPP] f1() [ADDR]
src/cpp_trace.d:12 _Dmain [ADDR]
```
1 change: 1 addition & 0 deletions mak/COPY
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ COPY=\
$(IMPDIR)\core\internal\attributes.d \
$(IMPDIR)\core\internal\convert.d \
$(IMPDIR)\core\internal\cppdemangle.d \
$(IMPDIR)\core\internal\cpptrace.d \
$(IMPDIR)\core\internal\dassert.d \
$(IMPDIR)\core\internal\destruction.d \
$(IMPDIR)\core\internal\entrypoint.d \
Expand Down
1 change: 1 addition & 0 deletions mak/SRCS
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ SRCS=\
src\core\internal\attributes.d \
src\core\internal\convert.d \
src\core\internal\cppdemangle.d \
src\core\internal\cpptrace.d \
src\core\internal\dassert.d \
src\core\internal\destruction.d \
src\core\internal\entrypoint.d \
Expand Down
3 changes: 3 additions & 0 deletions mak/WINDOWS
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ $(IMPDIR)\core\internal\convert.d : src\core\internal\convert.d
$(IMPDIR)\core\internal\cppdemangle.d : src\core\internal\cppdemangle.d
copy $** $@

$(IMPDIR)\core\internal\cpptrace.d : src\core\internal\cpptrace.d
copy $** $@

$(IMPDIR)\core\internal\dassert.d : src\core\internal\dassert.d
copy $** $@

Expand Down
3 changes: 3 additions & 0 deletions posix.mak
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,9 @@ $(ROOT)/unittest/% : $(ROOT)/unittest/test_runner

$(addsuffix /.run,$(filter-out test/shared,$(ADDITIONAL_TESTS))): $(DRUNTIME)
test/shared/.run: $(DRUNTIMESO)
ifeq (1,$(SHARED))
test/exceptions/.run: $(DRUNTIMESO) $(DRUNTIME)
endif

test/%/.run: test/%/Makefile $(DMD)
$(QUIET)$(MAKE) -C test/$* MODEL=$(MODEL) OS=$(OS) DMD=$(abspath $(DMD)) BUILD=$(BUILD) \
Expand Down
119 changes: 119 additions & 0 deletions src/core/internal/cpptrace.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/**
* This module provides the demangling of C++ symbols for stacktrace
*
* Copyright: Copyright © 2020, The D Language Foundation
* License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Ernesto Castellotti
* Source: $(DRUNTIMESRC core/internal/_cppdemangle.d)
*/
module core.internal.cpptrace;

import core.internal.cppdemangle : CPPDemangle, CPPDemangleStatus;
import core.stdc.stdio : fprintf, stderr;
import core.stdc.stdlib : exit;

/**
* Demangles C++ mangled names passing the runtime options to cppdemangle.
*
* If it is not a C++ mangled name or cppdemangle is not supported by your platform, the original mangled C++ name will be returned.
* The optional destination buffer and return will be contains the same string if the demangle is successful.
* This function is used to demangle C++ symbols in the stacktrace.
*
* Params:
* buf = The string to demangle.
* dst = The destination buffer, if the size of the destination buffer is <= the size of cppdemangle output the return string would be incomplete.
*
* Returns:
* The demangled name or the original string if the name is not a mangled C++ name.
*/
char[] demangleCppTrace(const(char)[] buf, char[] dst)
{
import core.internal.cppdemangle : copyResult;

if (!CPPTrace.config.enable)
{
return copyResult(buf, dst);
}

return CPPTrace.instance.cppdemangle(buf, dst, !CPPTrace.config.noprefix, CPPTrace.config.prefix);
}

private struct CPPTrace
{
__gshared CPPDemangle instance;
__gshared Config config;

static this()
{
import core.internal.parseoptions : initConfigOptions;
initConfigOptions(config, "cpptrace");

version (Posix)
{
version (Shared)
{
// OK! CPPDemangling may be supported
}
else
{
if (config.enable)
{
fprintf(stderr, "C++ demangling is only supported if phobos is dynamically linked. Recompile the program by passing -defaultlib=libphobos2.so to DMD\n");
exit(1);
assert(0);
}
}
}

if (config.enable)
{
auto result = CPPDemangle.inizialize();

final switch (result)
{
case CPPDemangleStatus.INIZIALIZED:
{
instance = CPPDemangle.instance();
return;
}

case CPPDemangleStatus.LOAD_ERROR:
{
version (Posix)
{
fprintf(stderr, "The C++ library for the C++ demangle could not be loaded with dlopen. Please disable the option for C++ demangling.\n");
}
else version (Windows)
{
fprintf(stderr, "The Debug Help Library could not be loaded. Please disable the option for C++ demangling.\n");
}

exit(1);
assert(0);
}

case CPPDemangleStatus.SYMBOL_ERROR:
{
version (Posix)
{
fprintf(stderr, "The __cxa_demangle symbol was not found in the C++ standard library (maybe it's not compatible). Please disable the option for C++ demangling.\n");
}
else version (Windows)
{
fprintf(stderr, "The UnDecorateSymbolName symbol was not found in the Debug Help Library (maybe it's not compatible). Please disable the option for C++ demangling.\n");
}

exit(1);
assert(0);
}
}
}
}
}

private struct Config
{
bool enable;
bool noprefix;
string prefix = "[C++]";
}
10 changes: 10 additions & 0 deletions src/core/runtime.d
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,16 @@ private:

auto sym = demangle(buf[symBeg .. symEnd], fixbuf[symBeg .. $]);

version (Shared)
{
import core.internal.cpptrace;

if (sym == buf[symBeg .. symEnd]) // Retry with demangleCppTrace
{
sym = demangleCppTrace(buf[symBeg .. symEnd], fixbuf[symBeg .. $]);
}
}

if (sym.ptr !is fixbuf.ptr + symBeg)
{
// demangle reallocated the buffer, copy the symbol to fixbuf
Expand Down
10 changes: 9 additions & 1 deletion src/core/sys/windows/stacktrace.d
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ version (Windows):

import core.demangle;
import core.runtime;
import core.internal.cpptrace;
import core.stdc.stdlib;
import core.stdc.string;
import core.sys.windows.dbghelp;
Expand Down Expand Up @@ -294,7 +295,14 @@ private:
size_t decodeIndex = 0;
tempSymName = decodeDmdString(tempSymName, decodeIndex);
}
res ~= demangle(tempSymName, demangleBuf);
auto demangledName = demangle(tempSymName, demangleBuf);

if (demangledName == tempSymName) // Retry with demangleCppTrace
{
demangledName = demangleCppTrace(tempSymName, demangleBuf);
}

res ~= demangledName;
return res;
}

Expand Down
16 changes: 15 additions & 1 deletion src/rt/backtrace/dwarf.d
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,21 @@ const(char)[] getDemangledSymbol(const(char)[] btSymbol, return ref char[1024] b
{
import core.demangle;
const mangledName = getMangledSymbolName(btSymbol);
return !mangledName.length ? buffer[0..0] : demangle(mangledName, buffer[]);
if (!mangledName.length) return buffer[0..0];

auto demangledName = demangle(mangledName, buffer[]);

version (Shared)
{
import core.internal.cpptrace;

if (demangledName == mangledName) // Retry with demangleCppTrace
{
demangledName = demangleCppTrace(mangledName, buffer[]);
}
}

return demangledName;
}

T read(T)(ref const(ubyte)[] buffer) @nogc nothrow
Expand Down
26 changes: 26 additions & 0 deletions test/exceptions/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,40 @@ include ../common.mak
TESTS=stderr_msg unittest_assert invalid_memory_operation unknown_gc static_dtor \
future_message refcounted rt_trap_exceptions_drt catch_in_finally

ifeq ($(shell test -e $(DRUNTIMESO) && echo 0), 0)
SHARED=1
endif

ifeq ($(OS)-$(BUILD),linux-debug)
TESTS+=line_trace long_backtrace_trunc rt_trap_exceptions
LINE_TRACE_DFLAGS:=-L--export-dynamic
ifeq ($(SHARED), 1)
TESTS+=cpp_trace
endif
endif
ifeq ($(OS),linux)
TESTS+=rt_trap_exceptions_drt_gdb
endif
ifeq ($(OS)-$(BUILD),freebsd-debug)
TESTS+=line_trace long_backtrace_trunc
LINE_TRACE_DFLAGS:=-L--export-dynamic
ifeq (SHARED, 1)
TESTS+=cpp_trace
endif
endif
ifeq ($(OS)-$(BUILD),dragonflybsd-debug)
TESTS+=line_trace long_backtrace_trunc
LINE_TRACE_DFLAGS:=-L--export-dynamic
ifeq (SHARED, 1)
TESTS+=cpp_trace
endif
endif
ifeq ($(OS)-$(BUILD),osx-debug)
TESTS+=line_trace long_backtrace_trunc
LINE_TRACE_DFLAGS:=
ifeq (SHARED, 1)
TESTS+=cpp_trace
endif
endif

ifeq ($(BUILD),debug)
Expand Down Expand Up @@ -56,6 +72,14 @@ $(ROOT)/chain.done: $(ROOT)/chain
@rm -f $(ROOT)/chain.output
@touch $@

$(ROOT)/cpp_trace.done: $(ROOT)/cpp_trace
@echo Testing cpp_trace
$(QUIET)$(TIMELIMIT)$(ROOT)/cpp_trace $(RUN_ARGS) > $(ROOT)/cpp_trace.output
# Use sed to canonicalize cpp_trace.output and compare against expected output in cpp_trace.exp
$(QUIET)$(SED) "s/\[0x[0-9a-f]*\]/\[ADDR\]/g; s/scope //g; s/Nl//g" $(ROOT)/cpp_trace.output | $(DIFF) cpp_trace.exp -
@rm -f $(ROOT)/cpp_trace.output
@touch $@

$(ROOT)/stderr_msg.done: STDERR_EXP="stderr_msg msg"
$(ROOT)/unittest_assert.done: STDERR_EXP="unittest_assert msg"
$(ROOT)/invalid_memory_operation.done: STDERR_EXP="InvalidMemoryOperationError"
Expand Down Expand Up @@ -99,6 +123,8 @@ $(ROOT)/line_trace: DFLAGS+=$(LINE_TRACE_DFLAGS)
$(ROOT)/rt_trap_exceptions_drt: DFLAGS+=-g
$(ROOT)/assert_fail: DFLAGS+=-checkaction=context
$(ROOT)/refcounted: DFLAGS+=-dip1008
$(ROOT)/cpp_trace.done: RUN_ARGS="--DRT-cpptrace=enable:y"
$(ROOT)/cpp_trace: DFLAGS+=-L$(DRUNTIMESO) $(LINE_TRACE_DFLAGS)

$(ROOT)/%: $(SRC)/%.d $(DMD) $(DRUNTIME)
$(QUIET)$(DMD) $(DFLAGS) -of$@ $<
Expand Down
12 changes: 12 additions & 0 deletions test/exceptions/cpp_trace.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object.Exception@src/cpp_trace.d(40): exception
----------------
src/cpp_trace.d:40 [C++] f1() [ADDR]
src/cpp_trace.d:5 _Dmain [ADDR]
object.Exception@src/cpp_trace.d(47): exception
----------------
src/cpp_trace.d:47 [C++] S1::f1() [ADDR]
src/cpp_trace.d:16 _Dmain [ADDR]
object.Exception@src/cpp_trace.d(57): exception
----------------
src/cpp_trace.d:57 [C++] C1<unsigned char>::f1(unsigned char, unsigned char*, unsigned char&) [ADDR]
src/cpp_trace.d:28 _Dmain [ADDR]
Loading

0 comments on commit 18fd3ec

Please sign in to comment.