No description
  • Rust 59.1%
  • Svelte 27%
  • TypeScript 10.8%
  • CSS 2.6%
  • GDB 0.3%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Tan Siewert d3ed013750
Some checks failed
CI / lint (push) Has been cancelled
CI / build-web (push) Has been cancelled
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled
Note about project being slopped
Signed-off-by: Tan Siewert <tan.siewert@9elements.com>
2026-07-21 23:49:06 +02:00
.config Add placeholder test for CI to pass 2026-05-11 17:19:21 +02:00
.forgejo/workflows Real initial commit 2026-06-30 14:25:54 +02:00
config ffdc-api: fix metrics 2026-07-16 14:53:29 +02:00
examples Real initial commit 2026-06-30 14:25:54 +02:00
ffdc-api ffdc-api: fix metrics 2026-07-16 14:53:29 +02:00
ffdc-core Migrate to Diesel for ORM, fix OAuth2 claims, fix parsing performance, 2026-07-16 13:49:06 +02:00
web-ui web-ui: please the linter 2026-07-16 13:50:37 +02:00
.dockerignore Real initial commit 2026-06-30 14:25:54 +02:00
.gitattributes Real initial commit 2026-06-30 14:25:54 +02:00
.gitignore Migrate to Diesel for ORM, fix OAuth2 claims, fix parsing performance, 2026-07-16 13:49:06 +02:00
Cargo.lock ffdc-tui: remove 2026-07-03 16:13:53 +02:00
Cargo.toml ffdc-tui: remove 2026-07-03 16:13:53 +02:00
compose.gdb.yml Real initial commit 2026-06-30 14:25:54 +02:00
compose.yml Real initial commit 2026-06-30 14:25:54 +02:00
Dockerfile ffdc-tui: remove 2026-07-03 16:13:53 +02:00
Dockerfile.gdb ffdc-tui: remove 2026-07-03 16:13:53 +02:00
README.md Note about project being slopped 2026-07-21 23:49:06 +02:00

FFDC Parser

IMPORTANT! This project is a full slop (i.e. written with a clanker) as I needed something that I can use to parse and share hundreds of phosphor-debug-collector archives.

Use this project with precaution! I don't give any guarantee and will likely yeet it again because it's slop "quality" and do it myself cleanly (when I have time lol)

Parses OpenBMC First Failure Data Capture (FFDC) dump archives. Extracts, classifies, and renders structured data from obmcdump tarballs.

Architecture

modular crate:

Crate Description
ffdc-core Core library: compression detection, archive extraction, file classification, structured parsers, journal parser, coredump analysis, SQLite metadata store
ffdc-api Axum REST API with upload, job tracking, search, compare, OIDC session auth
web-ui SvelteKit static SPA frontend

Usage (API)

ffdc-api --db-path /path/to/ffdc.db
Flag / Env Default Description
--bind / FFDC_BIND 0.0.0.0:3000 Listen address
--db-path / FFDC_DB_PATH ffdc.db SQLite database path
--cache-dir / FFDC_CACHE_DIR cache Upload extraction cache
--max-upload-bytes / FFDC_MAX_UPLOAD_BYTES 8388608 Max upload size
--auth-enabled / FFDC_AUTH_ENABLED false Require authentication
--session-ttl-secs / FFDC_SESSION_TTL_SECS 86400 Session cookie TTL
--oidc-issuer / FFDC_OIDC_ISSUER - OIDC issuer URL
--oidc-client-id / FFDC_OIDC_CLIENT_ID - OIDC client ID
--oidc-client-secret / FFDC_OIDC_CLIENT_SECRET - OIDC client secret
--oidc-redirect-url / FFDC_OIDC_REDIRECT_URL - OIDC callback URL
--static-dir / FFDC_STATIC_DIR - SPA static files dir

Web UI

cd web-ui && npm install && npm run build

Serve the built files from web-ui/build/ - either set FFDC_STATIC_DIR on the API or serve with any HTTP server.

E2E Tests

Uses Playwright against the real Rust API server with the built SvelteKit UI.

cd web-ui
npm install
npx playwright install chromium   # one-time: download browser binaries
npm run build
npm run test:e2e

The playwright.config.ts automatically builds the UI, starts the Rust API server on port 3000 (serving both the API and the static UI), runs tests, and shuts down.

To run against an externally managed API (e.g. already running on port 3000 with the UI on a SvelteKit dev server at port 5173):

PLAYWRIGHT_BASE_URL=http://localhost:5173 npx playwright test

This skips the built-in webServer and points the tests at your UI origin. The API is expected to be already running (at any port your UI proxies to).

Authentication

When --auth-enabled is set, the API uses OIDC (Authorization Code + PKCE) with httpOnly session cookies.

Flow:

  1. User clicks "Sign in with SSO" on the login page
  2. Browser is redirected to /api/v1/auth/oidc/start, which generates a PKCE challenge and redirects to the OIDC provider
  3. Provider authenticates the user and redirects back to /api/v1/auth/oidc/callback
  4. Server exchanges the authorization code (with PKCE verifier), fetches userinfo, creates a session, sets an httpOnly cookie, and redirects to /
  5. All subsequent requests include the session cookie automatically

