Skip to content

Commit

Permalink
Merge pull request #4394 from gamesh411/include-all-archs-in-ldlibrar…
Browse files Browse the repository at this point in the history
…ypath

[analyzer] Include arch subdirectories in LD_LIBRARY_PATH for logging
  • Loading branch information
dkrupp authored Dec 10, 2024
2 parents d084d79 + 4817933 commit 2f1aa32
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 22 deletions.
20 changes: 14 additions & 6 deletions analyzer/codechecker_analyzer/analyzer_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,20 @@ def __init__(self):
# Original caller environment of CodeChecker for external binaries
self.__original_env = None

self.logger_lib_dir_path = os.path.join(
self._data_files_dir_path, 'ld_logger', 'lib')

if not os.path.exists(self.logger_lib_dir_path):
self.logger_lib_dir_path = os.path.join(
self._lib_dir_path, 'codechecker_analyzer', 'ld_logger', 'lib')
# Find the path which has the architectures for the built ld_logger
# shared objects.
ld_logger_path = Path(self._data_files_dir_path, "ld_logger", "lib")
if not ld_logger_path.is_dir():
ld_logger_path = Path(
self._lib_dir_path, "codechecker_analyzer", "ld_logger", "lib"
)

# Add all children (architecture) paths to be later used in the
# LD_LIBRARY_PATH environment variable during logging of compiler
# invocations.
self.logger_lib_dir_path = ":".join(
[str(arch) for arch in ld_logger_path.iterdir() if arch.is_dir()]
)

