Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coverage: support output in LCOV tracefile format #30381

Merged
merged 1 commit into from
Dec 20, 2018
Merged
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
1 change: 1 addition & 0 deletions base/options.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct JLOptions
outputjitbc::Ptr{UInt8}
outputo::Ptr{UInt8}
outputji::Ptr{UInt8}
output_code_coverage::Ptr{UInt8}
incremental::Int8
end

Expand Down
87 changes: 62 additions & 25 deletions base/util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -399,42 +399,79 @@ printstyled(io::IO, msg...; bold::Bool=false, color::Union{Int,Symbol}=:normal)
with_output_color(print, color, io, msg...; bold=bold)
printstyled(msg...; bold::Bool=false, color::Union{Int,Symbol}=:normal) =
printstyled(stdout, msg...; bold=bold, color=color)

"""
Base.julia_cmd(juliapath=joinpath(Sys.BINDIR::String, julia_exename()))

Return a julia command similar to the one of the running process.
Propagates the `--cpu-target`, `--sysimage`, --compile `, `--depwarn`
and `--inline` command line arguments.
Propagates any of the `--cpu-target`, `--sysimage`, `--compile`, `--sysimage-native-code`,
`--compiled-modules`, `--inline`, `--check-bounds`, `--optimize`, `-g`,
`--code-coverage`, and `--depwarn`
command line arguments that are not at their default values.

Among others, `--math-mode`, `--warn-overwrite`, and `--trace-compile` are notably not propagated currently.

!!! compat "Julia 1.1"
The `--inline` flag is only propagated in Julia 1.1 and later.
Only the `--cpu-target`, `--sysimage`, `--depwarn`, `--compile` and `--check-bounds` flags were propagated before Julia 1.1.
"""
function julia_cmd(julia=joinpath(Sys.BINDIR::String, julia_exename()))
opts = JLOptions()
cpu_target = unsafe_string(opts.cpu_target)
image_file = unsafe_string(opts.image_file)
compile = if opts.compile_enabled == 0
"no"
elseif opts.compile_enabled == 2
"all"
elseif opts.compile_enabled == 3
"min"
else
"yes"
end
depwarn = if opts.depwarn == 0
"no"
elseif opts.depwarn == 2
"error"
else
"yes"
end
inline = if opts.can_inline == 0
"no"
else
"yes"
end
`$julia -C$cpu_target -J$image_file --compile=$compile --depwarn=$depwarn --inline=$inline`
addflags = String[]
let compile = if opts.compile_enabled == 0
"no"
elseif opts.compile_enabled == 2
"all"
elseif opts.compile_enabled == 3
"min"
else
"" # default = "yes"
end
isempty(compile) || push!(addflags, "--compile=$compile")
end
let depwarn = if opts.depwarn == 0
"no"
elseif opts.depwarn == 2
"error"
else
"" # default = "yes"
end
isempty(depwarn) || push!(addflags, "--depwarn=$depwarn")
end
let check_bounds = if opts.check_bounds == 1
"yes" # on
elseif opts.check_bounds == 2
"no" # off
else
"" # "default"
end
isempty(check_bounds) || push!(addflags, "--check-bounds=$check_bounds")
end
opts.can_inline == 0 && push!(addflags, "--inline=no")
opts.use_compiled_modules == 0 && push!(addflags, "--compiled-modules=no")
opts.opt_level == 2 || push!(addflags, "-O$(opts.opt_level)")
push!(addflags, "-g$(opts.debug_level)")
if opts.code_coverage != 0
# Forward the code-coverage flag only if applicable (if the filename is pid-dependent)
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")
elseif opts.code_coverage == 2
push!(addflags, "--code-coverage=all")
end
isempty(coverage_file) || push!(addflags, "--code-coverage=$coverage_file")
end
end
if opts.malloc_log != 0
if opts.malloc_log == 1
push!(addflags, "--track-allocation=user")
elseif opts.malloc_log == 2
push!(addflags, "--track-allocation=all")
end
end
return `$julia -C$cpu_target -J$image_file $addflags`
end

function julia_exename()
Expand Down
70 changes: 59 additions & 11 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1696,7 +1696,7 @@ static void write_log_data(logdata_t &logData, const char *extension)
std::ifstream inf(filename.c_str());
if (inf.is_open()) {
std::string outfile = filename + extension;
std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out);
std::ofstream outf(outfile.c_str(), std::ofstream::trunc | std::ofstream::out | std::ofstream::binary);
char line[1024];
int l = 1;
unsigned block = 0;
Expand All @@ -1722,7 +1722,7 @@ static void write_log_data(logdata_t &logData, const char *extension)
else
outf << (value - 1);
outf.width(0);
outf << " " << line << std::endl;
outf << " " << line << '\n';
}
outf.close();
inf.close();
Expand All @@ -1732,20 +1732,68 @@ static void write_log_data(logdata_t &logData, const char *extension)
}

