int out()

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;
}