Skip to content

Commit

Permalink
Merge pull request #3946 from panzone/prototype-stack-trace
Browse files Browse the repository at this point in the history
Print stack trace on halt

[PR by @Panzone, reviewed by @mppf and @GBT, merged by @mppf]

This PR extends the Chapel runtime with the ability to generate a stack
trace with the Chapel procedures names. Currently a stack trace is
generated and printed when an halt() is executed.

This feature works on linux64 (both libunwind and system) and darwin
(only system).

Future Work

* Update the testing system to ignore Stacktrace in .good files if stack
  tracing is disabled
* Update nightly testing to run with stack tracing enabled
* Consider if there is a reasonable way to simplify the paths output
* Consider more carefully the exact format of the stack trace printed
* Include libunwind in Chapel distributions

[Passed full local testing]
  • Loading branch information
mppf committed Jun 22, 2016
2 parents b136a65 + 5f264fe commit b4fc70d
Show file tree
Hide file tree
Showing 32 changed files with 386 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,8 @@ tags
/test/runtime/kbrady/goodAllocSize.good
/test/runtime/sungeun/chpl-env-gen.h
/test/runtime/sungeun/chpl-env-gen.test.gen.c
/test/runtime/panzone/stacktrace.good
/test/runtime/panzone/fact-stacktrace.good

test/separate_compilation/jturner/libclassextern2.so.a
test/separate_compilation/jturner/libfcfunc.so.a
Expand Down
1 change: 1 addition & 0 deletions compiler/include/driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ extern const char* CHPL_REGEXP;
extern const char* CHPL_WIDE_POINTERS;
extern const char* CHPL_LLVM;
extern const char* CHPL_AUX_FILESYS;
extern const char* CHPL_UNWIND;

extern bool printPasses;
extern FILE* printPassesFile;
Expand Down
2 changes: 2 additions & 0 deletions compiler/include/insertLineNumbers.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ extern std::vector<std::string> gFilenameLookup;
// Caches the location of filenames in gFilenameLookup
extern std::map<std::string, int> gFilenameLookupCache;

int getFilenameLookupPosition(std::string name);

#endif //_INSERT_LINE_NUMBERS_H
2 changes: 2 additions & 0 deletions compiler/main/driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ const char* CHPL_REGEXP = NULL;
const char* CHPL_WIDE_POINTERS = NULL;
const char* CHPL_LLVM = NULL;
const char* CHPL_AUX_FILESYS = NULL;
const char* CHPL_UNWIND = NULL;

bool widePointersStruct;

Expand Down Expand Up @@ -948,6 +949,7 @@ static void setChapelEnvs() {
CHPL_WIDE_POINTERS = envMap["CHPL_WIDE_POINTERS"];
CHPL_LLVM = envMap["CHPL_LLVM"];
CHPL_AUX_FILESYS = envMap["CHPL_AUX_FILESYS"];
CHPL_UNWIND = envMap["CHPL_UNWIND"];
}

