Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1093,9 +1093,30 @@ function _require_prelocked(uuidkey::PkgId)
error("package `$(uuidkey.name)` did not define the expected \
module `$(uuidkey.name)`, check for typos in package module name")
end
return root_module(uuidkey)
root_mod = root_module(uuidkey)
if JLOptions().code_coverage == 1 || JLOptions().malloc_log == 1 # 1 == "project" mode
pkg_dir = pkgdir(root_mod)
act_proj_dir = dirname(active_project())
if moduleroot(root_mod) == root_mod && (pkg_dir == act_proj_dir || pkg_dir in explicit_project_dirs)
@debug "Project tracking: $(repr(root_mod)) set as the project module" pkg_dir act_proj_dir explicit_project_dirs
set_project_module(root_mod)
end
end
return root_mod
end

# needed for Pkg.test because it conducts the tests in a temporary env, not the project env
const explicit_project_dirs = String[]

"""
set_project_module(m::Module)

Set a given module as the project module, marking it and its submodules
for inclusion when code coverage and allocation tracking is set to `project` mode.
"""
set_project_module(m::Module) = ccall(:jl_set_projmod, Cvoid, (Any,), m)
unset_project_module() = ccall(:jl_unset_projmod, Cvoid, ())

const loaded_modules = Dict{PkgId,Module}()
const module_keys = IdDict{Module,PkgId}() # the reverse

Expand Down
8 changes: 6 additions & 2 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,20 @@ function julia_cmd(julia=joinpath(Sys.BINDIR::String, julia_exename()))
coverage_file = (opts.output_code_coverage != C_NULL) ? unsafe_string(opts.output_code_coverage) : ""
if isempty(coverage_file) || occursin("%p", coverage_file)
if opts.code_coverage == 1
push!(addflags, "--code-coverage=user")
push!(addflags, "--code-coverage=project")
elseif opts.code_coverage == 2
push!(addflags, "--code-coverage=user")
elseif opts.code_coverage == 3
push!(addflags, "--code-coverage=all")
end
isempty(coverage_file) || push!(addflags, "--code-coverage=$coverage_file")
end
end
if opts.malloc_log == 1
push!(addflags, "--track-allocation=user")
push!(addflags, "--track-allocation=project")
elseif opts.malloc_log == 2
push!(addflags, "--track-allocation=user")
elseif opts.malloc_log == 3
push!(addflags, "--track-allocation=all")
end
if opts.color == 1
Expand Down
4 changes: 2 additions & 2 deletions doc/man/julia.1
Original file line number Diff line number Diff line change
Expand Up @@ -195,11 +195,11 @@ Generate LLVM bitcode (.bc)
Generate an incremental output file (rather than complete)

.TP
--code-coverage={none|user|all}, --code-coverage
--code-coverage={none|project|user|all}, --code-coverage
Count executions of source lines (omitting setting is equivalent to 'user')

.TP
--track-allocation={none|user|all}, --track-allocation
--track-allocation={none|project|user|all}, --track-allocation
Count bytes allocated by each source line

.SH FILES
Expand Down
4 changes: 2 additions & 2 deletions doc/src/manual/command-line-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,9 @@ The following is a complete list of command-line switches available when launchi
|`--inline={yes\|no}` |Control whether inlining is permitted, including overriding `@inline` declarations|
|`--check-bounds={yes\|no\|auto}` |Emit bounds checks always, never, or respect `@inbounds` declarations|
|`--math-mode={ieee,fast}` |Disallow or enable unsafe floating point optimizations (overrides `@fastmath` declaration)|
|`--code-coverage={none\|user\|all}` |Count executions of source lines|
|`--code-coverage={none\|project\|user\|all}` |Count executions of source lines|
|`--code-coverage` |equivalent to `--code-coverage=user`|
|`--track-allocation={none\|user\|all}` |Count bytes allocated by each source line|
|`--track-allocation={none\|project\|user\|all}` |Count bytes allocated by each source line|
|`--track-allocation` |equivalent to `--track-allocation=user`|

