Skip to content
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
9 changes: 8 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,16 @@ jobs:
with:
extra_args: --all-files

toolchain-detection-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run toolchain detection tests
run: bash core/scripts/test_detect_toolchain.sh

tests:
runs-on: ubuntu-latest
steps:
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
Expand Down
25 changes: 24 additions & 1 deletion core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,29 @@ cc_library(
)


# Generate a header with C++ toolchain information
genrule(
name = "toolchain_info_gen",
srcs = ["scripts/detect_toolchain.sh"],
outs = ["toolchain_info.h"],
cmd = """
case "$(COMPILATION_MODE)" in
opt) BUILD_TYPE="Release" ;;
dbg) BUILD_TYPE="Debug" ;;
fastbuild) BUILD_TYPE="FastBuild" ;;
*) BUILD_TYPE="$(COMPILATION_MODE)" ;;
esac
bash $(location scripts/detect_toolchain.sh) "$(CC)" "$$BUILD_TYPE" > $@
""",
toolchains = ["@bazel_tools//tools/cpp:current_cc_toolchain"],
)

cc_library(
name = "toolchain_info",
hdrs = [":toolchain_info_gen"],
includes = ["."],
)

# Define the codspeed library
cc_library(
name = "codspeed",
Expand All @@ -87,7 +110,7 @@ cc_library(
":walltime_mode": ["CODSPEED_ENABLED", "CODSPEED_WALLTIME", "CODSPEED_MODE_DISPLAY=\\\"walltime\\\""],
"//conditions:default": [],
}),
deps = [":instrument_hooks"],
deps = [":instrument_hooks", ":toolchain_info"],
visibility = ["//visibility:public"],
)

Expand Down
18 changes: 17 additions & 1 deletion core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ include(FetchContent)
FetchContent_Declare(
instrument_hooks_repo
GIT_REPOSITORY https://github.com/CodSpeedHQ/instrument-hooks
GIT_TAG 89fb72a076ec71c9eca6eee9bca98bada4b4dfb4
GIT_TAG e86719c70c9c0b1646db182a7c748230e243dace
)
FetchContent_MakeAvailable(instrument_hooks_repo)
FetchContent_GetProperties(instrument_hooks_repo)
Expand Down Expand Up @@ -96,6 +96,22 @@ target_link_libraries(codspeed PRIVATE instrument_hooks)
# Version
add_compile_definitions(CODSPEED_VERSION="${CODSPEED_VERSION}")

# Collect compiler toolchain information for environment reporting
# Use the shared detect_toolchain.sh script (same as Bazel) for consistent output
execute_process(
COMMAND bash "${CMAKE_CURRENT_SOURCE_DIR}/scripts/detect_toolchain.sh"
"${CMAKE_CXX_COMPILER}" "${CMAKE_BUILD_TYPE}"
OUTPUT_VARIABLE CODSPEED_TOOLCHAIN_HEADER
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE DETECT_TOOLCHAIN_RESULT
)
if(DETECT_TOOLCHAIN_RESULT EQUAL 0)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/toolchain_info.h" "${CODSPEED_TOOLCHAIN_HEADER}\n")
else()
message(WARNING "detect_toolchain.sh failed, toolchain info will not be available")
endif()
target_include_directories(codspeed PRIVATE "${CMAKE_CURRENT_BINARY_DIR}")

