Skip to main content

callsight — compile-time function tracing for C/C++

ci docs PyPI License: MIT

Docs: https://harshithsunku.github.io/callsight/ · Status & roadmap

Add entry/exit timing hooks to every function in a C/C++ project at compile time, with zero edits to its sources, control exactly which files, folders, or functions get hooks from one config file, and turn the resulting traces into per-function hotspot reports. Works with GCC and Clang, C and C++, with GNU Make and CMake projects.

Install

uv tool install callsight        # from PyPI; puts `callsight` on your PATH
uv tool install 'callsight[ui]'  # same, plus the optional web UI
uv tool install .                # or from a source checkout

(Requires uv. The tool itself is Python stdlib-only; the runtime it injects is dependency-free C.)

Web UI

callsight ui                # serves http://127.0.0.1:8321

A local web app that walks the whole workflow against any project on the machine: browse for the project folder, edit its trace.config, preview the instrumentation selection, build the instrumented profile (Make or CMake — CMake is fetched ephemerally via uvx if not installed), run the binary with tracing enabled, and view the sortable hotspot report. No root needed; bind address defaults to localhost.

Adopt it in your project (2 steps)

cd /path/to/your/project
callsight init .          # copies runtime + build wiring, writes trace.config

Then follow the printed wiring snippet for your build system:

  • Make: include callsight/Makefile.callsight, add an instrument target linking $(TRACE_OBJ) with -no-pie (snippet printed by init).
  • CMake: include(CallSight) + callsight_instrument(<target>), then configure with -DCALLSIGHT_INSTRUMENT=ON.

Collect and analyze

make instrument                                 # or: cmake --build build-instr
TRACE_ENABLE=1 TRACE_MAX=1000000 ./yourapp      # collect (inert without TRACE_ENABLE=1)
callsight analyze traces/ --exe ./yourapp --top 20

The analyzer reports calls / inclusive / self / max time per function, resolving symbols — including static functions — with addr2line. unmatched_exits=0 means a clean trace.

How it works

  1. callsight flags turns trace.config + your source list into -finstrument-functions plus compile-time exclude lists (-finstrument-functions-exclude-file-list/-exclude-function-list).
  2. The compiler emits calls to __cyg_profile_func_enter/exit at the entry/exit of every selected function. Excluded code emits no hook at all — compile-time exclusion is free at runtime.
  3. The hook runtime (trace.c, itself compiled without the flag) appends 32-byte events to a per-thread buffer — no locks, no malloc, no I/O in the hot path — flushing trace.<pid>.<tid>.bin when full or at exit.
  4. callsight analyze matches enter/exit events per thread and prints hotspot tables.

Selection strategy (important at scale)

Event volume is the main cost — a call-heavy program easily generates millions of events per second. Levers, cheapest first:

  1. Compile-time excludes in trace.config: run wide once, sort analyzer output by calls, exclude the chatty leaf helpers, rebuild. Typically cuts volume 10–100×.
  2. include directives: instrument only the subsystem under investigation.
  3. include-func (function/task level): name one entry function and callsight instruments exactly its call subtree, resolved statically from the sources — include-func workload_sort traces workload_sort and everything it calls, nothing else. Explore first with callsight select src/ --function workload_sort.
  4. Runtime gating: TRACE_ENABLE / TRACE_MAX control when and how much you pay; TRACE_THREADS="sort-*" traces only matching threads.
  5. Source opt-out (optional): __attribute__((no_instrument_function)).

callsight scan <dir> --config trace.config previews what a config selects.

Runtime knobs: TRACE_ENABLE (default off), TRACE_DIR (default ./traces), TRACE_MAX (global event cap — always set one for long runs), TRACE_THREADS (thread-name glob filter), TRACE_SHM / TRACE_SHM_SIZE (streaming mode, see below).

Remote streaming (devices, embedded)

On a constrained device you can't accumulate trace files — a busy program generates millions of events per second. Streaming mode keeps nothing on the device: the runtime flushes into a shared-memory ring, and a tiny on-device client forwards events ZSTD-compressed over raw TCP.

# analysis host (powerful machine):
callsight serve --port 9001 --out traces/     # needs callsight[stream]

# adopt with streaming support:
callsight init --stream /path/to/project      # adds trace_stream.c + zstd.c

