sol-dev-proc-macros 0.1.3

Solana development proc macros
Documentation

Solana Development Utilities

A collection of utilities and macros to enhance Solana program development, focusing on compute unit logging and instruction discriminant generation.

Features

  • Generate unique instruction discriminants for Anchor programs
  • Log compute units for functions and code blocks
  • Utility functions for Solana-specific operations

Installation

Add the following to your Cargo.toml:

[dependencies]
sol_dev_utils = "0.1.3"
sol_dev_macros = "0.1.3"
sol_dev_proc_macros = "0.1.3"

For parsing the logs, the cli can be installed using:

cargo install sol-dev-cli

Usage

Compute Unit Logging

Produces log entries that look like this:

[
    "Program log: my_function {{",
    "Program consumption: 196528 units remaining",
    "Program consumption: 196528 units consumed",
    "Program log: }} my_function"
]

Function-level logging

use sol_dev_proc_macros::compute_fn;

#[compute_fn]
fn my_function() {
    // Function body
}

Code block logging

use sol_dev_macros::compute_fn;

compute_fn!("My Operation" => {
    // Your code here
    some_fn();
});

Log Parsing

The CLI can be used to parse the logs generated by the compute unit logging macros. The logs can be parsed from a file or a directory containing multiple log files.

sol-dev-cli parse dir <path-to-dir>
sol-dev-cli parse file <path-to-file>

This parses logs into JSON that looks like this:

[
    {
        "type": "invoke"
        "id": "11111111111111111111111111111111111111111111"
        depth: 1,
        children: [
            {
                "type": "function",
                "name": "my_function",
                "naive_local": 726,
                "naive_global": 2971,
                "local": 418,
                "global": 1123,
                "children": [<children>]
            } 
        <more children>
    ],
    <more invocations>
]
Measurement Description
naive_local Raw CU consumed within the function, excluding its children, including measurement overhead
naive_global Raw CU consumed within the function, excluding its children, including measurement overhead
local Adjusted CU consumed within the function, excluding its children, excluding measurement overhead
global Adjusted CU consumed within the function, excluding its children, excluding measurement overhead

Notes on CU measurement

Of course measuring CU itself costs CU. Some of this extra cost we can account for, some of it we can't.

What we can account for is the cost of the msg! macro and sol_log_compute_units! macro. Some of this cost goes to the caller of the function, and some of it is internalized by the function itself. We can correct for this cost with some simple arithmetic, which is reflected in the 'local' and 'global' measurements.

What we can't account for is that this macro will cause the compiler to generate different code, which will have a different CU cost. This unaccounted difference is why we provide both naive and adjusted measurements. Example:

use sol_dev_macros::compute_fn;

fn my_inner_function() {
    // Function body
}

#[compute_fn]
fn my_function() {
    // Function body
    my_inner_function();
}

Normally, my_inner_function would likely be inlined into my_function, turning this into a single function call. Using compute_fn will prevent inlining, causing two function calls. We cannot account for this difference easily, so user discretion is advised.

In practice, these differences are significant, so be weary of this. My advice is to only use measurements to quantify the relative cost of different code paths, not the absolute cost.

Instruction Discriminants

Handles the global namespace automatically if no namespace is provided.

use sol_dev_proc_macros::anchor_discriminant;

match discriminant {
    anchor_discriminant![initialize] => initialize(),
    // This is equivalent to:
    // anchor_discriminant![global:initialize] => initialize(),
    anchor_discriminant![process] => process(),
    anchor_discriminant![custom:finalize] => finalize(),
    _ => return Err(ProgramError::InvalidInstructionData.into()),
}

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

This project is licensed under the MIT License - see the LICENSE file for details.