cairo_annotations/annotations/
profiler.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
use crate::annotations::impl_helpers::impl_namespace;
use cairo_lang_sierra::program::{Program, StatementIdx};
use derive_more::Display;
use regex::Regex;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::HashMap;
use std::sync::LazyLock;

/// Versioned representation of Profiler Annotations.
///
/// Always prefer using this enum when Serializing/Deserializing instead of inner ones.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum VersionedProfilerAnnotations {
    V1(ProfilerAnnotationsV1),
}

/// The mapping from sierra statement index
/// to stack a fully qualified Cairo paths of the Cairo functions
/// which caused the statement to be generated.
/// And all functions that were inlined
/// or generated along the way up to the first non-inlined function from the original code.
///
/// The vector represents the stack from the least meaningful elements.
///
/// Introduced in Scarb 2.7.0.
///
/// Needs `unstable-add-statements-functions-debug-info = true`
/// under `[profile.dev.cairo]` in the Scarb config to be generated.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct ProfilerAnnotationsV1 {
    pub statements_functions: HashMap<StatementIdx, Vec<FunctionName>>,
}

static RE_LOOP_FUNC: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"\[expr\d*\]").expect("Failed to create regex for normalizing loop function names")
});
static RE_MONOMORPHIZATION: LazyLock<Regex> = LazyLock::new(|| {
    Regex::new(r"<.*>")
        .expect("Failed to create regex for normalizing monomorphized generic function names")
});

/// The fully qualified Cairo path of the Cairo function.
#[derive(
    Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize, Display, Default,
)]
pub struct FunctionName(pub String);

impl FunctionName {
    /// Get `FunctionName` from given `sierra_statement_idx` and `sierra_program`
    /// Depending on `split_generics`, the resulting `FunctionName` will retain or remove
    /// the parameterization of generic types (eg <felt252>)
    #[must_use]
    pub fn from_sierra_statement_idx(
        statement_idx: StatementIdx,
        sierra_program: &Program,
        split_generics: bool,
    ) -> Self {
        // The `-1` here can't cause an underflow as the statement id of first function's entrypoint is
        // always 0, so it is always on the left side of the partition, thus the partition index is > 0.
        let function_idx = sierra_program
            .funcs
            .partition_point(|f| f.entry_point.0 <= statement_idx.0)
            - 1;
        let function_name = sierra_program.funcs[function_idx].id.to_string();
        // Remove suffix in case of loop function e.g. `[expr36]`.
        let function_name = RE_LOOP_FUNC.replace(&function_name, "");
        // Remove parameters from monomorphised Cairo generics e.g. `<felt252>`.
        let function_name = if split_generics {
            function_name
        } else {
            RE_MONOMORPHIZATION.replace(&function_name, "")
        };

        Self(function_name.to_string())
    }
}

// We can't use untagged enum here. See https://github.com/serde-rs/json/issues/1103
impl Serialize for VersionedProfilerAnnotations {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        match self {
            VersionedProfilerAnnotations::V1(v1) => v1.serialize(serializer),
        }
    }
}

impl<'de> Deserialize<'de> for VersionedProfilerAnnotations {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        ProfilerAnnotationsV1::deserialize(deserializer).map(VersionedProfilerAnnotations::V1)
    }
}

impl_namespace!(
    "github.com/software-mansion/cairo-profiler",
    ProfilerAnnotationsV1,
    VersionedProfilerAnnotations
);