static void setupChplGlobals(const char* argv0) {
Expand Down
58 changes: 58 additions & 0 deletions compiler/passes/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,61 @@ static void genFilenameTable() {
genGlobalInt32(sizeName, gFilenameLookup.size());
}

//
// This adds the Chapel symbol table to the config file
// Our symbol table is formed by two 1-D arrays with 2 elements
// per entry:
//
// chpl_funSymTable = cname, Chapel name
// chpl_filenumSymTable = Chapel file name index, Chapel line number
//
static void genUnwindSymbolTable(){
GenInfo *info = gGenInfo;
Vec<FnSymbol*> symbols;

//If CHPL_UNWIND is none we don't want any symbols in our tables
if(strcmp(CHPL_UNWIND, "none") != 0){
// Gets only user symbols
forv_Vec(FnSymbol, fn, gFnSymbols) {
if(strncmp(fn->cname, "chpl_", 5)) {
symbols.add(fn);
}
}
}

//TODO: Could have a native LLVM version, instead of relying on C to LLVM
if( info->cfile ) {
FILE* hdrfile = info->cfile;
bool first = true;

// Generate the cname, Chapel name table
fprintf(hdrfile,"\nc_string chpl_funSymTable[] = {\n");
forv_Vec(FnSymbol, fn, symbols) {
if(!first)
fprintf(hdrfile,",\n");

fprintf(hdrfile," \"%s\", \"%s\"", fn->cname, fn->name);
first = false;
}
fprintf(hdrfile,"};\n");

// Generate the filename index, linenum table
first = true;
fprintf(hdrfile,"\n\nint chpl_filenumSymTable[] = {\n");
forv_Vec(FnSymbol, fn, symbols) {
if(!first)
fprintf(hdrfile,",\n");

fprintf(hdrfile," %d, %d",
getFilenameLookupPosition(fn->fname()), fn->linenum());
first = false;
}
fprintf(hdrfile,"};\n");
}

genGlobalInt32("chpl_sizeSymTable", symbols.n * 2);
}

static int
compareSymbol(const void* v1, const void* v2) {
Symbol* s1 = *(Symbol* const *)v1;
Expand Down Expand Up @@ -625,6 +680,9 @@ static void codegen_header_compilation_config() {
genComment("Filename Lookup Table");
genFilenameTable();

genComment("Unwind symbol tables");
genUnwindSymbolTable();

closeCFile(&cfgfile);

gGenInfo->cfile = save_cfile;
Expand Down
2 changes: 1 addition & 1 deletion compiler/passes/insertLineNumbers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ std::vector<std::string> gFilenameLookup;
// Caches lookups into our filename vector
std::map<std::string, int> gFilenameLookupCache;

static int getFilenameLookupPosition(std::string name) {
int getFilenameLookupPosition(std::string name) {
std::map<std::string, int>::iterator it = gFilenameLookupCache.find(name);
int idx;
if (it != gFilenameLookupCache.end()) {
Expand Down
1 change: 1 addition & 0 deletions make/Makefile.base
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ include $(THIRD_PARTY_DIR)/re2/Makefile.include
include $(THIRD_PARTY_DIR)/llvm/Makefile.include-$(CHPL_MAKE_LLVM)
include $(THIRD_PARTY_DIR)/chpl-venv/Makefile.include
include $(THIRD_PARTY_DIR)/fltk/Makefile.include
include $(THIRD_PARTY_DIR)/libunwind/Makefile.include

-include $(THIRD_PARTY_DIR)/Makefile.devel.include

Expand Down
2 changes: 2 additions & 0 deletions modules/internal/ChapelEnv.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ module ChapelEnv {
/* See :ref:`readme-chplenv.CHPL_TIMERS` for more information. */
param CHPL_TIMERS:string = __primitive("get compiler variable", "CHPL_TIMERS");

param CHPL_UNWIND:string = __primitive("get compiler variable", "CHPL_UNWIND");

/* See :ref:`readme-chplenv.CHPL_MEM` for more information. */
param CHPL_MEM:string = __primitive("get compiler variable", "CHPL_MEM");

Expand Down
1 change: 1 addition & 0 deletions runtime/etc/Makefile.include
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.threads-$(CHPL_MAKE_THREADS)
-include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.tasks-$(CHPL_MAKE_TASKS)
include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.comm-$(CHPL_MAKE_COMM)
include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.mem-$(CHPL_MAKE_MEM)
-include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.unwind-$(CHPL_MAKE_UNWIND)
include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.gmp-$(CHPL_MAKE_GMP)
-include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.hwloc-$(CHPL_MAKE_HWLOC)
include $(CHPL_MAKE_HOME)/runtime/etc/Makefile.regexp-$(CHPL_MAKE_REGEXP)
Expand Down
18 changes: 18 additions & 0 deletions runtime/etc/Makefile.unwind-libunwind
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2004-2016 Cray Inc.
# Other additional copyright holders may be indicated within.
#
# The entirety of this work is licensed under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

GEN_LFLAGS += -L$(LIBUNWIND_LIB_DIR)
7 changes: 7 additions & 0 deletions runtime/include/chplcgfns.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ extern const char* CHPL_REGEXP;
extern const char* CHPL_WIDE_POINTERS;
extern const char* CHPL_LLVM;
extern const char* CHPL_AUX_FILESYS;
extern const char* CHPL_UNWIND;
extern const int CHPL_STACK_CHECKS;
extern const int CHPL_CACHE_REMOTE;

Expand All @@ -70,6 +71,12 @@ extern const int CHPL_CACHE_REMOTE;
extern c_string chpl_filenameTable[];
extern const int32_t chpl_filenameTableSize;

// Lookup tables used as a symbol table by the stack unwinder for translating
// C symbols into Chapel symbols. Defined in chpl_compilation_config.c
extern c_string chpl_funSymTable[];
extern int chpl_filenumSymTable[];
extern const int32_t chpl_sizeSymTable;

/* defined in main.c */
extern char* chpl_executionCommand;

Expand Down
4 changes: 4 additions & 0 deletions runtime/make/Makefile.runtime.include
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ endif
GMP_INCLUDE=$(RUNTIME_ROOT)/make/Makefile.runtime.gmp-$(CHPL_MAKE_GMP)
-include $(GMP_INCLUDE)

# Add any further includes for unwind.
UNWIND_INCLUDE=$(RUNTIME_ROOT)/make/Makefile.runtime.unwind-$(CHPL_MAKE_UNWIND)
-include $(UNWIND_INCLUDE)

# Add any further includes for HWLOC.
HWLOC_INCLUDE=$(RUNTIME_ROOT)/make/Makefile.runtime.hwloc-$(CHPL_MAKE_HWLOC)
-include $(HWLOC_INCLUDE)
Expand Down
19 changes: 19 additions & 0 deletions runtime/make/Makefile.runtime.unwind-libunwind
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2004-2016 Cray Inc.
# Other additional copyright holders may be indicated within.
#
# The entirety of this work is licensed under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

RUNTIME_DEFS += -DCHPL_UNWIND_LIBUNWIND
RUNTIME_INCLS += -I$(LIBUNWIND_INCLUDE_DIR)
19 changes: 19 additions & 0 deletions runtime/make/Makefile.runtime.unwind-system
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2004-2016 Cray Inc.
# Other additional copyright holders may be indicated within.
#
# The entirety of this work is licensed under the Apache License,
# Version 2.0 (the "License"); you may not use this file except
# in compliance with the License.
#
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

RUNTIME_DEFS += -DCHPL_UNWIND_LIBUNWIND

55 changes: 55 additions & 0 deletions runtime/src/error.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@

#include "chplrt.h"
#include "chpl-linefile-support.h"
#include "chplcgfns.h"

#include "error.h"
#include "chplexit.h"
#include "chpl-env.h"

#include <stdarg.h>
#include <string.h>
Expand All @@ -33,6 +35,54 @@
#include "chpl-atomics.h"
#endif

#ifdef CHPL_UNWIND_LIBUNWIND
// Necessary for instruct libunwind to use only the local unwind
#define UNW_LOCAL_ONLY
#include <libunwind.h>

static void chpl_stack_unwind(void){
// This is just a prototype using libunwind
unw_cursor_t cursor;
unw_context_t uc;
unw_word_t wordValue;
char buffer[128];

// Check if we need to print the stack trace (default = yes)
if(! chpl_get_rt_env_bool("UNWIND", true)) {
return;
}

unw_getcontext(&uc);
unw_init_local(&cursor, &uc);

if(chpl_sizeSymTable > 0)
fprintf(stderr,"\nStacktrace\n\n");

// This loop does the effective stack unwind, see libunwid documentation
while (unw_step(&cursor) > 0) {
unw_get_proc_name(&cursor, buffer, sizeof(buffer), &wordValue);
// Since this stack trace is printed out a program exit, we do not believe
// it is performance sensitive. Additionally, this initial implementation
// favors simplicity over performance.
//
// If it becomes necessary to improve performance, this code could use be
// faster using one of these two strategies:
// 1) Use a hashtable or map to find entries in chpl_funSymTable, or
// 2) Emit chpl_funSymTable in sorted order and use binary search on it
for(int t = 0; t < chpl_sizeSymTable; t+=2 ){
if (!strcmp(chpl_funSymTable[t], buffer)){
fprintf(stderr,"%s (%s:%d)\n",
chpl_funSymTable[t+1],
chpl_lookupFilename(chpl_filenumSymTable[t]),
chpl_filenumSymTable[t+1]);
break;
}
}
}
fprintf(stderr,"\n");
}
#endif

int verbosity = 1;

void chpl_warning(const char* message, int32_t lineno, int32_t filenameIdx) {
Expand Down Expand Up @@ -90,6 +140,11 @@ void chpl_error_explicit(const char *message, int32_t lineno,
else
fprintf(stderr, "error: %s", message);
fprintf(stderr, "\n");

#ifdef CHPL_UNWIND_LIBUNWIND
chpl_stack_unwind();
#endif

chpl_exit_any(1);
}

Expand Down
1 change: 1 addition & 0 deletions test/compflags/albrecht/chplenv/chplenv.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ writeln("CHPL_GASNET_SEGMENT=",CHPL_GASNET_SEGMENT);
writeln("CHPL_TASKS=",CHPL_TASKS);
writeln("CHPL_LAUNCHER=",CHPL_LAUNCHER);
writeln("CHPL_TIMERS=",CHPL_TIMERS);
writeln("CHPL_UNWIND=",CHPL_UNWIND);
writeln("CHPL_MEM=",CHPL_MEM);
writeln("CHPL_MAKE=",CHPL_MAKE);
writeln("CHPL_ATOMICS=",CHPL_ATOMICS);
Expand Down
1 change: 1 addition & 0 deletions test/modules/gbt/printchplenv.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if CHPL_COMM != 'none' && CHPL_COMM != 'ugni' then {
writeln('CHPL_TASKS: ', CHPL_TASKS);
writeln('CHPL_LAUNCHER: ', CHPL_LAUNCHER);
writeln('CHPL_TIMERS: ', CHPL_TIMERS);
writeln('CHPL_UNWIND: ', CHPL_UNWIND);
writeln('CHPL_MEM: ', CHPL_MEM);
writeln('CHPL_MAKE: ', CHPL_MAKE);
writeln('CHPL_ATOMICS: ', CHPL_ATOMICS);
Expand Down
6 changes: 6 additions & 0 deletions test/runtime/panzone/fact-stacktrace.chpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
proc fact(i : int) :int{
if(i == 0) then halt();
else return i*fact(i-1);
}

fact(2);
1 change: 1 addition & 0 deletions test/runtime/panzone/fact-stacktrace.cleanfiles
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fact-stacktrace.good
11 changes: 11 additions & 0 deletions test/runtime/panzone/fact-stacktrace.prediff
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /bin/bash

echo "$1.chpl:2: error: halt reached" > $1.good
echo "" >> $1.good
echo "Stacktrace" >> $1.good
echo "" >> $1.good
echo "halt ($CHPL_HOME/modules/internal/ChapelIO.chpl:679)" >> $1.good
echo "fact ($1.chpl:1)" >> $1.good
echo "fact ($1.chpl:1)" >> $1.good
echo "fact ($1.chpl:1)" >> $1.good
echo "" >> $1.good
2 changes: 2 additions & 0 deletions test/runtime/panzone/fact-stacktrace.skipif
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CHPL_RT_UNWIND==0
CHPL_UNWIND==none
13 changes: 13 additions & 0 deletions test/runtime/panzone/stacktrace.chpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
proc a() {
halt();
}

proc b(){
a();
}

proc c(){
b();
}

c();
1 change: 1 addition & 0 deletions test/runtime/panzone/stacktrace.cleanfiles
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
stacktrace.good
11 changes: 11 additions & 0 deletions test/runtime/panzone/stacktrace.prediff
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /bin/bash

echo "$1.chpl:2: error: halt reached" > $1.good
echo "" >> $1.good
echo "Stacktrace" >> $1.good
echo "" >> $1.good
echo "halt ($CHPL_HOME/modules/internal/ChapelIO.chpl:679)" >> $1.good
echo "a ($1.chpl:1)" >> $1.good
echo "b ($1.chpl:5)" >> $1.good
echo "c ($1.chpl:9)" >> $1.good
echo "" >> $1.good
2 changes: 2 additions & 0 deletions test/runtime/panzone/stacktrace.skipif
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CHPL_RT_UNWIND==0
CHPL_UNWIND==none

0 comments on commit b4fc70d

Please sign in to comment.