# Specify the include directories for users of the library
target_include_directories(
codspeed
Expand Down
25 changes: 22 additions & 3 deletions core/include/measurement.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,25 @@ inline bool measurement_is_instrumented() {
inline void measurement_set_metadata() {
std::string version = get_version();
instrument_hooks_set_integration(g_hooks, "codspeed-cpp", version.c_str());

// Report C++ toolchain information
#ifdef CODSPEED_CXX_COMPILER_ID
instrument_hooks_set_environment(g_hooks, "C++ Compiler", "compiler_id",
CODSPEED_CXX_COMPILER_ID);
#endif
#ifdef CODSPEED_CXX_COMPILER_VERSION
instrument_hooks_set_environment(g_hooks, "C++ Compiler", "version",
CODSPEED_CXX_COMPILER_VERSION);
#endif
#ifdef CODSPEED_CXX_COMPILER_FULL_VERSION
instrument_hooks_set_environment(g_hooks, "C++ Compiler", "build",
CODSPEED_CXX_COMPILER_FULL_VERSION);
#endif
#ifdef CODSPEED_BUILD_TYPE
instrument_hooks_set_environment(g_hooks, "C++ Compiler", "build_type",
CODSPEED_BUILD_TYPE);
#endif
instrument_hooks_write_environment(g_hooks, static_cast<uint32_t>(getpid()));
}

ALWAYS_INLINE void measurement_start() {
Expand All @@ -54,9 +73,9 @@ ALWAYS_INLINE uint64_t measurement_current_timestamp() {
return instrument_hooks_current_timestamp();
}

ALWAYS_INLINE int8_t measurement_add_marker(uint8_t marker_type,
uint64_t timestamp) {
auto pid = getpid();
ALWAYS_INLINE uint8_t measurement_add_marker(uint8_t marker_type,
uint64_t timestamp) {
auto pid = static_cast<uint32_t>(getpid());
return instrument_hooks_add_marker(g_hooks, pid, marker_type, timestamp);
}

Expand Down
2 changes: 1 addition & 1 deletion core/instrument-hooks
40 changes: 40 additions & 0 deletions core/scripts/detect_toolchain.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env bash
# Detect C++ compiler toolchain information from `<compiler> --version` output.
# Used by Bazel genrule to generate toolchain_info.h.
#
# Usage: detect_toolchain.sh <compiler_path> <build_type>
# Output: Writes a C header to stdout with CODSPEED_CXX_* defines.

set -e

COMPILER="${1:?Usage: detect_toolchain.sh <compiler_path> <build_type>}"
BUILD_TYPE="${2:?Usage: detect_toolchain.sh <compiler_path> <build_type>}"

FULL_VERSION=$("$COMPILER" --version 2>/dev/null | head -n1) || FULL_VERSION="unknown"

COMPILER_ID="unknown"
COMPILER_VERSION="unknown"

if echo "$FULL_VERSION" | grep -qi "clang"; then
COMPILER_ID="Clang"
COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown"
elif echo "$FULL_VERSION" | grep -qi "gcc\|g++\|GNU"; then
COMPILER_ID="GNU"
COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown"
elif echo "$FULL_VERSION" | grep -qi "MSVC\|Microsoft"; then
COMPILER_ID="MSVC"
COMPILER_VERSION=$(echo "$FULL_VERSION" | grep -oP '\d+\.\d+\.\d+' | head -n1) || COMPILER_VERSION="unknown"
fi

cat << HEADER_EOF
// Auto-generated - do not edit
#ifndef CODSPEED_TOOLCHAIN_INFO_H
#define CODSPEED_TOOLCHAIN_INFO_H

#define CODSPEED_CXX_COMPILER_ID "$COMPILER_ID"
#define CODSPEED_CXX_COMPILER_VERSION "$COMPILER_VERSION"
#define CODSPEED_CXX_COMPILER_FULL_VERSION "$FULL_VERSION"
#define CODSPEED_BUILD_TYPE "$BUILD_TYPE"

#endif // CODSPEED_TOOLCHAIN_INFO_H
HEADER_EOF
101 changes: 101 additions & 0 deletions core/scripts/test_detect_toolchain.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env bash
# Manual tests for detect_toolchain.sh
# Run: bash core/scripts/test_detect_toolchain.sh
#
# Uses mock compiler scripts to simulate different --version outputs.

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DETECT_SCRIPT="$SCRIPT_DIR/detect_toolchain.sh"
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT

PASS=0
FAIL=0

assert_contains() {
local label="$1"
local expected="$2"
local actual="$3"
if echo "$actual" | grep -qF "$expected"; then
PASS=$((PASS + 1))
else
FAIL=$((FAIL + 1))
echo "FAIL: $label"
echo " expected to contain: $expected"
echo " got: $actual"
fi
}

# Create a mock compiler that outputs a given --version string
make_mock_compiler() {
local name="$1"
local version_output="$2"
local path="$TMPDIR/$name"
cat > "$path" << EOF
#!/usr/bin/env bash
echo "$version_output"
EOF
chmod +x "$path"
echo "$path"
}

# --- Test cases ---

echo "Running detect_toolchain.sh tests..."
echo

# Test: GCC
mock=$(make_mock_compiler "gcc-mock" "gcc (GCC) 14.3.0")
output=$(bash "$DETECT_SCRIPT" "$mock" "Release")
assert_contains "GCC compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output"
assert_contains "GCC version" 'CODSPEED_CXX_COMPILER_VERSION "14.3.0"' "$output"
assert_contains "GCC full_version" 'CODSPEED_CXX_COMPILER_FULL_VERSION "gcc (GCC) 14.3.0"' "$output"
assert_contains "GCC build_type" 'CODSPEED_BUILD_TYPE "Release"' "$output"

# Test: g++
mock=$(make_mock_compiler "gpp-mock" "g++ (GCC) 13.2.1 20230801")
output=$(bash "$DETECT_SCRIPT" "$mock" "Debug")
assert_contains "g++ compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output"
assert_contains "g++ version" 'CODSPEED_CXX_COMPILER_VERSION "13.2.1"' "$output"
assert_contains "g++ build_type" 'CODSPEED_BUILD_TYPE "Debug"' "$output"

# Test: Clang
mock=$(make_mock_compiler "clang-mock" "clang version 19.1.7 (https://github.com/llvm/llvm-project abc123)")
output=$(bash "$DETECT_SCRIPT" "$mock" "Release")
assert_contains "Clang compiler_id" 'CODSPEED_CXX_COMPILER_ID "Clang"' "$output"
assert_contains "Clang version" 'CODSPEED_CXX_COMPILER_VERSION "19.1.7"' "$output"

# Test: Apple Clang
mock=$(make_mock_compiler "apple-clang-mock" "Apple clang version 15.0.0 (clang-1500.0.40.1)")
output=$(bash "$DETECT_SCRIPT" "$mock" "Release")
assert_contains "Apple Clang compiler_id" 'CODSPEED_CXX_COMPILER_ID "Clang"' "$output"
assert_contains "Apple Clang version" 'CODSPEED_CXX_COMPILER_VERSION "15.0.0"' "$output"

# Test: Ubuntu GCC
mock=$(make_mock_compiler "ubuntu-gcc-mock" "gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0")
output=$(bash "$DETECT_SCRIPT" "$mock" "Release")
assert_contains "Ubuntu GCC compiler_id" 'CODSPEED_CXX_COMPILER_ID "GNU"' "$output"
assert_contains "Ubuntu GCC version" 'CODSPEED_CXX_COMPILER_VERSION "13.3.0"' "$output"

# Test: Unknown compiler
mock=$(make_mock_compiler "unknown-mock" "some-compiler v2.0")
output=$(bash "$DETECT_SCRIPT" "$mock" "Release")
assert_contains "Unknown compiler_id" 'CODSPEED_CXX_COMPILER_ID "unknown"' "$output"
assert_contains "Unknown version" 'CODSPEED_CXX_COMPILER_VERSION "unknown"' "$output"

# Test: Header guard present
assert_contains "Header guard" '#ifndef CODSPEED_TOOLCHAIN_INFO_H' "$output"
assert_contains "Header guard endif" '#endif' "$output"

# --- Summary ---

echo
TOTAL=$((PASS + FAIL))
if [ "$FAIL" -eq 0 ]; then
echo "All $TOTAL tests passed."
else
echo "$FAIL/$TOTAL tests FAILED."
exit 1
fi
4 changes: 4 additions & 0 deletions core/src/codspeed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
#include <string>
#include <vector>

#if __has_include("toolchain_info.h")
#include "toolchain_info.h"
#endif

#include "measurement.hpp"

namespace codspeed {
Expand Down
Loading