Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5138b7d
feat(cli): add reusable argument parser with option suggestions
SizzleUnrlsd Feb 25, 2026
5c72165
feat(app): extract analyzer orchestration into AnalyzerApp service
SizzleUnrlsd Feb 25, 2026
3d4fee2
refactor(cli): slim main and delegate parsing/execution to services
SizzleUnrlsd Feb 25, 2026
e3fd7a5
feat(analyzer): add modular analysis pipeline and services
SizzleUnrlsd Feb 25, 2026
5628632
feat(reachability): add static unreachable stack-access classification
SizzleUnrlsd Feb 25, 2026
62eba3a
refactor(core): delegate module analysis to analyzer pipeline
SizzleUnrlsd Feb 25, 2026
1fc9cb4
refactor(logging): route analyzer traces through coretrace logger
SizzleUnrlsd Feb 25, 2026
f8cfbf2
build(core): register modular sources and add opt-in analyzer unit te…
SizzleUnrlsd Feb 25, 2026
fa0f6da
test(harness): extend CLI coverage and integrate optional analyzer un…
SizzleUnrlsd Feb 25, 2026
70010c7
test(unit): add analyzer module unit tests for location, reachability…
SizzleUnrlsd Feb 25, 2026
702fb8e
docs(architecture): document analyzer module responsibilities and pat…
SizzleUnrlsd Feb 25, 2026
a399eee
docs(readme): add library arg forwarding guidance and architecture re…
SizzleUnrlsd Feb 25, 2026
4de3748
feat(extern-project): support local source fallback and forwarded ana…
SizzleUnrlsd Feb 25, 2026
69f4469
docs(extern-project): add consumer build and run instructions
SizzleUnrlsd Feb 25, 2026
1eaf2eb
ci(integration): add consumer fixture analysis job
SizzleUnrlsd Feb 25, 2026
ffda4f2
test(duplicate-if): add expectations for nested duplicate else-if war…
SizzleUnrlsd Feb 25, 2026
3b06ec6
docs(test): clarify expected cross-tu uninitialized warning behavior
SizzleUnrlsd Feb 25, 2026
733ba3c
test(fixtures): add ci consumer CMake fixture for compile_commands in…
SizzleUnrlsd Feb 26, 2026
d4cf72c
chore(style): format code with clang-format
SizzleUnrlsd Feb 26, 2026
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
78 changes: 78 additions & 0 deletions .github/workflows/test-ci-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
- "Dockerfile"
- "action.yml"
- "scripts/ci/**"
- "fixtures/ci-consumer-project/**"
- ".github/workflows/test-ci-integration.yml"
pull_request:
branches: [main]
Expand Down Expand Up @@ -169,3 +170,80 @@ jobs:
test -f docker-ci-results.sarif || { echo "CI SARIF missing!"; exit 1; }
test -f docker-ci-results.json || { echo "CI JSON missing!"; exit 1; }
echo "CI script Docker test passed!"

# =================================================================
# Job 3: Test analyzer on a minimal consumer project in CI
# =================================================================
test-consumer-project:
name: Test Consumer Project
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Configure consumer fixture (compile_commands.json)
run: |
cmake -S fixtures/ci-consumer-project -B fixtures/ci-consumer-project/build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON

- name: Run CoreTrace Stack Analyzer (consumer fixture)
uses: ./
id: consumer-analysis
with:
compile-commands: fixtures/ci-consumer-project/build/compile_commands.json
fail-on: none
base-dir: ${{ github.workspace }}
analysis-profile: fast
resource-model: default
resource-cache-memory-only: "true"
warnings-only: "false"
sarif-file: artifacts/consumer/action-results.sarif
json-file: artifacts/consumer/action-results.json
upload-sarif: false

- name: Validate consumer JSON and SARIF
run: |
python3 - <<'PY'
import json
from pathlib import Path

json_path = Path("artifacts/consumer/action-results.json")
sarif_path = Path("artifacts/consumer/action-results.sarif")
assert json_path.is_file(), f"Missing JSON output: {json_path}"
assert sarif_path.is_file(), f"Missing SARIF output: {sarif_path}"

payload = json.loads(json_path.read_text(encoding="utf-8"))
diagnostics = payload.get("diagnostics", [])
assert diagnostics, "Expected at least one diagnostic in JSON output"
print(f"JSON diagnostics: {len(diagnostics)}")

sarif = json.loads(sarif_path.read_text(encoding="utf-8"))
assert sarif.get("version") == "2.1.0", "Invalid SARIF version"
runs = sarif.get("runs", [])
assert runs, "SARIF has no runs"
results = runs[0].get("results", [])
assert results, "Expected at least one SARIF result"