# on the device:
cc -O2 -o callsight/trace_stream callsight/trace_stream.c callsight/zstd.c
./callsight/trace_stream /callsight0 <server-ip> 9001 &
TRACE_ENABLE=1 TRACE_SHM=/callsight0 ./yourapp.instr
  • The traced process does no disk or network I/O — only a shared-memory memcpy per buffered batch.
  • If the ring fills faster than the client drains (network slow, ring too small), events are dropped and counted, never blocking the workload; the server reports the drop count. Size the ring with TRACE_SHM_SIZE.
  • The server writes standard trace.stream.*.bin files — callsight analyze and the web UI consume them unchanged.
  • The client is self-contained C built against the vendored single-file zstd v1.5.7 (src/callsight/stream/zstd.c, generated from the official repo's build/single_file_libs; BSD license in zstd.LICENSE).

CLI reference

command purpose
callsight init <dir> [--build make|cmake] adopt into a project
callsight scan <dir> [--config c] preview instrumentation selection
callsight select <dir> --function F [--depth N] show a function's call subtree; emit config lines
callsight flags --config c -- srcs... print compiler flags (build integrations use this)
callsight analyze [traces/] [--exe bin] [--top N] hotspot report
callsight ui [--host H] [--port P] web UI (needs callsight[ui])
callsight serve [--host H] [--port P] [--out dir] TCP server for remote streams (needs callsight[stream])

Repo layout

  • src/callsight/ — the tool: cli.py, flags.py (config → compiler flags), analyze.py (offline analyzer); stdlib-only. src/callsight/ui/ is the optional web UI (FastAPI, only imported by callsight ui).
  • src/callsight/runtime/trace.c/trace.h/trace_shm.h, the hook runtime copied into adopted projects. Self-contained C, no deps beyond pthreads.
  • src/callsight/stream/trace_stream.c on-device streaming client + vendored single-file zstd v1.5.7 (zstd.c, BSD — see zstd.LICENSE).
  • src/callsight/share/Makefile.callsight, src/callsight/cmake/CallSight.cmake — build-system integrations.
  • tests/matrixlab/ — multi-threaded C11 demo workload; doubles as the end-to-end smoke test (make instrument → run → callsight analyze).
  • tests/cmake_demo/ — tiny CMake fixture for the CMake integration.
  • docs/instrumentation-options.md — survey of GCC/Clang compile-time instrumentation mechanisms and how they map to callsight's roadmap.

Known limitations

  • Inlined functions emit no hooks (there is no call boundary).
  • Overhead ~30–60 ns per event; a profiling build, not a production one.
  • A crashed/killed process loses each thread's buffered tail; clean exits flush everything.
  • exclude-file-list matching by the compiler is substring-based; unusually overlapping directory names can over-match.

Roadmap

  • Phase 2 — web UI: done (callsight ui, optional callsight[ui] extra). Next: richer report views (call graphs, flame graphs), live build-log streaming.
  • Phase 3 — remote streaming: done (TRACE_SHM ring → trace_stream client → ZSTD/TCP → callsight serve). Next: runtime on/off via -fpatchable-function-entry (see docs/instrumentation-options.md), live stream view in the web UI.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

callsight-0.1.1.tar.gz (5.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

callsight-0.1.1-py3-none-any.whl (569.1 kB view details)

Uploaded Python 3

File details

Details for the file callsight-0.1.1.tar.gz.

File metadata

  • Download URL: callsight-0.1.1.tar.gz
  • Upload date:
  • Size: 5.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for callsight-0.1.1.tar.gz
Algorithm Hash digest
SHA256 f699680b193eaa6d6cf4b7dad96a709917468ed4818f3b9b8ad1d4088a2a3331
MD5 ce144b814742e2dcaaa44fade926e4f8
BLAKE2b-256 67c1f118a54a59d990baa615363663da0b8c0d18b3e2180ab3b23eee55c0172a

See more details on using hashes here.

Provenance

The following attestation bundles were made for callsight-0.1.1.tar.gz:

Publisher: release.yml on harshithsunku/callsight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file callsight-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: callsight-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 569.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for callsight-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 58986ea5c97aa7140f9d61c99d765fbea0ae48f385373d6aec0fe88d36449ef6
MD5 ee3109a5475c6adad6f7775aff7cd0c2
BLAKE2b-256 15de92a80edc39511f33966a7b10cb0f1a2c5584fb4a29910430a70bee0cd6ea

See more details on using hashes here.

Provenance

The following attestation bundles were made for callsight-0.1.1-py3-none-any.whl:

Publisher: release.yml on harshithsunku/callsight

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.5.0

2 files

0.4.0

2 files

0.3.1

2 files

0.3.0

2 files

0.2.2

2 files

0.2.1

2 files

0.2.0

2 files

This release

0.1.1 This release

2 files

0.1.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page