extern "C" int jl_getpid();
extern "C" void jl_write_coverage_data(void)

static void write_lcov_data(logdata_t &logData, const std::string &outfile)
{
std::ostringstream stm;
stm << jl_getpid();
std::string outf = "." + stm.str() + ".cov";
write_log_data(coverageData, outf.c_str());
std::ofstream outf(outfile.c_str(), std::ofstream::ate | std::ofstream::out | std::ofstream::binary);
//std::string base = std::string(jl_options.julia_bindir);
//base = base + "/../share/julia/base/";
logdata_t::iterator it = logData.begin();
for (; it != logData.end(); it++) {
const std::string &filename = it->first();
const std::vector<logdata_block*> &values = it->second;
if (!values.empty()) {
//if (!isabspath(filename.c_str()))
// filename = base + filename;
outf << "SF:" << filename << '\n';
size_t n_covered = 0;
size_t n_instrumented = 0;
size_t lno = 0;
for (auto &itv : values) {
if (itv) {
logdata_block &data = *itv;
for (int i = 0; i < logdata_blocksize; i++) {
auto cov = data[i];
if (cov > 0) {
n_instrumented++;
if (cov > 1)
n_covered++;
outf << "DA:" << lno << ',' << (cov - 1) << '\n';
}
lno++;
}
}
else {
lno += logdata_blocksize;
}
}
outf << "LH:" << n_covered << '\n';
outf << "LF:" << n_instrumented << '\n';
outf << "end_of_record\n";
}
}
outf.close();
}

extern "C" void jl_write_coverage_data(const char *output)
{
if (output) {
StringRef output_pattern(output);
if (output_pattern.endswith(".info"))
write_lcov_data(coverageData, jl_format_filename(output_pattern));
}
else {
std::ostringstream stm;
stm << "." << jl_getpid() << ".cov";
write_log_data(coverageData, stm.str().c_str());
}
}

extern "C" void jl_write_malloc_log(void)
{
std::ostringstream stm;
stm << jl_getpid();
std::string outf = "." + stm.str() + ".mem";
write_log_data(mallocData, outf.c_str());
stm << "." << jl_getpid() << ".mem";
write_log_data(mallocData, stm.str().c_str());
}

// --- constant determination ---
Expand Down Expand Up @@ -7368,7 +7416,7 @@ extern "C" void *jl_init_llvm(void)
cl::ParseEnvironmentOptions("Julia", "JULIA_LLVM_ARGS");

jl_page_size = jl_getpagesize();
imaging_mode = jl_generating_output();
imaging_mode = jl_generating_output() && !jl_options.incremental;
jl_init_debuginfo();

#ifdef USE_POLLY
Expand Down
56 changes: 52 additions & 4 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ 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);
void jl_write_coverage_data(const char*);
void jl_write_malloc_log(void);
void jl_write_compiler_output(void);

Expand Down Expand Up @@ -222,7 +222,7 @@ JL_DLLEXPORT void jl_atexit_hook(int exitcode)
jl_write_compiler_output();
jl_print_gc_stats(JL_STDERR);
if (jl_options.code_coverage)
jl_write_coverage_data();
jl_write_coverage_data(jl_options.output_code_coverage);
if (jl_options.malloc_log)
jl_write_malloc_log();
if (jl_base_module) {
Expand Down Expand Up @@ -462,7 +462,7 @@ int isabspath(const char *in)
}

static char *abspath(const char *in, int nprefix)
{ // compute an absolute path location, so that chdir doesn't change the file reference
{ // compute an absolute realpath location, so that chdir doesn't change the file reference
// ignores (copies directly over) nprefix characters at the start of abspath
#ifndef _OS_WINDOWS_
char *out = realpath(in + nprefix, NULL);
Expand Down Expand Up @@ -495,6 +495,8 @@ static char *abspath(const char *in, int nprefix)
jl_error("fatal error: unexpected error while retrieving current working directory");
}
out = (char*)malloc(path_size + 1 + sz + nprefix);
if (!out)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
memcpy(out, in, nprefix);
memcpy(out + nprefix, path, path_size);
out[nprefix + path_size] = PATHSEPSTRING[0];
Expand All @@ -508,6 +510,8 @@ static char *abspath(const char *in, int nprefix)
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
}
char *out = (char*)malloc(n + nprefix);
if (!out)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
DWORD m = GetFullPathName(in + nprefix, n, out + nprefix, NULL);
if (n != m + 1) {
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
Expand All @@ -517,6 +521,38 @@ static char *abspath(const char *in, int nprefix)
return out;
}

