Commit a6a3dabc authored by Ben Gamari's avatar Ben Gamari Committed by Ben Gamari

Libdw: Add libdw-based stack unwinding

This adds basic support to the RTS for DWARF-assisted unwinding of the
Haskell and C stack via libdw. This only adds the infrastructure;
consumers of this functionality will be introduced in future diffs.

Currently we are carrying the initial register collection code in
Libdw.c but this will eventually make its way upstream to libdw.

Test Plan: See future patches

Reviewers: Tarrasch, scpmw, austin, simonmar

Reviewed By: austin, simonmar

Subscribers: simonmar, thomie, erikd

Differential Revision: https://phabricator.haskell.org/D1196

GHC Trac Issues: #10656
parent fff02548
......@@ -102,6 +102,8 @@ endif
@echo 'cGhcWithSMP = "$(GhcWithSMP)"' >> $@
@echo 'cGhcRTSWays :: String' >> $@
@echo 'cGhcRTSWays = "$(GhcRTSWays)"' >> $@
@echo 'cGhcRtsWithLibdw :: String' >> $@
@echo 'cGhcRtsWithLibdw = "$(GhcRtsWithLibdw)"' >> $@
@echo 'cGhcEnableTablesNextToCode :: String' >> $@
@echo 'cGhcEnableTablesNextToCode = "$(GhcEnableTablesNextToCode)"' >> $@
@echo 'cLeadingUnderscore :: String' >> $@
......
......@@ -4073,6 +4073,7 @@ compilerInfo dflags
("Support SMP", cGhcWithSMP),
("Tables next to code", cGhcEnableTablesNextToCode),
("RTS ways", cGhcRTSWays),
("RTS expects libdw", cGhcRtsWithLibdw),
("Support dynamic-too", if isWindows then "NO" else "YES"),
("Support parallel --make", "YES"),
("Support reexported-modules", "YES"),
......
......@@ -1095,6 +1095,9 @@ if test "$use_large_address_space" = "yes" ; then
AC_DEFINE([USE_LARGE_ADDRESS_SPACE], [1], [Enable single heap address space support])
fi
AC_CHECK_LIB(dw, dwfl_begin, [HaveLibdw=YES], [HaveLibdw=NO])
AC_SUBST(HaveLibdw)
if test -n "$SPHINXBUILD"; then
BUILD_MAN=YES
BUILD_SPHINX_HTML=YES
......
......@@ -91,6 +91,11 @@ dnl --------------------------------------------------------------
FIND_LD([LdCmd])
AC_SUBST([LdCmd])
dnl ** Have libdw?
dnl --------------------------------------------------------------
AC_CHECK_LIB(dw, dwfl_begin, [HaveLibdw=YES], [HaveLibdw=NO])
AC_SUBST(HaveLibdw)
FP_GCC_VERSION
AC_PROG_CPP
......
......@@ -238,6 +238,7 @@ INLINE_HEADER Time fsecondsToTime (double t)
#include "rts/PrimFloat.h"
#include "rts/Main.h"
#include "rts/StaticPtrTable.h"
#include "rts/Libdw.h"
/* Misc stuff without a home */
DLL_IMPORT_RTS extern char **prog_argv; /* so we can get at these from Haskell */
......
/* ---------------------------------------------------------------------------
*
* (c) The GHC Team, 2014-2015
*
* Producing DWARF-based stacktraces with libdw.
*
* --------------------------------------------------------------------------*/
#ifndef RTS_LIBDW_H
#define RTS_LIBDW_H
// Chunk capacity
// This is rather arbitrary
#define BACKTRACE_CHUNK_SZ 256
/*
* Note [Chunked stack representation]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Consider the stack,
* main calls (bottom of stack)
* func1 which in turn calls
* func2 which calls
* func3 which calls
* func4 which calls
* func5 which calls
* func6 which calls
* func7 which requests a backtrace (top of stack)
*
* This would produce the Backtrace (using a smaller chunk size of three for
* illustrative purposes),
*
* Backtrace /----> Chunk /----> Chunk /----> Chunk
* last --------/ next --------/ next --------/ next
* n_frames=8 n_frames=2 n_frames=3 n_frames=3
* ~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~
* func1 func4 func7
* main func3 func6
* func2 func5
*
*/
/* A chunk of code addresses from an execution stack
*
* The first address in this list corresponds to the stack frame
* nearest to the "top" of the stack.
*/
typedef struct BacktraceChunk_ {
StgWord n_frames; // number of frames in this chunk
struct BacktraceChunk_ *next; // the chunk following this one
StgPtr frames[BACKTRACE_CHUNK_SZ]; // the code addresses from the
// frames
} __attribute__((packed)) BacktraceChunk;
/* A chunked list of code addresses from an execution stack
*
* This structure is optimized for append operations since we append O(stack
* depth) times yet typically only traverse the stack trace once. Consequently,
* the "top" stack frame (that is, the one where we started unwinding) can be
* found in the last chunk. Yes, this is a bit inconsistent with the ordering
* within a chunk. See Note [Chunked stack representation] for a depiction.
*/
typedef struct Backtrace_ {
StgWord n_frames; // Total number of frames in the backtrace
BacktraceChunk *last; // The first chunk of frames (corresponding to the
// bottom of the stack)
} Backtrace;
/* Various information describing the location of an address */
typedef struct Location_ {
const char *object_file;
const char *function;
// lineno and colno are only valid if source_file /= NULL
const char *source_file;
StgWord32 lineno;
StgWord32 colno;
} __attribute__((packed)) Location;
#ifdef USE_LIBDW
void backtrace_free(Backtrace *bt);
#else
INLINE_HEADER void backtrace_free(Backtrace *bt STG_UNUSED) { }
#endif /* USE_LIBDW */
#endif /* RTS_LIBDW_H */
......@@ -341,6 +341,9 @@ FFILibDir=@FFILibDir@
FFIIncludeDir=@FFIIncludeDir@
# Include support for DWARF unwinding
GhcRtsWithLibdw = @HaveLibdw@
################################################################################
#
# Paths (see paths.mk)
......
This diff is collapsed.
/* -----------------------------------------------------------------------------
*
* (c) The GHC Team, 2014-2015
*
* Producing stacktraces with DWARF unwinding using libdw..
*
* Do not #include this file directly: #include "Rts.h" instead.
*
* To understand the structure of the RTS headers, see the wiki:
* http://ghc.haskell.org/trac/ghc/wiki/Commentary/SourceTree/Includes
*
* -------------------------------------------------------------------------- */
#ifndef LIBDW_H
#define LIBDW_H
#include "BeginPrivate.h"
#ifdef USE_LIBDW
struct LibDwSession_;
typedef struct LibDwSession_ LibDwSession;
/* Begin a libdw session. A session is tied to a particular capability */
LibDwSession *libdw_init(void);
/* Free a session */
void libdw_free(LibDwSession *session);
/* Request a backtrace of the current stack state */
Backtrace *libdw_get_backtrace(LibDwSession *session);
/* Lookup Location information for the given address.
* Returns 0 if successful, 1 if address could not be found. */
int libdw_lookup_location(LibDwSession *session, Location *loc, StgPtr pc);
/* Pretty-print a backtrace to std*/
void libdw_print_backtrace(LibDwSession *session, FILE *file, Backtrace *bt);
// Traverse backtrace in order of outer-most to inner-most frame
#define FOREACH_FRAME_INWARDS(pc, bt) \
BacktraceChunk *_chunk; \
unsigned int _frame_idx; \
for (_chunk = &bt->frames; _chunk != NULL; _chunk = _chunk->next) \
for (_frame_idx=0; \
pc = _chunk->frames[_frame_idx], _frame_idx < _chunk->n_frames; \
_frame_idx++)
// Traverse a backtrace in order of inner-most to outer-most frame
int foreach_frame_outwards(Backtrace *bt,
int (*cb)(StgPtr, void*),
void *user_data);
#endif /* USE_LIBDW */
#include "EndPrivate.h"
#endif /* LIBDW_H */
......@@ -489,6 +489,14 @@ rts_PACKAGE_CPP_OPTS += '-DFFI_LIB="C$(LIBFFI_NAME)"'
endif
#-----------------------------------------------------------------------------
# Add support for reading DWARF debugging information, if available
ifeq "$(GhcRtsWithLibdw)" "YES"
rts_CC_OPTS += -DUSE_LIBDW
rts_PACKAGE_CPP_OPTS += -DUSE_LIBDW
endif
# -----------------------------------------------------------------------------
# dependencies
......
......@@ -56,6 +56,10 @@ extra-libraries:
#if USE_PAPI
, "papi"
#endif
#ifdef USE_LIBDW
, "elf"
, "dw" /* for backtraces */
#endif
#ifdef INSTALLING
include-dirs: INCLUDE_DIR PAPI_INCLUDE_DIR FFI_INCLUDE_DIR
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment