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),