From a64f9f61a08ad21a2c7917308c2889db5d172b7c Mon Sep 17 00:00:00 2001 From: Jeff Bezanson <jeff.bezanson@gmail.com> Date: Thu, 16 Jan 2014 16:02:31 -0500 Subject: [PATCH 1/2] implement a simple code coverage tool To use the tool, pass --code-coverage. On exit, for each source file foo.jl for which we have data, write foo.jl.cov next to the original file, with execution counts in front of each line, or "-" if no data. also start consolidating compiler options into a jl_compileropts struct --- src/codegen.cpp | 18 ++++++++----- src/debuginfo.cpp | 64 +++++++++++++++++++++++++++++++++++++++++++++++ src/dump.c | 3 ++- src/init.c | 18 +++++++++---- src/jlapi.c | 2 +- src/julia.h | 22 ++++++++++------ src/toplevel.c | 1 - ui/repl.c | 28 +++++++++++---------- 8 files changed, 122 insertions(+), 34 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 778a375fbd9bd..4cd1f17b81db7 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -85,6 +85,7 @@ #include <string> #include <sstream> +#include <fstream> #include <map> #include <vector> #include <set> @@ -273,12 +274,6 @@ struct jl_varinfo_t { }; // --- helpers for reloading IR image -extern "C" -void jl_set_imaging_mode(int stat) -{ - imaging_mode = !!stat; -} - static void jl_gen_llvm_gv_array(); extern "C" @@ -3080,6 +3075,8 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) ctx.f = f; // step 5. set up debug info context and create first basic block + bool do_coverage = + jl_compileropts.code_coverage && lam->module != jl_base_module && lam->module != jl_core_module; jl_value_t *stmt = jl_cellref(stmts,0); std::string filename = "no file"; char *dbgFuncName = lam->name->name; @@ -3124,6 +3121,7 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) // special value: if function name is empty, disable debug info builder.SetCurrentDebugLocation(noDbg); debug_enabled = false; + do_coverage = false; } else { // TODO: Fix when moving to new LLVM version @@ -3423,6 +3421,8 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) } // step 15. compile body statements + if (do_coverage) + coverageVisitLine(filename, lno); bool prevlabel = false; for(i=0; i < stmtslen; i++) { jl_value_t *stmt = jl_cellref(stmts,i); @@ -3430,12 +3430,16 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) int lno = jl_linenode_line(stmt); if (debug_enabled) builder.SetCurrentDebugLocation(DebugLoc::get(lno, 1, (MDNode*)SP, NULL)); + if (do_coverage) + coverageVisitLine(filename, lno); ctx.lineno = lno; } else if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == line_sym) { int lno = jl_unbox_long(jl_exprarg(stmt, 0)); if (debug_enabled) builder.SetCurrentDebugLocation(DebugLoc::get(lno, 1, (MDNode*)SP, NULL)); + if (do_coverage) + coverageVisitLine(filename, lno); ctx.lineno = lno; } if (jl_is_labelnode(stmt)) { @@ -3985,6 +3989,8 @@ static void init_julia_llvm_env(Module *m) extern "C" void jl_init_codegen(void) { + imaging_mode = jl_compileropts.build_path != NULL; + InitializeNativeTarget(); InitializeNativeTargetAsmPrinter(); InitializeNativeTargetAsmParser(); diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 428a859a20056..9df584409a82d 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -155,3 +155,67 @@ class JITMemoryManagerWin : public JITMemoryManager { virtual void registerEHFrames(StringRef SectionData) { return JMM->registerEHFrames(SectionData); } }; #endif + +// Code coverage + +typedef std::map<std::string,std::vector<GlobalVariable*> > coveragedata_t; +static coveragedata_t coverageData; + +static void coverageVisitLine(std::string filename, int line) +{ + if (filename == "" || filename == "none" || filename == "no file") + return; + coveragedata_t::iterator it = coverageData.find(filename); + if (it == coverageData.end()) { + coverageData[filename] = std::vector<GlobalVariable*>(0); + } + std::vector<GlobalVariable*> &vec = coverageData[filename]; + if (vec.size() <= (size_t)line) + vec.resize(line+1, NULL); + if (vec[line] == NULL) + vec[line] = new GlobalVariable(*jl_Module, T_int64, false, GlobalVariable::ExternalLinkage, + ConstantInt::get(T_int64,0), "lcnt"); + GlobalVariable *v = vec[line]; + builder.CreateStore(builder.CreateAdd(builder.CreateLoad(v), + ConstantInt::get(T_int64,1)), + v); +} + +extern "C" void jl_write_coverage_data(void) +{ + coveragedata_t::iterator it = coverageData.begin(); + for (; it != coverageData.end(); it++) { + std::string filename = (*it).first; + std::string outfile = filename + ".cov"; + std::vector<GlobalVariable*> &counts = (*it).second; + if (counts.size() > 1) { + std::ifstream inf(filename.c_str()); + if (inf.is_open()) { + std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out); + char line[1024]; + int l = 1; + while (!inf.eof()) { + inf.getline(line, sizeof(line)); + int count = -1; + if ((size_t)l < counts.size()) { + GlobalVariable *gv = counts[l]; + if (gv) { + int *p = (int*)jl_ExecutionEngine->getPointerToGlobal(gv); + count = *p; + } + } + outf.width(9); + if (count == -1) + outf<<'-'; + else + outf<<count; + outf.width(0); + outf<<" "<<line<<std::endl; + l++; + } + outf.close(); + inf.close(); + } + } + } +} diff --git a/src/dump.c b/src/dump.c index c293e8cec1121..3c4e8243f052a 100644 --- a/src/dump.c +++ b/src/dump.c @@ -989,7 +989,7 @@ extern void jl_get_system_hooks(void); extern void jl_get_uv_hooks(void); DLLEXPORT -void jl_restore_system_image(char *fname, int build_mode) +void jl_restore_system_image(char *fname) { ios_t f; char *fpath = fname; @@ -997,6 +997,7 @@ void jl_restore_system_image(char *fname, int build_mode) JL_PRINTF(JL_STDERR, "System image file \"%s\" not found\n", fname); exit(1); } + int build_mode = (jl_compileropts.build_path != NULL); #ifdef _OS_WINDOWS_ //XXX: the windows linker forces our system image to be // linked against only one dll, I picked libjulia-release diff --git a/src/init.c b/src/init.c index 5497fcbdf4c3b..99f872bf0b612 100644 --- a/src/init.c +++ b/src/init.c @@ -59,6 +59,11 @@ extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE); #include <sched.h> // for setting CPU affinity #endif +char *julia_home = NULL; +jl_compileropts_t jl_compileropts = { NULL, // build_path + 0 // code_coverage +}; + int jl_boot_file_loaded = 0; char *jl_stack_lo; @@ -350,11 +355,15 @@ static void jl_uv_exitcleanup_walk(uv_handle_t* handle, void *arg) jl_uv_exitcleanup_add(handle, (struct uv_shutdown_queue*)arg); } +void jl_write_coverage_data(void); + DLLEXPORT void uv_atexit_hook() { #if defined(JL_GC_MARKSWEEP) && defined(GC_FINAL_STATS) jl_print_gc_stats(JL_STDERR); #endif + if (jl_compileropts.code_coverage) + jl_write_coverage_data(); if (jl_base_module) { jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit")); if (f!=NULL && jl_is_function(f)) { @@ -622,10 +631,8 @@ kern_return_t catch_exception_raise(mach_port_t exception_port, #endif -void julia_init(char *imageFile, int build_mode) +void julia_init(char *imageFile) { - if (build_mode) - jl_set_imaging_mode(1); jl_page_size = jl_getpagesize(); jl_find_stack_bottom(); jl_dl_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT); @@ -704,7 +711,7 @@ void julia_init(char *imageFile, int build_mode) if (imageFile) { JL_TRY { - jl_restore_system_image(imageFile, build_mode); + jl_restore_system_image(imageFile); } JL_CATCH { JL_PRINTF(JL_STDERR, "error during init:\n"); @@ -830,7 +837,7 @@ DLLEXPORT void jl_install_sigint_handler() extern int asprintf(char **str, const char *fmt, ...); extern void * __stack_chk_guard; -DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char *av[]), char *build_path) +DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char *av[])) { #if defined(_OS_WINDOWS_) SetUnhandledExceptionFilter(exception_handler); @@ -851,6 +858,7 @@ DLLEXPORT int julia_trampoline(int argc, char **argv, int (*pmain)(int ac,char * } #endif int ret = pmain(argc, argv); + char *build_path = jl_compileropts.build_path; if (build_path) { char *build_ji; if (asprintf(&build_ji, "%s.ji",build_path) > 0) { diff --git a/src/jlapi.c b/src/jlapi.c index 66ee594942f46..947df923e8c0b 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -44,7 +44,7 @@ DLLEXPORT void jl_init(char *julia_home_dir) { libsupport_init(); char *image_file = jl_locate_sysimg(julia_home_dir, JL_SYSTEM_IMAGE_PATH); - julia_init(image_file, 0); + julia_init(image_file); jl_set_const(jl_core_module, jl_symbol("JULIA_HOME"), jl_cstr_to_string(julia_home)); jl_module_export(jl_core_module, jl_symbol("JULIA_HOME")); diff --git a/src/julia.h b/src/julia.h index 0c00c33064442..e978e37eb9629 100644 --- a/src/julia.h +++ b/src/julia.h @@ -885,8 +885,8 @@ jl_value_t *jl_no_method_error(jl_function_t *f, jl_value_t **args, size_t na); void jl_check_type_tuple(jl_tuple_t *t, jl_sym_t *name, const char *ctx); // initialization functions -DLLEXPORT void julia_init(char *imageFile, int build_mode); -DLLEXPORT int julia_trampoline(int argc, char *argv[], int (*pmain)(int ac,char *av[]), char* build_mode); +DLLEXPORT void julia_init(char *imageFile); +DLLEXPORT int julia_trampoline(int argc, char *argv[], int (*pmain)(int ac,char *av[])); void jl_init_types(void); void jl_init_box_caches(void); DLLEXPORT void jl_init_frontend(void); @@ -897,9 +897,8 @@ void jl_init_tasks(void *stack, size_t ssize); void jl_init_serializer(void); DLLEXPORT void jl_save_system_image(char *fname); -DLLEXPORT void jl_restore_system_image(char *fname, int build_mode); +DLLEXPORT void jl_restore_system_image(char *fname); void jl_dump_bitcode(char *fname); -void jl_set_imaging_mode(int stat); int32_t jl_get_llvm_gv(jl_value_t *p); // front end interface @@ -1267,9 +1266,9 @@ size_t rec_backtrace_ctx_dwarf(ptrint_t *data, size_t maxsize, bt_context_t ctx) //IO objects -extern DLLEXPORT uv_stream_t *jl_uv_stdin; -extern DLLEXPORT uv_stream_t * jl_uv_stdout; -extern DLLEXPORT uv_stream_t * jl_uv_stderr; +extern DLLEXPORT uv_stream_t *jl_uv_stdin; +extern DLLEXPORT uv_stream_t *jl_uv_stdout; +extern DLLEXPORT uv_stream_t *jl_uv_stderr; DLLEXPORT JL_STREAM *jl_stdout_stream(void); DLLEXPORT JL_STREAM *jl_stdin_stream(void); @@ -1334,6 +1333,15 @@ void jl_longjmp(jmp_buf _Buf,int _Value); for (i__ca=1, jl_eh_restore_state(&__eh); i__ca; i__ca=0) #endif +// Compiler options + +typedef struct { + char *build_path; + int code_coverage; +} jl_compileropts_t; + +extern DLLEXPORT jl_compileropts_t jl_compileropts; + #ifdef __cplusplus } #endif diff --git a/src/toplevel.c b/src/toplevel.c index 43a1b19a193a8..7fc73334c2a2b 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -17,7 +17,6 @@ #include "julia.h" #include "builtin_proto.h" -char *julia_home = NULL; // current line number in a file int jl_lineno = 0; diff --git a/ui/repl.c b/ui/repl.c index ae687fbf12995..a55f1ad468dcd 100644 --- a/ui/repl.c +++ b/ui/repl.c @@ -16,13 +16,13 @@ static int lisp_prompt = 0; static char *program = NULL; char *image_file = NULL; int tab_width = 2; -char *build_mode = NULL; static const char *usage = "julia [options] [program] [args...]\n"; static const char *opts = " -v --version Display version information\n" + " -h --help Print this message\n" " -q --quiet Quiet startup without banner\n" - " -H --home <dir> Load files relative to <dir>\n" + " -H --home <dir> Set location of julia executable\n" " -T --tab <size> Set REPL tab width to <size>\n\n" " -e --eval <expr> Evaluate <expr>\n" @@ -39,17 +39,19 @@ static const char *opts = " -F Load ~/.juliarc.jl, then handle remaining inputs\n" " --color=yes|no Enable or disable color text\n\n" - " -h --help Print this message\n"; + " --code-coverage Count executions of source lines\n"; -void parse_opts(int *argcp, char ***argvp) { +void parse_opts(int *argcp, char ***argvp) +{ static char* shortopts = "+H:T:hJ:"; static struct option longopts[] = { - { "home", required_argument, 0, 'H' }, - { "tab", required_argument, 0, 'T' }, - { "build", required_argument, 0, 'b' }, - { "lisp", no_argument, &lisp_prompt, 1 }, - { "help", no_argument, 0, 'h' }, - { "sysimage", required_argument, 0, 'J' }, + { "home", required_argument, 0, 'H' }, + { "tab", required_argument, 0, 'T' }, + { "build", required_argument, 0, 'b' }, + { "lisp", no_argument, &lisp_prompt, 1 }, + { "help", no_argument, 0, 'h' }, + { "sysimage", required_argument, 0, 'J' }, + { "code-coverage", no_argument, &jl_compileropts.code_coverage, 1 }, { 0, 0, 0, 0 } }; int c; @@ -74,7 +76,7 @@ void parse_opts(int *argcp, char ***argvp) { tab_width = atoi(optarg); break; case 'b': - build_mode = strdup(optarg); + jl_compileropts.build_path = strdup(optarg); if (!imagepathspecified) image_file = NULL; break; @@ -289,6 +291,6 @@ int main(int argc, char *argv[]) jl_lisp_prompt(); return 0; } - julia_init(lisp_prompt ? NULL : image_file, build_mode!=NULL); - return julia_trampoline(argc, argv, true_main, build_mode); + julia_init(lisp_prompt ? NULL : image_file); + return julia_trampoline(argc, argv, true_main); } From 631b0b358dd53befdfeea74bac139378c04a61fe Mon Sep 17 00:00:00 2001 From: Jeff Bezanson <jeff.bezanson@gmail.com> Date: Thu, 16 Jan 2014 17:26:50 -0500 Subject: [PATCH 2/2] fix redundant first line count switch line counters to internal linkage --- src/codegen.cpp | 2 -- src/debuginfo.cpp | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 4cd1f17b81db7..b71508e8ef407 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3421,8 +3421,6 @@ static Function *emit_function(jl_lambda_info_t *lam, bool cstyle) } // step 15. compile body statements - if (do_coverage) - coverageVisitLine(filename, lno); bool prevlabel = false; for(i=0; i < stmtslen; i++) { jl_value_t *stmt = jl_cellref(stmts,i); diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index 9df584409a82d..b1abff812f2e1 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -173,7 +173,7 @@ static void coverageVisitLine(std::string filename, int line) if (vec.size() <= (size_t)line) vec.resize(line+1, NULL); if (vec[line] == NULL) - vec[line] = new GlobalVariable(*jl_Module, T_int64, false, GlobalVariable::ExternalLinkage, + vec[line] = new GlobalVariable(*jl_Module, T_int64, false, GlobalVariable::InternalLinkage, ConstantInt::get(T_int64,0), "lcnt"); GlobalVariable *v = vec[line]; builder.CreateStore(builder.CreateAdd(builder.CreateLoad(v),