!!! compat "Julia 1.1"
Expand Down
38 changes: 25 additions & 13 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6961,14 +6961,20 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
!jl_is_submodule(mod, jl_core_module));
};
bool mod_is_user_mod = in_user_mod(ctx.module);
auto in_project_mod = [] (jl_module_t *mod) {
return (jl_is_submodule(mod, jl_project_module));
};
bool mod_is_project_mod = in_project_mod(ctx.module);
struct DebugLineTable {
DebugLoc loc;
StringRef file;
ssize_t line;
bool is_user_code;
bool is_project_code;
unsigned inlined_at;
bool operator ==(const DebugLineTable &other) const {
return other.loc == loc && other.file == file && other.line == line && other.is_user_code == is_user_code && other.inlined_at == inlined_at;
return other.loc == loc && other.file == file && other.line == line && other.is_user_code == is_user_code &&
other.is_project_code == is_project_code && other.inlined_at == inlined_at;
}
};
std::vector<DebugLineTable> linetable;
Expand All @@ -6981,6 +6987,7 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
topinfo.file = ctx.file;
topinfo.line = toplineno;
topinfo.is_user_code = mod_is_user_mod;
topinfo.is_project_code = mod_is_project_mod;
topinfo.inlined_at = 0;
topinfo.loc = topdebugloc;
for (size_t i = 0; i < nlocs; i++) {
Expand All @@ -6994,10 +7001,13 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
info.line = jl_unbox_long(jl_fieldref(locinfo, 3));
info.inlined_at = jl_unbox_long(jl_fieldref(locinfo, 4));
assert(info.inlined_at <= i);
if (module == ctx.module)
if (module == ctx.module) {
info.is_user_code = mod_is_user_mod;
else
info.is_project_code = mod_is_project_mod;
} else {
info.is_user_code = in_user_mod(module);
info.is_project_code = in_project_mod(module);
}
info.file = jl_symbol_name(filesym);
if (info.file.empty())
info.file = "<missing>";
Expand Down Expand Up @@ -7114,13 +7124,15 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
cursor = -1;
};