self.logger_bin = None
self.logger_file = None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ def gen_test(path, mode):
which compare the output of the command with the
stored expected output.
"""

def test(self):
self.check_one_file(path, mode)
return test
Expand Down Expand Up @@ -787,3 +788,100 @@ def test_html_checker_url(self):
encoding="utf-8", errors="ignore") as f:
content = f.read()
self.assertTrue(re.search('"url": ""', content))

def test_mixed_architecture_logging(self):
"""
Test if CodeChecker can properly log compilation commands when the
build process involves both 32-bit and 64-bit binaries acting as
build drivers.
This verifies that the LD_LIBRARY_PATH setup in analyzer_context.py
correctly includes all architecture versions of the ld_logger.so
library, and that logging works with this setup.
"""
with tempfile.TemporaryDirectory() as tmp_dir:
# We use a temporary directory, because we produce multiple files
# during this test, and it is easier to clean up.
mixed_arch_driver = os.path.join(
self.test_dir,
"mixed_arch_driver.c"
)
simple_c = os.path.join(
self.test_dir,
"simple.c"
)

shutil.copy(mixed_arch_driver, tmp_dir)
shutil.copy(simple_c, tmp_dir)

best_gcc_candidate_in_path = [
path
for path in os.environ["PATH"].split(":")
if os.path.exists(os.path.join(path, "gcc"))
]
if not best_gcc_candidate_in_path:
self.skipTest(f"No gcc candidate found in PATH:\
{os.environ['PATH']}")

try:
subprocess.check_call(
["gcc", "-m32", "-c", "simple.c"],
cwd=tmp_dir,
stderr=subprocess.PIPE,
)
except subprocess.CalledProcessError as err:
self.skipTest(f"No 32-bit compilation support available:\
{err.stderr}")
try:
subprocess.check_call(
["gcc", "-m64", "-c", "simple.c"],
cwd=tmp_dir,
stderr=subprocess.PIPE,
)
except subprocess.CalledProcessError as err:
self.skipTest(f"No 64-bit compilation support available:\
{err.stderr}")

subprocess.check_call(
["gcc", "-m32", "mixed_arch_driver.c", "-o", "driver32"],
cwd=tmp_dir
)
subprocess.check_call(
["gcc", "-m64", "mixed_arch_driver.c", "-o", "driver64"],
cwd=tmp_dir
)

log_file = os.path.join(tmp_dir, "compile_commands.json")
cmd = [
"CodeChecker", "log", "-b", "./driver32;./driver64",
"-o", log_file,
]

_, err, returncode = call_command(cmd, cwd=tmp_dir,
env=self.env)

self.assertEqual(returncode, 0, f"CodeChecker log failed:\
{err}")

# Verify the logged commands
with open(log_file, "r", encoding="utf-8") as f:
logged_commands = json.load(f)

# The buildlog should have 4 commands - 2 from each driver
# (and for each driver there is one with a '-m32' and one with a
# '-m64' flag)
self.assertEqual(
len(logged_commands), 4, f"Logged commands: {logged_commands}"
)

commands = [entry["command"] for entry in logged_commands]
self.assertTrue(
2 == len([cmd for cmd in commands if "-m32" in cmd]),
f"Expected 2 32-bit compilations. Logged commands:\
{logged_commands}"
)
self.assertTrue(
2 == len([cmd for cmd in commands if "-m64" in cmd]),
f"Expected 2 64-bit compilations. Logged commands:\
{logged_commands}"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#include <stdlib.h>

int main(void) {
// These commands are intended to test CodeChecker's ability to build-log
// cross compilations.
system("gcc -m32 simple.c -o simple32");
system("gcc -m64 simple.c -o simple64");
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main(void) {
return 0;
}
18 changes: 6 additions & 12 deletions analyzer/tools/build-logger/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,24 +76,18 @@ LIB_DIR = $(BUILD_DIR)/lib
all: ldlogger ldlogger_32.so ldlogger_64.so pack32bit pack64bit

pack32bit: 32bit packbin
for x86dir in 'i386' 'i486' 'i586' 'i686'; do \
mkdir -p $(LIB_DIR)/$$x86dir ; \
cp ldlogger_32.so $(LIB_DIR)/$$x86dir/ldlogger.so ; \
done
mkdir -p $(LIB_DIR)/32bit ; \
cp ldlogger_32.so $(LIB_DIR)/32bit/ldlogger.so ; \
rm -f ldlogger_32.so

pack64bit: 64bit packbin
for x8664dir in 'x86_64'; do \
mkdir -p $(LIB_DIR)/$$x8664dir ; \
cp ldlogger_64.so $(LIB_DIR)/$$x8664dir/ldlogger.so ; \
done
mkdir -p $(LIB_DIR)/64bit ; \
cp ldlogger_64.so $(LIB_DIR)/64bit/ldlogger.so ; \
rm -f ldlogger_64.so

pack64bit_only: 64bit_only packbin64
for x8664dir in 'x86_64'; do \
mkdir -p $(LIB_DIR)/$$x8664dir ; \
cp ldlogger_64.so $(LIB_DIR)/$$x8664dir/ldlogger.so ; \
done
mkdir -p $(LIB_DIR)/64bit ; \
cp ldlogger_64.so $(LIB_DIR)/64bit/ldlogger.so ; \
rm -f ldlogger_64.so

# pack binary
Expand Down
7 changes: 5 additions & 2 deletions analyzer/tools/build-logger/tests/unit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

import json
import os
import platform
import shlex
import subprocess
import tempfile
import unittest
from pathlib import Path
from typing import Mapping, Optional, Tuple, Sequence

REPO_ROOT = os.path.abspath(os.getenv("REPO_ROOT"))
Expand Down Expand Up @@ -106,10 +106,13 @@ def read_actual_json(self) -> str:
return fd.read()

def get_envvars(self) -> Mapping[str, str]:
libdir = Path(LOGGER_DIR, "lib")
return {
"PATH": os.getenv("PATH"),
"LD_PRELOAD": "ldlogger.so",
"LD_LIBRARY_PATH": os.path.join(LOGGER_DIR, "lib"),
"LD_LIBRARY_PATH": ':'.join([str(arch) for arch in
libdir.iterdir()
if arch.is_dir()]),
"CC_LOGGER_GCC_LIKE": "gcc:g++:clang:clang++:/cc:c++",
"CC_LOGGER_FILE": self.logger_file,
"CC_LOGGER_DEBUG_FILE": self.logger_debug_file,
Expand Down
7 changes: 5 additions & 2 deletions docs/analyzer/user_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,10 @@ If this is not possible, you can work around the situation by
specifying the absolute path of the `ldlogger.so` in the `LD_PRELOAD`:

```sh
LD_PRELOAD=<CODECHECKER_DIR>/ld_logger/lib/x86_64/ldlogger.so CodeChecker log -o compile_commands.json -b "make -j2"
# For 64-bit compilers
LD_PRELOAD=<CODECHECKER_DIR>/ld_logger/lib/64bit/ldlogger.so CodeChecker log -o compile_commands.json -b "make -j2"
# For 32-bit compilers
LD_PRELOAD=<CODECHECKER_DIR>/ld_logger/lib/32bit/ldlogger.so CodeChecker log -o compile_commands.json -b "make -j2"
```

#### Change user inside the build command
Expand Down Expand Up @@ -2718,4 +2721,4 @@ The following actions are available:
setting.

If none of the filter options is provided, then that setting is not applied on
any report.
any report.

0 comments on commit 2f1aa32

Please sign in to comment.