layout_audit/
types.rs

1use serde::Serialize;
2
3#[derive(Debug, Clone, Serialize)]
4pub struct StructLayout {
5    pub name: String,
6    pub size: u64,
7    pub alignment: Option<u64>,
8    pub members: Vec<MemberLayout>,
9    pub metrics: LayoutMetrics,
10    #[serde(skip_serializing_if = "Option::is_none")]
11    pub source_location: Option<SourceLocation>,
12}
13
14#[derive(Debug, Clone, Serialize)]
15pub struct MemberLayout {
16    pub name: String,
17    pub type_name: String,
18    pub offset: Option<u64>,
19    pub size: Option<u64>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub bit_offset: Option<u64>,
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub bit_size: Option<u64>,
24    /// True if the type was marked with DW_TAG_atomic_type in DWARF debug info.
25    /// This provides more reliable atomic detection than string pattern matching.
26    #[serde(skip_serializing_if = "std::ops::Not::not")]
27    pub is_atomic: bool,
28}
29
30#[derive(Debug, Clone, Serialize, Default)]
31pub struct LayoutMetrics {
32    pub total_size: u64,
33    pub useful_size: u64,
34    pub padding_bytes: u64,
35    pub padding_percentage: f64,
36    pub cache_lines_spanned: u32,
37    pub cache_line_density: f64,
38    pub padding_holes: Vec<PaddingHole>,
39    #[serde(skip_serializing_if = "std::ops::Not::not")]
40    pub partial: bool,
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub false_sharing: Option<FalseSharingAnalysis>,
43}
44
45#[derive(Debug, Clone, Serialize)]
46pub struct PaddingHole {
47    pub offset: u64,
48    pub size: u64,
49    pub after_member: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize)]
53pub struct SourceLocation {
54    pub file: String,
55    pub line: u64,
56}
57
58#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
59pub struct FalseSharingWarning {
60    pub member_a: String,
61    pub member_b: String,
62    pub cache_line: u64,
63    /// Gap in bytes between member_a's end and member_b's start.
64    /// Negative = overlap, Zero = adjacent, Positive = gap
65    pub gap_bytes: i64,
66}
67
68#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
69pub struct CacheLineSpanningWarning {
70    pub member: String,
71    pub type_name: String,
72    pub offset: u64,
73    pub size: u64,
74    pub start_cache_line: u64,
75    pub end_cache_line: u64,
76    pub lines_spanned: u64,
77}
78
79#[derive(Debug, Clone, Serialize, Default, PartialEq, Eq)]
80pub struct FalseSharingAnalysis {
81    pub atomic_members: Vec<AtomicMember>,
82    pub warnings: Vec<FalseSharingWarning>,
83    #[serde(skip_serializing_if = "Vec::is_empty")]
84    pub spanning_warnings: Vec<CacheLineSpanningWarning>,
85}
86
87#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
88pub struct AtomicMember {
89    pub name: String,
90    pub type_name: String,
91    pub offset: u64,
92    pub size: u64,
93    pub cache_line: u64,
94    pub end_cache_line: u64,
95    pub spans_cache_lines: bool,
96}
97
98impl StructLayout {
99    pub fn new(name: String, size: u64, alignment: Option<u64>) -> Self {
100        Self {
101            name,
102            size,
103            alignment,
104            members: Vec::new(),
105            metrics: LayoutMetrics::default(),
106            source_location: None,
107        }
108    }
109}
110
111impl MemberLayout {
112    pub fn new(name: String, type_name: String, offset: Option<u64>, size: Option<u64>) -> Self {
113        Self { name, type_name, offset, size, bit_offset: None, bit_size: None, is_atomic: false }
114    }
115
116    pub fn with_atomic(mut self, is_atomic: bool) -> Self {
117        self.is_atomic = is_atomic;
118        self
119    }
120
121    pub fn end_offset(&self) -> Option<u64> {
122        match (self.offset, self.size) {
123            (Some(off), Some(sz)) => Some(off + sz),
124            _ => None,
125        }
126    }
127}