auto do_coverage = [&] (bool in_user_code) {
auto do_coverage = [&] (bool in_user_code, bool in_project_code) {
return (coverage_mode == JL_LOG_ALL ||
(coverage_mode == JL_LOG_USER && in_user_code));
(coverage_mode == JL_LOG_USER && in_user_code) ||
(coverage_mode == JL_LOG_PROJECT && in_project_code));
};
auto do_malloc_log = [&] (bool in_user_code) {
auto do_malloc_log = [&] (bool in_user_code, bool in_project_code) {
return (malloc_log_mode == JL_LOG_ALL ||
(malloc_log_mode == JL_LOG_USER && in_user_code));
(malloc_log_mode == JL_LOG_USER && in_user_code) ||
(malloc_log_mode == JL_LOG_PROJECT && in_project_code));
};
std::vector<unsigned> current_lineinfo, new_lineinfo;
auto coverageVisitStmt = [&] (size_t dbg) {
Expand All @@ -7139,15 +7151,15 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
if (newdbg != current_lineinfo[dbg]) {
current_lineinfo[dbg] = newdbg;
const auto &info = linetable.at(newdbg);
if (do_coverage(info.is_user_code))
if (do_coverage(info.is_user_code, info.is_project_code))
coverageVisitLine(ctx, info.file, info.line);
}
}
new_lineinfo.clear();
};
auto mallocVisitStmt = [&] (unsigned dbg, Value *sync) {
if (!do_malloc_log(mod_is_user_mod) || dbg == 0) {
if (do_malloc_log(true) && sync)
if (!do_malloc_log(mod_is_user_mod, mod_is_project_mod) || dbg == 0) {
if (do_malloc_log(true, true) && sync)
ctx.builder.CreateCall(prepare_call(sync_gc_total_bytes_func), {sync});
return;
}
Expand All @@ -7158,7 +7170,7 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
if (coverage_mode != JL_LOG_NONE) {
// record all lines that could be covered
for (const auto &info : linetable)
if (do_coverage(info.is_user_code))
if (do_coverage(info.is_user_code, info.is_project_code))
jl_coverage_alloc_line(info.file, info.line);
}

Expand Down Expand Up @@ -7213,15 +7225,15 @@ static std::pair<std::unique_ptr<Module>, jl_llvm_functions_t>
}

Value *sync_bytes = nullptr;
if (do_malloc_log(true))
if (do_malloc_log(true, true))
sync_bytes = ctx.builder.CreateCall(prepare_call(diff_gc_total_bytes_func), {});
{ // coverage for the function definition line number
const auto &topinfo = linetable.at(0);
if (linetable.size() > 1) {
if (topinfo == linetable.at(1))
current_lineinfo.push_back(1);
}
if (do_coverage(topinfo.is_user_code))
if (do_coverage(topinfo.is_user_code, topinfo.is_project_code))
coverageVisitLine(ctx, topinfo.file, topinfo.line);
}

Expand Down
1 change: 1 addition & 0 deletions src/jl_exported_data.inc
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
XX(jl_pinode_type) \
XX(jl_pointer_type) \
XX(jl_pointer_typename) \
XX(jl_project_module) \
XX(jl_quotenode_type) \
XX(jl_readonlymemory_exception) \
XX(jl_ref_type) \
Expand Down
12 changes: 8 additions & 4 deletions src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,12 @@ static const char opts[] =
" --math-mode={ieee,fast} Disallow or enable unsafe floating point optimizations (overrides @fastmath declaration)\n\n"

// instrumentation options
" --code-coverage={none|user|all}, --code-coverage\n"
" --code-coverage={none|project|user|all}, --code-coverage\n"
" Count executions of source lines (omitting setting is equivalent to \"user\")\n"
" --code-coverage=tracefile.info\n"
" Append coverage information to the LCOV tracefile (filename supports format tokens).\n"
// TODO: These TOKENS are defined in `runtime_ccall.cpp`. A more verbose `--help` should include that list here.
" --track-allocation={none|user|all}, --track-allocation\n"
" --track-allocation={none|project|user|all}, --track-allocation\n"
" Count bytes allocated by each source line (omitting setting is equivalent to \"user\")\n"
" --bug-report=KIND Launch a bug report session. It can be used to start a REPL, run a script, or evaluate\n"
" expressions. It first tries to use BugReporting.jl installed in current environment and\n"
Expand Down Expand Up @@ -499,7 +499,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
case opt_code_coverage:
if (optarg != NULL) {
size_t endof = strlen(optarg);
if (!strcmp(optarg, "user"))
if (!strcmp(optarg,"project"))
codecov = JL_LOG_PROJECT;
else if (!strcmp(optarg, "user"))
codecov = JL_LOG_USER;
else if (!strcmp(optarg, "all"))
codecov = JL_LOG_ALL;
Expand All @@ -520,7 +522,9 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
break;
case opt_track_allocation:
if (optarg != NULL) {
if (!strcmp(optarg,"user"))
if (!strcmp(optarg,"project"))
malloclog = JL_LOG_PROJECT;
else if (!strcmp(optarg,"user"))
malloclog = JL_LOG_USER;
else if (!strcmp(optarg,"all"))
malloclog = JL_LOG_ALL;
Expand Down
6 changes: 4 additions & 2 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,7 @@ extern JL_DLLEXPORT jl_module_t *jl_main_module JL_GLOBALLY_ROOTED;
extern JL_DLLEXPORT jl_module_t *jl_core_module JL_GLOBALLY_ROOTED;
extern JL_DLLEXPORT jl_module_t *jl_base_module JL_GLOBALLY_ROOTED;
extern JL_DLLEXPORT jl_module_t *jl_top_module JL_GLOBALLY_ROOTED;
extern JL_DLLEXPORT jl_module_t *jl_project_module JL_GLOBALLY_ROOTED;
JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name);
JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on);
JL_DLLEXPORT void jl_set_module_optlevel(jl_module_t *self, int lvl);
Expand Down Expand Up @@ -2071,8 +2072,9 @@ JL_DLLEXPORT int jl_generating_output(void) JL_NOTSAFEPOINT;
// Settings for code_coverage and malloc_log
// NOTE: if these numbers change, test/cmdlineargs.jl will have to be updated
#define JL_LOG_NONE 0
#define JL_LOG_USER 1
#define JL_LOG_ALL 2
#define JL_LOG_PROJECT 1
#define JL_LOG_USER 2
#define JL_LOG_ALL 3

#define JL_OPTIONS_CHECK_BOUNDS_DEFAULT 0
#define JL_OPTIONS_CHECK_BOUNDS_ON 1
Expand Down
10 changes: 10 additions & 0 deletions src/module.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ JL_DLLEXPORT uint8_t jl_istopmod(jl_module_t *mod)
return mod->istopmod;
}

