cargo-instruments

repository·main·Indexed 21 days ago

https://github.com/cmyr/cargo-instruments

A bridge between Cargo and Xcode Instruments for profiling Rust binaries, examples, and benchmarks on macOS. It automates target compilation, symbol demangling, and code signing for aarch64, allowing developers to use Xcode profiling templates such as Time Profiler, Allocations, and System Trace via the cargo instruments CLI.

Tokens
3.4K
Snippets
11
Records
16
Agent score
74%

What's inside cargo-instruments

  1. Install cargo-instruments

    main

    You can install cargo-instruments using Homebrew or by building from source.

    Via Homebrew

    brew install cargo-instruments

    Via Cargo (Building from Source)

    If you have OpenSSL installed (e.g., via brew):

    cargo install cargo-instruments

    If you encounter OpenSSL errors (e.g., Could not find directory of OpenSSL installation...), use the vendored-openssl feature:

    cargo install --features vendored-openssl cargo-instruments
  2. Profile a Rust binary with cargo-instruments

    main

    cargo-instruments allows you to profile binary targets, examples, or benchmarks using Xcode Instruments templates.

    Basic Usage

    By default, it profiles the crate's main.rs. To profile a specific target, use --bin, --example, or --bench.

    To profile the Allocations template for the main binary:

    cargo instruments -t Allocations

    Workspace and Specific Targets

    To profile a specific binary (bar) within a specific package (foo) in a workspace:

    cargo instruments --package foo --template alloc --bin bar

    Profiling in Release Mode

    To see debugging symbols when profiling in release mode, you must enable them in your Cargo.toml:

    [profile.release]
    debug = true
  3. Configure Xcode Instruments prerequisites

    main

    cargo-instruments requires macOS and a full installation of Xcode (not just Command Line Tools) to use the Instruments profiling suite.

    1. Install Xcode from the App Store.
    2. Verify installation by running xctrace. It should print help output.
    3. Check developer directory: Run xcode-select --print-path. It should return /Applications/Xcode.app/Contents/Developer.
    4. Fix path if necessary: If the path is incorrect, reset it using:
    sudo xcode-select --reset
  4. How Xcode Instruments versions are handled

    main

    The library abstracts the differences between the modern xctrace tool and the legacy instruments binary.

    When using xctrace, the tool constructs a command using xcrun xctrace record with flags like --template, --time-limit, and --output. It also attempts to redirect stdin/stdout to the current TTY to ensure interactive compatibility.

    When using the legacy instruments binary, it uses flags like -t for templates, -D for the destination path, and -l for the time limit.

  5. How cargo-instruments executes profiling

    main

    The cargo-instruments application follows a specific lifecycle to profile a Rust target using Xcode Instruments:

    1. Environment Detection: It detects the installed xctrace tool.
    2. Template Selection: If requested, it lists available Xcode Instruments templates. Otherwise, it proceeds to profile using a selected template.
    3. Target Compilation: It builds the specified target (binary, example, or benchmark) using cargo build. It automatically configures the build with profile.<profile>.split-debuginfo='packed' to ensure debug information is available for profiling.
    4. Symbol Demangling: It attempts to demangle symbols in the built binary to make trace results more readable.
    5. Code Signing (aarch64 only): On Apple Silicon (aarch64), it automatically signs the binary with the com.apple.security.get-task-allow entitlement, which is required for profiling.
    6. Profiling: It runs the target through xctrace to generate a trace file.
    7. Result Handling: It prints the relative path of the generated trace file and, by default, opens it in the Xcode Instruments application.
  6. Build and profile specific targets

    main

    The tool can target specific binaries, examples, or benchmarks. When a specific target is provided, cargo-instruments uses Cargo's compilation logic to build only that unit, minimizing build time.

    Supported target types include:

    • Binaries: Standard executable targets.
    • Examples: Targets located in the examples/ directory.
    • Benchmarks: Benchmark targets (e.g., using criterion).

    If multiple targets are found for a single command, the tool will return an error to prevent ambiguous profiling.

  7. Use cargo-instruments CLI

    main

    The cargo instruments command profiles a Rust binary using Xcode Instruments. It uses templates to define which instrument to run (e.g., Time Profiler, Allocations).

    To see a list of available templates, use the --list-templates flag.

    Note on Target Arguments: To pass arguments directly to your target binary, use the -- separator. Everything following -- is treated as an argument for the binary being profiled, not for cargo-instruments.

    # Profile the main binary with the Time Profiler
    cargo instruments -t time
    
    # Profile a specific example with arguments passed to the example
    cargo instruments -t alloc -- --example-arg
  8. List available Instruments templates

    main

    Instruments uses 'templates' to define sets of dtrace probes. You can list all available templates (including custom ones) using the --list-templates flag. If you don't provide a template name when running a profile, you will be prompted to choose one.

    Common built-in templates include:

    • Time Profiler (time)
    • Allocations (alloc)
    • System Trace (sys)
    • File Activity (io)
    • Leaks
    cargo instruments --list-templates
  9. Reference: cargo-instruments CLI options

    main

    cargo-instruments uses Clap for its CLI. Use cargo instruments -h for a compact summary or cargo instruments --help for full details.

    Arguments

    • [ARGS]...: Arguments passed directly to the target binary. Use -- to separate these from cargo-instruments flags (e.g., cargo instruments -- -t test1.txt).

    Options

    • -l, --list-templates: List available Instruments templates.
    • -t, --template <TEMPLATE>: Specify the instruments template to run.
    • -p, --package <NAME>: Specify package for example/bin/bench.
    • --example <NAME>: Example binary to run.
    • --bin <NAME>: Binary to run.
    • --bench <NAME>: Benchmark target to run.
    • --release: Pass --release to cargo.
    • --profile <NAME>: Pass --profile NAME to cargo.
    • -o, --output <PATH>: Output .trace file to the given path.
    • --time-limit <MILLIS>: Limit recording time in milliseconds.
    • --no-open: Do not open the generated trace file in Instruments.app.
    • --features <CARGO-FEATURES>: Features to pass to cargo.
    • --manifest-path <PATH>: Path to Cargo.toml.
    • --all-features: Activate all features for the selected target.
    • --no-default-features: Do not activate the default features.
    • --no-demangle: Do not demangle Rust symbols in the profiling output.
    cargo instruments [OPTIONS] [ARGS]...
  10. Profile a target binary

    main

    The profile_target function automates the execution of the profiling command for a specific binary. It handles template resolution, trace file path generation, and command execution.

    Workflow:

    1. Template Resolution: Resolves abbreviations (like time or alloc) to full template names (like Time Profiler or Allocations).
    2. Path Preparation: Computes the output path for the .trace file. By default, it creates files in target/instruments/ using the format {target_shortname}_{template_name}_{timestamp}.trace.
    3. Execution: Runs the underlying xctrace or instruments command with the provided AppConfig (including time limits and extra target arguments).

    Parameters:

    • target_filepath: Path to the binary to profile.
    • xctrace_tool: The detected XcodeInstruments instance.
    • app_config: Configuration containing template_name, time_limit, and target_args.
    • workspace: The Cargo workspace context used to resolve the root directory.
    // Example usage (conceptual)
    let trace_path = profile_target(
        &target_path,
        &instruments_version,
        &app_config,
        &workspace
    )?;
  11. Detect Xcode Instruments version

    main

    The XcodeInstruments::detect() method determines which profiling tool is available on the system. It checks the macOS version to decide between the modern xctrace tool or the legacy instruments binary.

    • XcTrace: Used on macOS 10.15 and newer if Xcode or Command Line Tools are installed.
    • InstrumentsBinary: The legacy tool used on older macOS versions.

    If neither is found, it returns an error indicating that Xcode or Xcode Command Line Tools must be installed.

    let instruments_version = XcodeInstruments::detect()?;
    match instruments_version {
        XcodeInstruments::XcTrace => println!("Using xctrace"),
        XcodeInstruments::InstrumentsBinary => println!("Using legacy instruments"),
    }
  12. List and view available Instruments templates

    main

    You can retrieve a list of available profiling templates (both standard and custom) using the available_templates() method on an XcodeInstruments instance. This returns a TemplateCatalog containing two vectors: standard_templates and custom_templates.

    To display these templates in a human-readable format with abbreviations, use render_template_catalog().

    let instruments = XcodeInstruments::detect()?;
    let catalog = instruments.available_templates()?;
    println!("{}", render_template_catalog(&catalog));