C SDK Reference

This page is a map of the Perfetto C SDK's public surface: the headers you include, the functions and macros they expose, and — at the bottom — the ABI stability contract that the rest of the documentation links to.

For task-oriented guides, start with the C SDK Getting Started tutorial and the Track Events and Custom Data Sources how-tos.

WARNING: The C SDK is not yet stable. See ABI stability below before depending on it.

Header layout

The SDK is split into two layers. You compile against both, but only one is the stability boundary.

  • include/perfetto/public/*.h — a header-only convenience layer of static inline functions and macros. This code is compiled into your binary. It is where the ergonomics live (PERFETTO_TE, PERFETTO_DS_TRACE).
  • include/perfetto/public/abi/*.h — the actual ABI boundary: opaque structs and extern "C" functions exported from libperfetto_c. The convenience layer calls into these. This is the surface stability applies to.

All headers compile as both C and C++.

Convenience headers (include/perfetto/public/)

HeaderWhat it gives you
producer.hGlobal init: PerfettoProducerInit(), PerfettoProducerInitArgs, backend selection, PerfettoProducerActivateTrigger().
track_event.hTrack-event runtime: category/track registration (PerfettoTeNamedTrackRegister, PerfettoTeCounterTrackRegister), process/thread/global track UUIDs, flows, dynamic categories.
te_category_macros.hDeclare and register category lists: PERFETTO_TE_CATEGORIES_DEFINE, PERFETTO_TE_REGISTER_CATEGORIES.
te_macros.hEmit events: the PERFETTO_TE(...) macro and its type/param macros (PERFETTO_TE_SLICE_BEGIN, _INSTANT, _COUNTER, _ARG_*, _FLOW, _REGISTERED_TRACK, …).
data_source.hCustom data sources: PERFETTO_DS_INIT, PerfettoDsRegister, PERFETTO_DS_TRACE, packet begin/end, per-instance TLS and incremental state.
tracing_session.hCreate a consumer session (PerfettoTracingSessionCreate) for in-process recording.
stream_writer.h, pb_msg.h, pb_macros.h, pb_packed.h, pb_utils.hProtozero serialization primitives used to fill in trace packets and configs.
pb_decoder.hIterator-based decoding for reading traces back.
fnv1a.h, thread_utils.h, compiler.hHashing, thread ids, portability macros (PERFETTO_NULL, PERFETTO_STATIC_CAST).

ABI headers (include/perfetto/public/abi/)

These declare the stable boundary. You rarely call them directly — the convenience headers wrap them — but this is what libperfetto_c exports.

HeaderBoundary it defines
export.hPERFETTO_SDK_EXPORT visibility control.
backend_type.hPERFETTO_BACKEND_IN_PROCESS / PERFETTO_BACKEND_SYSTEM.
producer_abi.hProducer/backend init entry points.
tracing_session_abi.hFull session lifecycle (create/setup/start/stop/flush/read/destroy).
data_source_abi.hOpaque data-source/tracer handles and lifecycle callbacks.
track_event_abi.hPerfettoTeInit(), category and track registration.
track_event_hl_abi.hHigh-Level track-event ABI — minimal call-site code size; what PERFETTO_TE uses.
track_event_ll_abi.hLow-Level track-event ABI — iterate active instances and serialize protos by hand.
stream_writer_abi.h, heap_buffer.h, pb_decoder_abi.h, atomic.h, thread_utils_abi.hSerialization, heap buffers, decoding, atomics, thread-id primitives.

Lifecycle at a glance

Producer side (the process being traced):

struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
args.backends = PERFETTO_BACKEND_IN_PROCESS;  // or PERFETTO_BACKEND_SYSTEM
PerfettoProducerInit(args);
PerfettoTeInit();                              // if using track events
PERFETTO_TE_REGISTER_CATEGORIES(MY_CATEGORIES);

Consumer side (controlling an in-process trace):

struct PerfettoTracingSessionImpl* session =
    PerfettoTracingSessionCreate(PERFETTO_BACKEND_IN_PROCESS);
PerfettoTracingSessionSetup(session, cfg_bytes, cfg_size);  // serialized TraceConfig
PerfettoTracingSessionStartBlocking(session);
// ... run the workload ...
PerfettoTracingSessionStopBlocking(session);
PerfettoTracingSessionReadTraceBlocking(session, read_cb, user_arg);
PerfettoTracingSessionDestroy(session);

See the tutorial for the full, compilable version.

High-Level vs Low-Level track events

Two ABIs back track events, trading code size against flexibility:

  • High-Level (track_event_hl_abi.h) — used by PERFETTO_TE. The call site describes the event with a small argument list and the SDK does the serialization. Minimal generated code per event. Use this by default.
  • Low-Level (track_event_ll_abi.h) — you iterate the active tracing instances yourself and serialize arbitrary protobuf per instance. More code per event, maximum control. Reach for it only when the High-Level API can't express what you need.

ABI stability & versioning

The C SDK is not yet stable, and will not be for some time. Its API and ABI are subject to change. Do not depend on it where you cannot tolerate breaking changes.

  • There is no date-based timeline. Realistically stabilization is at least a year out, and that is a lower bound only.
  • Stabilization is gated on ongoing work to make the entirety of the Android OS build on this SDK, and on shaking out the technical and architectural issues that surface along the way. It will be declared stable once that is proven in practice — not before.
  • Until then, treat the surface as evolving: symbols, struct layouts, and behavior may change between releases.

This is the same message carried by examples/shared_lib/README.md and the API and ABI design doc.

Why we still call it the foundation

The instability is about the surface not being frozen yet, not about the design being unfinished. The mechanism that will make it stable already exists and is worth understanding, because it is why the Rust and Java bindings can already build on it:

  • Everything crossing the ABI is an opaque pointer or a plain-C function. Internal representations (PerfettoTracingSessionImpl, PerfettoDsImpl, …) are never exposed by value, so their layout can change without breaking callers.
  • Structs that are not ABI-stable never cross the boundary. For example PerfettoProducerInitArgs is documented as “not ABI-stable, fields can be added and rearranged.” It is consumed only by the static inline PerfettoProducerInit() wrapper in your binary, which translates it into stable setter calls. New fields can be added without breaking the ABI.
  • Forward-compatible initializers (PERFETTO_PRODUCER_INIT_ARGS_INIT(), PERFETTO_DS_INIT(), PerfettoDsParamsDefault()) let added fields default safely.
  • Export control (PERFETTO_SDK_EXPORT) keeps only the intended symbols visible from libperfetto_c.

There is no numeric SDK version macro today; stability is structural (opaque pointers + export control) rather than versioned. When the surface is frozen, this section will document the compatibility guarantees.

Source of truth

The headers themselves are the authoritative reference. Start from:

  • include/perfetto/public/ and include/perfetto/public/abi/
  • Worked examples: examples/shared_lib/example_shlib_track_event.c, examples/shared_lib/example_shlib_data_source.c
  • End-to-end in-process usage: src/shared_lib/test/api_integrationtest.cc and src/shared_lib/test/utils.cc