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

[SYCLomatic][Intercept-build] Refine intercept-build tool when tool chain is available #2511

Merged
139 changes: 74 additions & 65 deletions clang/tools/scan-build-py/lib/libear/ear.c
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,11 @@ static int call_eaccess(const char *pathname, int mode) {
}

int eaccess(const char *pathname, int mode) {
int ret = call_eaccess(pathname, mode);
if (ret == 0) {
return 0;
}

int len = strlen(pathname);
if (len == 4 && pathname[3] == 'c' && pathname[2] == 'c' &&
pathname[1] == 'v' && pathname[0] == 'n') {
Expand All @@ -500,7 +505,65 @@ int eaccess(const char *pathname, int mode) {
// To handle case like "/path/to/nvcc foo.cu ..."
return 0;
}
return call_eaccess(pathname, mode);
return ret;
}

const char *get_intercept_stub_path(void) {

const char *intercept_stub_path = getenv("INTERCEPT_STUB_PATH");
if (intercept_stub_path) {
return intercept_stub_path;
}

perror("bear: failed to get value of environment variable "
"'INTERCEPT_STUB_PATH'\n");
exit(EXIT_FAILURE);
}

static int call_stat(const char *pathname, struct stat *statbuf) {
typedef int (*func)(const char *, struct statbuf *);
DLSYM(func, fp, "stat");
int const result = (*fp)(pathname, statbuf);
return result;
}

int stat(const char *pathname, struct stat *statbuf) {
int ret = call_stat(pathname, statbuf);
if (ret == 0) {
return 0;
}
int len = strlen(pathname);
if (len == 4 && pathname[3] == 'c' && pathname[2] == 'c' &&
pathname[1] == 'v' && pathname[0] == 'n') {
// To handle case like "nvcc foo.cu ..."

const char *nvcc_path = getenv("INTERCEPT_COMPILE_PATH");
if (nvcc_path) {
call_stat(nvcc_path, statbuf);
return 0;
}

pathname = get_intercept_stub_path();
call_stat(pathname, statbuf);
return 0;
}

if (len > 4 && pathname[len - 1] == 'c' && pathname[len - 2] == 'c' &&
pathname[len - 3] == 'v' && pathname[len - 4] == 'n' &&
pathname[len - 5] == '/') {
// To handle case like "/path/to/nvcc foo.cu ..."

const char *nvcc_path = getenv("INTERCEPT_COMPILE_PATH");
if (nvcc_path) {
call_stat(nvcc_path, statbuf);
return 0;
}

pathname = get_intercept_stub_path();
call_stat(pathname, statbuf);
return 0;
}
return ret;
}

/*
Expand Down Expand Up @@ -1616,39 +1679,7 @@ void emit_cmake_warning(char const *argv[], int argc) {
// returns no return value.
char *replace_binary_name(const char *src, const char *pos, int compiler_idx,
const char *const compiler_array[]) {
FILE *fp;
char replacement[PATH_MAX];
char file_path[PATH_MAX];

fp = popen("which dpct", "r");
if (fp == NULL) {
perror("bear: failed to run command 'which dpct'\n");
exit(EXIT_FAILURE);
}

if (fgets(replacement, PATH_MAX, fp) == NULL) {
perror("bear: fgets\n");
exit(EXIT_FAILURE);
}
pclose(fp);
replacement[strlen(replacement) - 1] =
'\0'; // to remove extra '\n' added by "which dpct"

char *res = realpath(
replacement,
file_path); // to get the canonicalized absolute pathname in file_path

if (!res) {
perror("bear: realpath\n");
exit(EXIT_FAILURE);
}
if ((strlen(file_path) + strlen("lib/libear/intercept-stub") -
strlen("bin/dpct")) >= PATH_MAX) {
perror("bear: strcpy overflow, path to dpct is too long.\n");
exit(EXIT_FAILURE);
}
strcpy(file_path + strlen(file_path) - strlen("bin/dpct"),
"lib/libear/intercept-stub");
const char *file_path = get_intercept_stub_path();

// To malloc required size of physical memory it really needs may fail in
// some case, so malloc 4K bytes (one physical page) instead.
Expand Down Expand Up @@ -1688,53 +1719,31 @@ int is_tool_available(const char *pathname) {

int len = strlen(pathname);
int is_nvcc = 0;
int is_nvcc_available = 0;

const char *env_var = "INTERCEPT_COMPILE_PATH";
char *value = getenv(env_var);
if (value) {
is_nvcc_available = 1;
}

if (len == 4 && pathname[3] == 'c' && pathname[2] == 'c' &&
pathname[1] == 'v' && pathname[0] == 'n') {
// To handle case like "nvcc"
is_nvcc = 1;
}

if (len > 4 && pathname[len - 1] == 'c' && pathname[len - 2] == 'c' &&
pathname[len - 3] == 'v' && pathname[len - 4] == 'n' &&
pathname[len - 5] == '/') {
// To handle case like "/path/to/nvcc"
is_nvcc = 1;
}

if (is_nvcc) {
int idx = len - 4;
for (; idx > 0 && !isspace(pathname[idx]); idx--)
;
struct stat buffer;
if (stat(pathname + idx, &buffer) == 0) {
if (is_nvcc_available) {
return 1;
}
return 0;
}

int is_ld = 0;
if (len == 2 && pathname[1] == 'd' && pathname[0] == 'l') {
// To handle case like "ld"
is_ld = 1;
}

if (len > 2 && pathname[len - 1] == 'd' && pathname[len - 2] == 'l' &&
pathname[len - 3] == '/') {
// To handle case like "/path/to/ld"
is_ld = 1;
}

if (is_ld) {
int idx = len - 2;
for (; idx > 0 && !isspace(pathname[idx]); idx--)
;
struct stat buffer;
if (stat(pathname + idx, &buffer) == 0) {
return 1;
}
return 0;
}

return 1;
}

Expand Down
59 changes: 59 additions & 0 deletions clang/tools/scan-build-py/lib/libscanbuild/intercept.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@
import json
import glob
import logging
# SYCLomatic_CUSTOMIZATION begin
import subprocess
# SYCLomatic_CUSTOMIZATION end
from libear import build_libear, TemporaryDirectory
from libscanbuild import (
command_entry_point,
Expand Down Expand Up @@ -119,6 +122,9 @@ def post_processing(commands):
entries_post = []
occur_set = set()
for entry in entries:
# filter out the commmands that use /tmp/ directory
if " /tmp/" in entry["command"]:
continue
if not ("file" in entry):
key = entry["directory"] + entry["command"]
if key not in occur_set:
Expand Down Expand Up @@ -162,8 +168,49 @@ def post_processing(commands):
# dump the compilation database
with open(args.cdb, "w+") as handle:
json.dump(entries, handle, sort_keys=True, indent=4)

return exit_code

# SYCLomatic_CUSTOMIZATION begin
def set_sys_env_var(variable_name, value):
"""Set an environment variable and ensure it is available to other processes."""
try:
os.environ[variable_name] = value
export_command = f'export {variable_name}="{value}"'
subprocess.run(export_command, shell=True, check=True)
except subprocess.CalledProcessError as e:
logging.debug(f"Error while exporting the environment variable: {e}")
except Exception as e:
logging.debug(f"An error occurred: {e}")

def find_nvcc_and_set_env_variable():
"""Check if nvcc is available and set environment variable."""
# Check if nvcc exists in the path of environment PATH
nvcc_path = None
for path in os.environ.get("PATH", "").split(os.pathsep):
possible_path = os.path.join(path, "nvcc")
if os.path.isfile(possible_path) and os.access(possible_path, os.X_OK):
nvcc_path = possible_path
break

if nvcc_path:
set_sys_env_var("INTERCEPT_COMPILE_PATH", nvcc_path)
return

# Search for nvcc in CUDA SDK default installation directories.
# Look for directories like /usr/local/cuda-*
cuda_dirs = glob.glob("/usr/local/cuda-*")
for cuda_dir in cuda_dirs:
possible_path = os.path.join(cuda_dir, "bin", "nvcc")
if os.path.isfile(possible_path) and os.access(possible_path, os.X_OK):
nvcc_path = possible_path
break

if nvcc_path:
set_system_environment_variable("INTERCEPT_COMPILE_PATH", nvcc_path)
return

# SYCLomatic_CUSTOMIZATION end

def setup_environment(args, destination):
"""Sets up the environment for the build command.
Expand All @@ -172,6 +219,18 @@ def setup_environment(args, destination):
The exec calls will be logged by the 'libear' preloaded library or by the
'wrapper' programs."""

# SYCLomatic_CUSTOMIZATION begin
# if nvcc is available in the PATH environment variable or in default
# CUDA installation directories, set envrionment INTERCEPT_COMPILE_PATH used in 'libear'.
find_nvcc_and_set_env_variable()

cur_dir = os.path.dirname(sys.argv[0])
file_path_1 = os.path.join(cur_dir, "..", "lib", "libear","intercept-stub")
file_path_2 = os.path.join(cur_dir, "..", "opt", "dpct", "lib", "libear","intercept-stub")
intercept_stub_path = file_path_1 if os.access(file_path_1, os.X_OK) else file_path_2
set_sys_env_var("INTERCEPT_STUB_PATH", intercept_stub_path)
# SYCLomatic_CUSTOMIZATION end

c_compiler = args.cc if "cc" in args else "cc"
cxx_compiler = args.cxx if "cxx" in args else "c++"

Expand Down