JL_DLLEXPORT void jl_set_projmod(jl_module_t *mod)
{
jl_project_module = mod;
}

JL_DLLEXPORT uint8_t jl_unset_projmod(void)
{
jl_project_module = NULL;
}

static jl_binding_t *new_binding(jl_sym_t *name)
{
jl_task_t *ct = jl_current_task;
Expand Down
3 changes: 2 additions & 1 deletion src/staticdata.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ extern "C" {
// TODO: put WeakRefs on the weak_refs list during deserialization
// TODO: handle finalizers

#define NUM_TAGS 153
#define NUM_TAGS 154

// An array of references that need to be restored from the sysimg
// This is a manually constructed dual of the gvars array, which would be produced by codegen for Julia code, for C.
Expand Down Expand Up @@ -165,6 +165,7 @@ jl_value_t **const*const get_tags(void) {
INSERT_TAG(jl_base_module);
INSERT_TAG(jl_main_module);
INSERT_TAG(jl_top_module);
INSERT_TAG(jl_project_module);
INSERT_TAG(jl_typeinf_func);
INSERT_TAG(jl_type_type_mt);
INSERT_TAG(jl_nonfunction_mt);
Expand Down
4 changes: 2 additions & 2 deletions stdlib/Pkg.version
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PKG_BRANCH = master
PKG_SHA1 = c991ce0a31b784133e061816d21f7d2556a182e7
PKG_BRANCH = ib/coverage_project
PKG_SHA1 = 7a8920e20e47173fb55c27da48ef54a66ce3b65d
PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git
PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1
12 changes: 9 additions & 3 deletions test/cmdlineargs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -306,21 +306,27 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no`
--code-coverage=$covfile --code-coverage=none`) == "0"
@test !isfile(covfile)
@test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
--code-coverage=$covfile --code-coverage`) == "1"
--code-coverage=$covfile --code-coverage`) == "2"
@test isfile(covfile)
got = read(covfile, String)
rm(covfile)
@test occursin(expected, got) || (expected, got)
@test_broken occursin(expected_good, got)
@test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
--code-coverage=$covfile --code-coverage=user`) == "1"
--code-coverage=$covfile --code-coverage=project`) == "1"
@test isfile(covfile)
got = read(covfile, String)
rm(covfile)
@test isempty(got) || ("", got)
@test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
--code-coverage=$covfile --code-coverage=user`) == "2"
@test isfile(covfile)
got = read(covfile, String)
rm(covfile)
@test occursin(expected, got) || (expected, got)
@test_broken occursin(expected_good, got)
@test readchomp(`$exename -E "Base.JLOptions().code_coverage" -L $inputfile
--code-coverage=$covfile --code-coverage=all`) == "2"
--code-coverage=$covfile --code-coverage=all`) == "3"
@test isfile(covfile)
got = read(covfile, String)
rm(covfile)
Expand Down