From: Lucas Bajolet Date: Fri, 31 Jan 2014 18:04:50 +0000 (-0500) Subject: nitg: Added option to bind C function names to Nit names when generating a Stack... X-Git-Tag: v0.6.4~36^2~5 X-Git-Url: http://nitlanguage.org nitg: Added option to bind C function names to Nit names when generating a Stack Trace (Requires gperf) Signed-off-by: Lucas Bajolet --- diff --git a/README b/README index 0b17475..c3e7c1b 100644 --- a/README +++ b/README @@ -20,10 +20,12 @@ Requirement: * ccache http://ccache.samba.org/ to improve recompilation * libgc-dev http://www.hpl.hp.com/personal/Hans_Boehm/gc/ * graphviz http://www.graphviz.org/ to enable graphes with the nitdoc tool + * libunwind http://nongnu.org/libunwind + * gperf http://gnu.org/software/gperf to enable mapping from C to Nit function names in a stack trace Those are available in most linux distributions - # sudo apt-get install build-essential ccache libgc-dev graphviz + # sudo apt-get install build-essential ccache libgc-dev graphviz libunwind gperf Important files and directory: diff --git a/src/abstract_compiler.nit b/src/abstract_compiler.nit index 697ecb7..9f6e123 100644 --- a/src/abstract_compiler.nit +++ b/src/abstract_compiler.nit @@ -50,6 +50,8 @@ redef class ToolContext var opt_no_check_other: OptionBool = new OptionBool("Disable implicit tests: unset attribute, null receiver (dangerous)", "--no-check-other") # --typing-test-metrics var opt_typing_test_metrics: OptionBool = new OptionBool("Enable static and dynamic count of all type tests", "--typing-test-metrics") + # --stack-trace-C-to-Nit-name-binding + var opt_stacktrace: OptionBool = new OptionBool("Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime", "--nit-stacktrace") redef init do @@ -57,6 +59,7 @@ redef class ToolContext self.option_context.add_option(self.opt_output, self.opt_no_cc, self.opt_make_flags, self.opt_compile_dir, self.opt_hardening, self.opt_no_shortcut_range) self.option_context.add_option(self.opt_no_check_covariance, self.opt_no_check_initialization, self.opt_no_check_assert, self.opt_no_check_autocast, self.opt_no_check_other) self.option_context.add_option(self.opt_typing_test_metrics) + self.option_context.add_option(self.opt_stacktrace) end end @@ -111,6 +114,9 @@ redef class ModelBuilder if compile_dir == null then compile_dir = ".nit_compile" compile_dir.mkdir + + if self.toolcontext.opt_stacktrace.value then compiler.build_c_to_nit_bindings + var orig_dir=".." # FIXME only works if `compile_dir` is a subdirectory of cwd var outname = self.toolcontext.opt_output.value @@ -256,6 +262,9 @@ end abstract class AbstractCompiler type VISITOR: AbstractCompilerVisitor + # Table corresponding c_names to nit names (methods) + var names = new HashMap[String, String] + # The main module of the program currently compiled # Is assigned during the separate compilation var mainmodule: MModule writable @@ -306,6 +315,40 @@ abstract class AbstractCompiler private var provided_declarations = new HashMap[String, String] + # Builds the .c and .h files to be used when generating a Stack Trace + # Binds the generated C function names to Nit function names + fun build_c_to_nit_bindings + do + var compile_dir = modelbuilder.toolcontext.opt_compile_dir.value + if compile_dir == null then compile_dir = ".nit_compile" + + var stream = new OFStream.open("{compile_dir}/C_fun_names") + stream.write("%\{\n#include \"c_functions_hash.h\"\n%\}\n") + stream.write("%define lookup-function-name get_nit_name\n") + stream.write("struct C_Nit_Names;\n") + stream.write("%%\n") + stream.write("####\n") + for i in names.keys do + stream.write(i) + stream.write(",\t\"") + stream.write(names[i]) + stream.write("\"\n") + end + stream.write("####\n") + stream.write("%%\n") + stream.close + + stream = new OFStream.open("{compile_dir}/c_functions_hash.h") + stream.write("typedef struct C_Nit_Names\{char* name; char* nit_name;\}C_Nit_Names;\n") + stream.write("const struct C_Nit_Names* get_nit_name(register const char *str, register unsigned int len);\n") + stream.close + + var x = new Process("gperf","{compile_dir}/C_fun_names","-t","-7","--output-file={compile_dir}/c_functions_hash.c","-C") + x.wait + + extern_bodies.add(new ExternCFile("{compile_dir}/c_functions_hash.c", "")) + end + # Compile C headers # This method call compile_header_strucs method that has to be refined fun compile_header do @@ -314,6 +357,9 @@ abstract class AbstractCompiler self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") + if modelbuilder.toolcontext.opt_stacktrace.value then + self.header.add_decl("#include \"c_functions_hash.h\"") + end self.header.add_decl("#include ") self.header.add_decl("#include ") self.header.add_decl("#include ") @@ -365,11 +411,20 @@ abstract class AbstractCompiler v.add_decl("unw_getcontext(&uc);") v.add_decl("unw_init_local(&cursor, &uc);") v.add_decl("printf(\"-------------------------------------------------\\n\");") - v.add_decl("printf(\"-- C Stack Trace ------------------------------\\n\");") + v.add_decl("printf(\"-- Stack Trace ------------------------------\\n\");") v.add_decl("printf(\"-------------------------------------------------\\n\");") v.add_decl("while (unw_step(&cursor) > 0) \{") v.add_decl(" unw_get_proc_name(&cursor, procname, 100, &ip);") + if modelbuilder.toolcontext.opt_stacktrace.value then + v.add_decl(" const C_Nit_Names* recv = get_nit_name(procname, strlen(procname));") + v.add_decl(" if (recv != 0)\{") + v.add_decl(" printf(\"` %s\\n\", recv->nit_name);") + v.add_decl(" \}else\{") + v.add_decl(" printf(\"` %s\\n\", procname);") + v.add_decl(" \}") + else v.add_decl(" printf(\"` %s \\n\",procname);") + end v.add_decl("\}") v.add_decl("printf(\"-------------------------------------------------\\n\");") v.add_decl("free(procname);") diff --git a/src/global_compiler.nit b/src/global_compiler.nit index 8d0290d..6dc5295 100644 --- a/src/global_compiler.nit +++ b/src/global_compiler.nit @@ -978,6 +978,7 @@ private class CustomizedRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") + if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" end redef fun call(v: VISITOR, arguments: Array[RuntimeVariable]): nullable RuntimeVariable diff --git a/src/separate_compiler.nit b/src/separate_compiler.nit index 0d2e061..ea4e00f 100644 --- a/src/separate_compiler.nit +++ b/src/separate_compiler.nit @@ -1688,6 +1688,7 @@ class SeparateRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") + if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" end end @@ -1765,6 +1766,7 @@ class VirtualRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") + if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})" end # TODO ? diff --git a/tests/sav/nitg.res b/tests/sav/nitg.res index 55c09f5..7b0a116 100644 --- a/tests/sav/nitg.res +++ b/tests/sav/nitg.res @@ -22,6 +22,7 @@ --no-check-autocast Disable implicit casts on unsafe expression usage (dangerous) --no-check-other Disable implicit tests: unset attribute, null receiver (dangerous) --typing-test-metrics Enable static and dynamic count of all type tests + --nit-stacktrace Enables the use of gperf to bind C to Nit function names when encountering a Stack trace at runtime --separate Use separate compilation --no-inline-intern Do not inline call to intern methods --no-union-attribute Put primitive attibutes in a box instead of an union