When --auth-enabled is false (default), all routes are accessible without authentication.

Docker

docker compose up --build

Runs the API on port 3000 with persistent storage. Set FFDC_AUTH_ENABLED=true and OIDC environment variables in compose.yml for auth.

GDB coredump analysis

The gdb Cargo feature adds automated coredump analysis: backtrace, crashing thread, signal, and register state. Analysis requires a Yocto cross-GDB binary and the matching target sysroot for debug-symbol resolution.

Because BMC hardware families use different CPU architectures (ARM 32-bit, AArch64, RISC-V, …), each with its own SDK, the server supports multiple SDKs simultaneously. The correct SDK is selected automatically based on the archive filename.

How SDK selection works

After the archive is parsed, the server reads the dump's own metadata to pick the right SDK, not the archive filename (which is unreliable)!

Dump field Source file Example value
hostnamectl.architecture hostnamectl output "arm", "arm64"
hostnamectl.hostname hostnamectl output "rainier-bmc"
os-release.TARGET_MACHINE os-release "ast2600"

Selection rules:

  1. Each SDK entry is checked in order. Entries with at least one non-empty criterion are checked first (specific match).
  2. architectures must match hostnamectl.architecture (substring, case-insensitive).
  3. machines must match TARGET_MACHINE or hostname (substring, case-insensitive).
  4. Both criteria are ANDed. An empty list means "any" for that criterion.
  5. If no specific entry matches, the first catch-all entry (both lists empty) is used.
  6. If still no match, the on_no_match policy applies: "skip" (default), "warn", "error".

If automatic selection picks the wrong SDK (e.g. because metadata is missing), the user can override it explicitly. See Manual SDK override below.

1. Create an SDK config file

Copy the example and fill in the real paths for your SDKs:

cp config/sdks.example.json config/sdks.json
{
  "sdks": [
    {
      "name": "ARM Cortex-A (AST2600 / AST2500)",
      "gdb_path": "/sdks/arm/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gdb",
      "sysroot": "/sdks/arm/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi",
      "architectures": ["arm"],
      "machines": ["ast2600", "ast2500", "rainier", "everest"]
    },
    {
      "name": "AArch64 (AST2700)",
      "gdb_path": "/sdks/aarch64/sysroots/x86_64-pokysdk-linux/usr/bin/aarch64-poky-linux/aarch64-poky-linux-gdb",
      "sysroot": "/sdks/aarch64/sysroots/cortexa35-poky-linux",
      "architectures": ["arm64", "aarch64"],
      "machines": ["ast2700", "bletchley"]
    }
  ],
  "on_no_match": "skip"
}

Fields:

Field Required Description
name yes Unique label; used in logs and for manual override
gdb_path yes Absolute path to the cross-GDB binary inside the SDK
sysroot no Absolute path to the target sysroot (enables debug-symbol loading)
architectures no hostnamectl.architecture substrings; empty means any arch
machines no TARGET_MACHINE or hostname substrings; empty means any machine

on_no_match values: "skip" (default) · "warn" · "error"

Manual SDK override

If no SDK matched (GDB analysis was skipped) or the wrong SDK was selected, trigger a re-parse with an explicit SDK name:

# 1. List available SDKs
curl http://localhost:3000/api/v1/sdks

# 2. Re-parse with a specific SDK
curl -X POST http://localhost:3000/api/v1/ffdc/<uuid>/reparse \
     -H 'Content-Type: application/json' \
     -d '{"sdk": "ARM Cortex-A (AST2600 / AST2500)"}'

The sdk field must match the name of an entry in your SDK config exactly. Omit the body (or omit the sdk field) to use automatic selection.

2. Build and run

Place your Yocto SDK installer scripts in the repo root, then use the compose.gdb.yml overlay which switches the build to Dockerfile.gdb:

cp config/sdks.example.json config/sdks.json   # fill in your paths
docker compose -f compose.yml -f compose.gdb.yml up --build

compose.gdb.yml sets FFDC_SDK_CONFIG=/sdks/sdks.json. Bake your sdks.json into the image by placing it in the repo root and adding a COPY sdks.json /sdks/sdks.json line to Dockerfile.gdb, or mount it at runtime:

docker run -p 3000:3000 \
  -v /path/to/data:/data \
  -v $(pwd)/config/sdks.json:/sdks/sdks.json:ro \
  -e FFDC_SDK_CONFIG=/sdks/sdks.json \
  ffdc-api-gdb

See the comments inside Dockerfile.gdb for SDK installer filenames and install-path constraints.

Single-target setup

If you only have one architecture, create a sdks.json with a single entry and leave both architectures and machines empty so it acts as a catch-all:

{
  "sdks": [
    {
      "name": "ARM Cortex-A",
      "gdb_path": "/sdks/arm/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gdb",
      "sysroot": "/sdks/arm/sysroots/cortexa7t2hf-neon-poky-linux-gnueabi",
      "architectures": [],
      "machines": []
    }
  ],
  "on_no_match": "skip"
}

Development

Build

Each crate builds independently:

cargo build -p ffdc-core
cargo build -p ffdc-api
cargo build --workspace

Test & Lint

cargo test
cargo clippy --workspace
cargo doc --no-deps --document-private-items