in cli/bpfcov.c [1308:1535]
int out(struct root_args *args)
{
log_info(args, "%s\n", "generating coverage visualization...");
char report_path[PATH_MAX];
strcpy(report_path, args->report_path);
// Generating a *.profdata for each input *.profraw
char profdata[args->num_profraw][PATH_MAX];
memset(profdata, 0, args->num_profraw * PATH_MAX * sizeof(char));
char bpfobjs[args->num_profraw][PATH_MAX];
memset(bpfobjs, 0, args->num_profraw * PATH_MAX * sizeof(char));
int c = 0;
char** ptr = args->profraw;
for (char* profraw = *ptr; profraw; profraw = *++ptr) {
// Looking up for *.bpf.obj file sibling to the current input *.profraw file
char *profraw_wo_ext = strdup(profraw);
strip_extension(profraw_wo_ext);
char bpfobj_path[PATH_MAX];
int bpfobj_path_len = snprintf(bpfobj_path, PATH_MAX, "%s.bpf.obj", profraw_wo_ext);
if (bpfobj_path_len >= PATH_MAX)
{
log_fata(args, "%s\n", "bpf.obj output path too long");
}
log_info(args, "looking for BPF coverage object at '%s'\n", bpfobj_path);
if (access(bpfobj_path, F_OK) != 0)
{
log_fata(args, "could not find the BPF coverage object at '%s'", bpfobj_path);
}
free(profraw_wo_ext);
// Storing the *.bpf.obj file for later
strncpy(bpfobjs[c], bpfobj_path, PATH_MAX);
// Creating the *.profdata path (relative to the execution directory)
char *profraw_name = strdup(basename(profraw));
strtok(profraw_name, ".");
char profdata_path[PATH_MAX];
int profdata_path_len = snprintf(profdata_path, PATH_MAX, "%s.profdata", profraw_name);
if (profdata_path_len >= PATH_MAX)
{
log_fata(args, "%s\n", "profdata output path too long");
}
free(profraw_name);
// Storing the output *.profdata file for later
strncpy(profdata[c], profdata_path, PATH_MAX);
log_info(args, "generating '%s'\n", profdata[c]);
// Generating the single *.profdata file
pid_t data_pid;
switch ((data_pid = fork()))
{
case -1:
log_fata(args, "%s\n", "could not fork");
break;
case 0:
log_debu(args, "llvm-profdata merge -sparse %s -o %s\n", profraw, profdata[c]);
int devnull = open("/dev/null", O_WRONLY | O_CREAT, 0666);
dup2(devnull, STDERR_FILENO);
execlp("llvm-profdata", "llvm-profdata", "merge", "-sparse", profraw, "-o", profdata[c], NULL);
close(devnull);
log_fata(args, "%s\n", "could not exec llvm-profdata");
break;
}
wait_or_exit(args, data_pid, "llvm-profdata: exited with status");
c++;
}
// Merge all the *.profdata into one
char target_profdata[PATH_MAX];
int num_profdata = sizeof(profdata) / PATH_MAX;
if (num_profdata > 1) {
strncpy(target_profdata, "all.profdata", PATH_MAX);
log_info(args, "merging into '%s'\n", target_profdata);
pid_t merge_pid;
switch ((merge_pid = fork()))
{
case -1:
log_fata(args, "%s\n", "could not fork");
break;
case 0:
char *arguments[num_profdata + 6];
arguments[0] = "llvm-profdata";
arguments[1] = "merge";
arguments[2] = "-sparse";
arguments[3] = "-o";
arguments[4] = target_profdata;
for (int i = 0; i < num_profdata; i++) {
arguments[i + 5] = profdata[i];
}
arguments[num_profdata + 5] = NULL;
log_debu(args, "%s ", arguments[0]);
for (int a = 1; a < num_profdata + 5; a++) {
if (DEBUG) {
print_log(3, NULL, args, "%s ", arguments[a]);
}
}
if (DEBUG) {
print_log(3, NULL, args, "%s", "\n");
}
int devnull = open("/dev/null", O_WRONLY | O_CREAT, 0666);
dup2(devnull, STDERR_FILENO);
execvp("llvm-profdata", arguments);
close(devnull);
log_fata(args, "%s\n", "could not exec llvm-profdata");
break;
}
wait_or_exit(args, merge_pid, "llvm-profdata: exited with status");
}
else {
strncpy(target_profdata, profdata[0], PATH_MAX);
}
int num_bpfobj_params = num_profdata * 2;
int devnull = open("/dev/null", O_WRONLY | O_CREAT, 0666);
if (devnull == -1) {
log_fata(args, "could not open %s\n", "/dev/null");
}
log_info(args, "about to generate the %s coverage report in '%s'\n", format_string[args->out_format], report_path);
switch (args->out_format)
{
case FORMAT_html:
pid_t html_pid;
switch ((html_pid = fork()))
{
case -1:
log_fata(args, "%s\n", "could not fork");
break;
case 0:
char *arguments[num_bpfobj_params + 11];
arguments[0] = "llvm-cov";
arguments[1] = "show";
arguments[2] = "--format=html";
arguments[3] = "--show-branches=count";
arguments[4] = "--show-line-counts-or-regions";
arguments[5] = "--show-region-summary";
arguments[6] = "--output-dir";
arguments[7] = report_path;
arguments[8] = "-instr-profile";
arguments[9] = target_profdata;
for (int i = 0; i < num_profdata; i++) {
int off = i * 2;
arguments[off + 10] = "-object";
arguments[off + 11] = bpfobjs[i];
}
arguments[num_bpfobj_params + 10] = NULL;
log_debu(args, "%s ", arguments[0]);
for (int a = 1; a < num_bpfobj_params + 10; a++) {
if (DEBUG) {
print_log(3, NULL, args, "%s ", arguments[a]);
}
}
if (DEBUG) {
print_log(3, NULL, args, "%s", "\n");
}
dup2(devnull, STDERR_FILENO);
execvp("llvm-cov", arguments);
break;
}
close(devnull);
wait_or_exit(args, html_pid, "llvm-cov exited with status");
break;
case FORMAT_lcov:
/* Fallthrough */
case FORMAT_json:
int outfile = open(report_path, O_RDWR | O_CREAT, 0600);
if (outfile == -1) {
log_fata(args, "could not open %s\n", report_path);
}
pid_t export_pid;
switch ((export_pid = fork()))
{
case -1:
log_fata(args, "%s\n", "could not fork");
break;
case 0:
char *arguments[num_bpfobj_params + 8];
arguments[0] = "llvm-cov";
arguments[1] = "export";
arguments[2] = "--format=text";
arguments[3] = "--show-branch-summary";
arguments[4] = "--show-region-summary";
arguments[5] = "-instr-profile";
arguments[6] = target_profdata;
for (int i = 0; i < num_profdata; i++) {
int off = i > 0 ? (i + 1) : i;
arguments[off + 7] = "-object";
arguments[off + 8] = bpfobjs[i];
}
arguments[num_bpfobj_params + 7] = NULL;
log_debu(args, "%s ", arguments[0]);
for (int a = 1; a < num_bpfobj_params + 7; a++) {
if (DEBUG) {
print_log(3, NULL, args, "%s ", arguments[a]);
}
}
if (DEBUG) {
print_log(3, NULL, args, "%s", "\n");
}
dup2(devnull, STDERR_FILENO);
dup2(outfile, STDOUT_FILENO);
execvp("llvm-cov", arguments);
break;
}
close(devnull);
close(outfile);
wait_or_exit(args, export_pid, "llvm-cov exited with status");
break;
}
return 0;
}