for result in results:
for loc in result.get("locations", []):
physical = loc.get("physicalLocation", {})
artifact = physical.get("artifactLocation", {})
uri = artifact.get("uri", "")
assert not uri.startswith("/"), f"URI must be relative: {uri}"
region = physical.get("region", {})
col = int(region.get("startColumn", 1))
assert col >= 1, f"Invalid SARIF startColumn: {col}"
print(f"SARIF results: {len(results)}")
PY

- name: Upload consumer fixture artifacts
uses: actions/upload-artifact@v4
with:
name: ci-consumer-analysis
path: |
artifacts/consumer/action-results.json
artifacts/consumer/action-results.sarif
31 changes: 31 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ option(ENABLE_STACK_USAGE "Emit per-function stack usage (.su) files" OFF)
# Communs Sources
# ===========================
set(STACK_ANALYZER_SOURCES
src/analyzer/AnalysisPipeline.cpp
src/analyzer/DiagnosticEmitter.cpp
src/analyzer/LocationResolver.cpp
src/analyzer/ModulePreparationService.cpp
src/app/AnalyzerApp.cpp
src/cli/ArgParser.cpp
src/StackUsageAnalyzer.cpp
src/analysis/AllocaUsage.cpp
src/analysis/AnalyzerUtils.cpp
Expand All @@ -71,6 +77,7 @@ set(STACK_ANALYZER_SOURCES
src/analysis/InvalidBaseReconstruction.cpp
src/analysis/MemIntrinsicOverflow.cpp
src/analysis/ResourceLifetimeAnalysis.cpp
src/analysis/Reachability.cpp
src/analysis/SizeMinusKWrites.cpp
src/analysis/StackBufferAnalysis.cpp
src/analysis/StackComputation.cpp
Expand Down Expand Up @@ -187,6 +194,30 @@ if(BUILD_CLI)
endif()
endif()

# =========
# TESTING
# =========
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
include(CTest)

option(BUILD_ANALYZER_UNIT_TESTS "Build fine-grained analyzer module unit tests" OFF)
if(BUILD_ANALYZER_UNIT_TESTS)
add_executable(stack_usage_analyzer_unit_tests
test/unit/analyzer_module_unit_tests.cpp
)

target_link_libraries(stack_usage_analyzer_unit_tests
PRIVATE
stack_usage_analyzer_lib
)

add_test(
NAME analyzer_module_unit_tests
COMMAND stack_usage_analyzer_unit_tests ${CMAKE_CURRENT_SOURCE_DIR}
)
endif()
endif()

# ============
# FORMATTING
# ============
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ python3 scripts/ci/run_code_analysis.py \
GitHub Actions consumer example is available at:
- `docs/ci/github-actions-consumer.yml`
- `docs/ci/github-actions-module-consumer.yml` (consume this repo directly via `uses:`)
- Analyzer architecture notes: `docs/architecture/analyzer-modules.md`

### Reusable GitHub Action module (for other repositories)

Expand Down Expand Up @@ -274,6 +275,32 @@ Examples:
When inputs are auto-discovered from `compile_commands.json` and multiple files are analyzed,
the CLI auto-selects `fast` unless you explicitly pass `--analysis-profile=full`.

### Library mode: forward analyzer args from another CLI

If you embed the analyzer as a library and still want to reuse analyzer-style
arguments (`--mode=...`, `--jobs=...`, etc.), use the CLI parser bridge:

- `ctrace::stack::cli::parseArguments(const std::vector<std::string>&)`
- `ctrace::stack::cli::parseCommandLine(const std::string&)`

Example:

```cpp
#include "cli/ArgParser.hpp"

auto parsed = ctrace::stack::cli::parseCommandLine(
"--mode=abi --analysis-profile=fast --warnings-only --jobs=4"
);
if (parsed.status == ctrace::stack::cli::ParseStatus::Error) {
// handle parsed.error
}

ctrace::stack::AnalysisConfig cfg = parsed.parsed.config;
```

This keeps one single source of truth for option semantics between CLI and
library consumers.

When `--compile-commands` is provided and no input file is passed on the CLI,
the analyzer automatically uses `compile_commands.json` as the source of truth:
- it analyzes supported entries (`.c`, `.cc`, `.cpp`, `.cxx`, `.ll`)
Expand Down
94 changes: 94 additions & 0 deletions docs/architecture/analyzer-modules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Analyzer Modules Architecture

This document describes the module split introduced around `StackUsageAnalyzer` to reduce coupling, improve testability, and keep `main.cpp` and the public API focused on orchestration.

## Goals

- Keep analysis orchestration separated from LLVM parsing and diagnostic formatting details.
- Make core services independently testable with focused unit tests.
- Preserve existing integration behavior while enabling smaller regression checks.

## Modules

### `src/analyzer/AnalysisPipeline.cpp`

Role:
- Entry point for module-level analysis execution.
- Coordinates preparation, analysis passes, and diagnostic emission.

Pattern:
- `Facade` over lower-level analysis services.

Why:
- A single coordinator makes control flow explicit while avoiding a very large `StackUsageAnalyzer.cpp`.

### `src/analyzer/ModulePreparationService.cpp`

Role:
- Builds `ModuleAnalysisContext`.
- Computes local stack sizes, filtered call graph, and recursion metadata.

Pattern:
- `Application Service` with a small `Builder-like` output (`PreparedModule`).

Why:
- Preparation logic is pure module state derivation and should be reusable without triggering diagnostic side effects.

### `src/analyzer/LocationResolver.cpp`

Role:
- Converts LLVM debug locations into normalized source coordinates.
- Resolves source location for allocas using debug intrinsics fallbacks.

Pattern:
- `Domain Service` (stateless policy logic).

Why:
- Location derivation has multiple LLVM-specific fallbacks; isolating it keeps diagnostics code simpler and easier to test.

### `src/analyzer/DiagnosticEmitter.cpp`

Role:
- Converts analysis findings into final diagnostics outputs.
- Central place for rule IDs, severities, and source location mapping.

Pattern:
- `Adapter` between analysis model objects and output/report models.

Why:
- Separates "what was found" from "how it is reported".

### `src/analysis/Reachability.cpp`

Role:
- Contains static reachability heuristics for stack access findings.

Pattern:
- `Policy` function isolated from pass execution.

Why:
- Reachability criteria can evolve independently and be regression-tested as a focused unit.

## Data Flow

1. Input pipeline loads/normalizes LLVM module.
2. `ModulePreparationService` creates `PreparedModule`.
3. Analysis passes compute findings.
4. `Reachability` filters/annotates specific findings.
5. `LocationResolver` provides precise locations.
6. `DiagnosticEmitter` produces diagnostics consumed by CLI/lib callers.

## Test Strategy

Fine-grained unit tests live in:
- `test/unit/analyzer_module_unit_tests.cpp`

Covered modules:
- `LocationResolver`
- `Reachability`
- `ModulePreparationService`

Execution:
- Built as `stack_usage_analyzer_unit_tests` (standalone project builds only).
- Wired into `run_test.py` via `check_analyzer_module_unit_tests()`.
- Also registered in CTest as `analyzer_module_unit_tests`.
22 changes: 17 additions & 5 deletions extern-project/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,23 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)

include(FetchContent)

FetchContent_Declare(
stack_analyzer
GIT_REPOSITORY https://github.com/CoreTrace/coretrace-stack-analyzer.git
GIT_TAG main
)
# Prefer the local parent checkout when this example is built from the
# repository tree; fallback to GitHub when used standalone.
set(STACK_ANALYZER_LOCAL_SOURCE "${CMAKE_CURRENT_LIST_DIR}/..")
if(EXISTS "${STACK_ANALYZER_LOCAL_SOURCE}/CMakeLists.txt")
message(STATUS "Using local stack analyzer source: ${STACK_ANALYZER_LOCAL_SOURCE}")
FetchContent_Declare(
stack_analyzer
SOURCE_DIR "${STACK_ANALYZER_LOCAL_SOURCE}"
)
else()
message(STATUS "Using remote stack analyzer source from GitHub")
FetchContent_Declare(
stack_analyzer
GIT_REPOSITORY https://github.com/CoreTrace/coretrace-stack-analyzer.git
GIT_TAG main
)
endif()

FetchContent_MakeAvailable(stack_analyzer)

Expand Down
32 changes: 32 additions & 0 deletions extern-project/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# extern-project (library consumer example)

This folder demonstrates how to consume `coretrace::stack_usage_analyzer_lib`
from another project and forward analyzer options from your own CLI.

## Build

```bash
cmake -S extern-project -B extern-project/build
cmake --build extern-project/build -j
```

## Run

```bash
./extern-project/build/sa_consumer \
test/alloca/oversized-constant.c \
build/compile_commands.json \
--mode=abi \
--analysis-profile=fast \
--warnings-only \
--jobs=4 \
--format=sarif
```

Notes:
- Input file is the first positional argument.
- `compile_commands.json` path is the second positional argument.
- All remaining arguments are parsed with the analyzer's CLI parser bridge
(`ctrace::stack::cli::parseArguments(...)`).
- Do not pass `--compile-commands` / `--compdb` in forwarded args here; use
the second positional argument.
Loading
Loading