// create an absolute-path copy of the input path format string
// formed as `joinpath(replace(pwd(), "%" => "%%"), in)`
// unless `in` starts with `%`
vtjnash marked this conversation as resolved.
Show resolved Hide resolved
static const char *absformat(const char *in)
{
if (in[0] == '%' || isabspath(in))
return in;
// get an escaped copy of cwd
size_t path_size = PATH_MAX;
char path[PATH_MAX];
if (uv_cwd(path, &path_size)) {
jl_error("fatal error: unexpected error while retrieving current working directory");
}
size_t sz = strlen(in) + 1;
size_t i, fmt_size = 0;
for (i = 0; i < path_size; i++)
fmt_size += (path[i] == '%' ? 2 : 1);
char *out = (char*)malloc(fmt_size + 1 + sz);
if (!out)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
fmt_size = 0;
for (i = 0; i < path_size; i++) { // copy-replace pwd portion
char c = path[i];
out[fmt_size++] = c;
if (c == '%')
out[fmt_size++] = '%';
}
out[fmt_size++] = PATHSEPSTRING[0]; // path sep
memcpy(out + fmt_size, in, sz); // copy over format, including nul
return out;
}

static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
{ // this function resolves the paths in jl_options to absolute file locations as needed
// and it replaces the pointers to `julia_bindir`, `julia_bin`, `image_file`, and output file paths
Expand All @@ -527,13 +563,17 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
// calling `julia_init()`
char *free_path = (char*)malloc(PATH_MAX);
size_t path_size = PATH_MAX;
if (!free_path)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
if (uv_exepath(free_path, &path_size)) {
jl_error("fatal error: unexpected error while retrieving exepath");
}
if (path_size >= PATH_MAX) {
jl_error("fatal error: jl_options.julia_bin path too long");
}
jl_options.julia_bin = (char*)malloc(path_size+1);
if (!jl_options.julia_bin)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
memcpy((char*)jl_options.julia_bin, free_path, path_size);
((char*)jl_options.julia_bin)[path_size] = '\0';
if (!jl_options.julia_bindir) {
Expand All @@ -550,6 +590,8 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
if (rel == JL_IMAGE_JULIA_HOME && !isabspath(jl_options.image_file)) {
// build time path, relative to JULIA_BINDIR
free_path = (char*)malloc(PATH_MAX);
if (!free_path)
jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno));
int n = snprintf(free_path, PATH_MAX, "%s" PATHSEPSTRING "%s",
jl_options.julia_bindir, jl_options.image_file);
if (n >= PATH_MAX || n < 0) {
Expand All @@ -572,8 +614,13 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
jl_options.outputbc = abspath(jl_options.outputbc, 0);
if (jl_options.machine_file)
jl_options.machine_file = abspath(jl_options.machine_file, 0);
if (jl_options.project && strncmp(jl_options.project, "@.", strlen(jl_options.project)) != 0)
if (jl_options.project
&& strcmp(jl_options.project, "@.") != 0
&& strcmp(jl_options.project, "@") != 0
&& strcmp(jl_options.project, "") != 0)
jl_options.project = abspath(jl_options.project, 0);
if (jl_options.output_code_coverage)
jl_options.output_code_coverage = absformat(jl_options.output_code_coverage);
vtjnash marked this conversation as resolved.
Show resolved Hide resolved

const char **cmdp = jl_options.cmds;
if (cmdp) {
Expand All @@ -600,6 +647,7 @@ void _julia_init(JL_IMAGE_SEARCH rel)
jl_get_ptls_states_getter();
#endif
jl_ptls_t ptls = jl_get_ptls_states();
(void)ptls; assert(ptls); // make sure early that we have initialized ptls
jl_safepoint_init();
libsupport_init();
htable_new(&jl_current_modules, 0);
Expand Down
16 changes: 13 additions & 3 deletions src/jloptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ jl_options_t jl_options = { 0, // quiet
NULL, // output-jit-bc
NULL, // output-o
NULL, // output-ji
NULL, // output-code_coverage
0, // incremental
0 // image_file_specified
};
Expand Down Expand Up @@ -129,6 +130,9 @@ static const char opts[] =
// instrumentation options
" --code-coverage={none|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"
vtjnash marked this conversation as resolved.
Show resolved Hide resolved
// 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"
" Count bytes allocated by each source line\n\n"

Expand Down Expand Up @@ -437,12 +441,18 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp)
break;
case opt_code_coverage:
if (optarg != NULL) {
if (!strcmp(optarg,"user"))
size_t endof = strlen(optarg);
if (!strcmp(optarg, "user"))
codecov = JL_LOG_USER;
else if (!strcmp(optarg,"all"))
else if (!strcmp(optarg, "all"))
codecov = JL_LOG_ALL;
else if (!strcmp(optarg,"none"))
else if (!strcmp(optarg, "none"))
codecov = JL_LOG_NONE;
else if (endof > 5 && !strcmp(optarg + endof - 5, ".info")) {
if (codecov == JL_LOG_NONE)
codecov = JL_LOG_ALL;
jl_options.output_code_coverage = optarg;
}
else
jl_errorf("julia: invalid argument to --code-coverage (%s)", optarg);
break;
Expand Down
Loading