linux_perf_data/jitdump/
records.rs

1use byteorder::{BigEndian, ByteOrder, LittleEndian};
2use linux_perf_event_reader::{Endianness, RawData};
3
4use super::record::JitDumpRecordHeader;
5
6/// A parsed `JIT_CODE_LOAD` record, for a single jitted function.
7///
8/// This carries the function name and the code bytes.
9#[derive(Debug, Clone)]
10pub struct JitCodeLoadRecord<'a> {
11    /// The process ID of the runtime generating the jitted code.
12    pub pid: u32,
13    /// The thread ID of the runtime thread generating the jitted code.
14    pub tid: u32,
15    /// The virtual address where `code_bytes` starts in the memory of the process.
16    pub vma: u64,
17    /// The code start address for the jitted code. It is unclear in what cases this would differ from `vma`.
18    pub code_addr: u64,
19    /// A unique identifier for this piece of jitted code, to allow future `JitCodeMoveRecord`s to refer back to this record.
20    pub code_index: u64,
21    /// The function name, in ASCII.
22    pub function_name: RawData<'a>,
23    /// The jitted code, as raw bytes. These bytes can be decoded into assembly
24    /// instructions of the CPU architecture given in the file header.
25    pub code_bytes: RawData<'a>,
26}
27
28impl<'a> JitCodeLoadRecord<'a> {
29    /// The offset, in bytes, between the start of the record header and
30    /// the start of the function name.
31    pub const NAME_OFFSET_FROM_RECORD_START: usize =
32        JitDumpRecordHeader::SIZE + 4 + 4 + 8 + 8 + 8 + 8;
33
34    pub fn parse(endian: Endianness, data: RawData<'a>) -> Result<Self, std::io::Error> {
35        match endian {
36            Endianness::LittleEndian => Self::parse_impl::<LittleEndian>(data),
37            Endianness::BigEndian => Self::parse_impl::<BigEndian>(data),
38        }
39    }
40
41    pub fn parse_impl<O: ByteOrder>(data: RawData<'a>) -> Result<Self, std::io::Error> {
42        let mut cur = data;
43        let pid = cur.read_u32::<O>()?;
44        let tid = cur.read_u32::<O>()?;
45        let vma = cur.read_u64::<O>()?;
46        let code_addr = cur.read_u64::<O>()?;
47        let code_size = cur.read_u64::<O>()?;
48        let code_index = cur.read_u64::<O>()?;
49        let function_name = cur.read_string().ok_or(std::io::ErrorKind::UnexpectedEof)?;
50        let code_bytes = cur.split_off_prefix(code_size as usize)?;
51        Ok(Self {
52            pid,
53            tid,
54            vma,
55            code_addr,
56            code_index,
57            function_name,
58            code_bytes,
59        })
60    }
61
62    /// The offset, in bytes, between the start of the record header and
63    /// the start of the code bytes.
64    ///
65    /// This can be different for each record because the code bytes are after
66    /// the function name, so this offset depends on the length of the function
67    /// name.
68    pub fn code_bytes_offset_from_record_header_start(&self) -> usize {
69        JitDumpRecordHeader::SIZE + 4 + 4 + 8 + 8 + 8 + 8 + self.function_name.len() + 1
70    }
71}
72
73/// A parsed `JIT_CODE_MOVE` record.
74#[derive(Debug, Clone)]
75pub struct JitCodeMoveRecord {
76    /// The process ID of the runtime generating the jitted code.
77    pub pid: u32,
78    /// The thread ID of the runtime thread generating the jitted code.
79    pub tid: u32,
80    /// The new address where the jitted code starts in the virtual memory of the process.
81    pub vma: u64,
82    /// The old address of this function's code bytes.
83    pub old_code_addr: u64,
84    /// The new address of this function's code bytes. It is unclear in what cases this might be different from `vma`.
85    pub new_code_addr: u64,
86    /// The size in bytes of the jitted code.
87    pub code_size: u64,
88    /// The index referring to the `JIT_CODE_LOAD` record for this function with the same `code_index`.
89    pub code_index: u64,
90}
91
92impl JitCodeMoveRecord {
93    pub fn parse(endian: Endianness, data: RawData) -> Result<Self, std::io::Error> {
94        match endian {
95            Endianness::LittleEndian => Self::parse_impl::<LittleEndian>(data),
96            Endianness::BigEndian => Self::parse_impl::<BigEndian>(data),
97        }
98    }
99
100    pub fn parse_impl<O: ByteOrder>(data: RawData) -> Result<Self, std::io::Error> {
101        let mut cur = data;
102        let pid = cur.read_u32::<O>()?;
103        let tid = cur.read_u32::<O>()?;
104        let vma = cur.read_u64::<O>()?;
105        let old_code_addr = cur.read_u64::<O>()?;
106        let new_code_addr = cur.read_u64::<O>()?;
107        let code_size = cur.read_u64::<O>()?;
108        let code_index = cur.read_u64::<O>()?;
109        Ok(Self {
110            pid,
111            tid,
112            vma,
113            old_code_addr,
114            new_code_addr,
115            code_size,
116            code_index,
117        })
118    }
119}
120
121/// A parsed `JIT_CODE_DEBUG_INFO` record, mapping addresses to source lines.
122#[derive(Debug, Clone)]
123pub struct JitCodeDebugInfoRecord<'a> {
124    /// The address of the code bytes of the function for which the debug information is generated.
125    pub code_addr: u64,
126    /// The list of line entries, sorted by address.
127    pub entries: Vec<JitCodeDebugInfoEntry<'a>>,
128}
129
130/// An entry for a single code location (file, line, column). Used inside a [`JitCodeDebugInfoRecord`].
131///
132/// Each entry describes a contiguous range of code bytes: this entry's address to the next
133/// entry's address, or to the end of the function if this is the last entry.
134/// address
135#[derive(Debug, Clone)]
136pub struct JitCodeDebugInfoEntry<'a> {
137    /// The start address of the range of code bytes which this entry describes.
138    ///
139    /// The range goes to the next entry, or to the end of the function if this is the last entry.
140    pub code_addr: u64,
141    /// The line number in the source file (1-based) for this entry.
142    pub line: u32,
143    /// The column number. Zero means "no column information", 1 means "beginning of the line".
144    pub column: u32,
145    /// The path of the source code file, in ASCII.
146    pub file_path: RawData<'a>,
147}
148
149impl<'a> JitCodeDebugInfoRecord<'a> {
150    pub fn parse(endian: Endianness, data: RawData<'a>) -> Result<Self, std::io::Error> {
151        match endian {
152            Endianness::LittleEndian => Self::parse_impl::<LittleEndian>(data),
153            Endianness::BigEndian => Self::parse_impl::<BigEndian>(data),
154        }
155    }
156
157    pub fn parse_impl<O: ByteOrder>(data: RawData<'a>) -> Result<Self, std::io::Error> {
158        let mut cur = data;
159        let code_addr = cur.read_u64::<O>()?;
160        let nr_entry = cur.read_u64::<O>()?;
161        let mut entries = Vec::with_capacity(nr_entry as usize);
162        for _ in 0..nr_entry {
163            let code_addr = cur.read_u64::<O>()?;
164            let line = cur.read_u32::<O>()?;
165            let column = cur.read_u32::<O>()?;
166            let file_path = cur.read_string().ok_or(std::io::ErrorKind::UnexpectedEof)?;
167            entries.push(JitCodeDebugInfoEntry {
168                code_addr,
169                line,
170                column,
171                file_path,
172            });
173        }
174
175        Ok(Self { code_addr, entries })
176    }
177
178    pub fn lookup(&self, addr: u64) -> Option<&JitCodeDebugInfoEntry> {
179        let index = match self
180            .entries
181            .binary_search_by_key(&addr, |entry| entry.code_addr)
182        {
183            Ok(i) => i,
184            Err(0) => return None,
185            Err(i) => i - 1,
186        };
187        Some(&self.entries[index])
188    }
189}
190
191/// A parsed `JIT_CODE_UNWINDING_INFO` record, with `eh_frame` data for a single jitted function.
192#[derive(Debug, Clone)]
193pub struct JitCodeUnwindingInfoRecord<'a> {
194    /// The size of the unwinding data mapped in memory. This is either zero or equal to `eh_frame_header.len() + eh_frame.len()`.
195    pub mapped_size: u64,
196    /// The eh_frame_hdr data. This provides an index for the eh_frame data.
197    pub eh_frame_hdr: RawData<'a>,
198    /// The eh_frame data.
199    pub eh_frame: RawData<'a>,
200}
201
202impl<'a> JitCodeUnwindingInfoRecord<'a> {
203    pub fn parse(endian: Endianness, data: RawData<'a>) -> Result<Self, std::io::Error> {
204        match endian {
205            Endianness::LittleEndian => Self::parse_impl::<LittleEndian>(data),
206            Endianness::BigEndian => Self::parse_impl::<BigEndian>(data),
207        }
208    }
209
210    pub fn parse_impl<O: ByteOrder>(data: RawData<'a>) -> Result<Self, std::io::Error> {
211        let mut cur = data;
212        let unwind_data_size = cur.read_u64::<O>()?;
213        let eh_frame_hdr_size = cur.read_u64::<O>()? as usize;
214        let mapped_size = cur.read_u64::<O>()?;
215        let mut unwind_data = cur.split_off_prefix(unwind_data_size as usize)?;
216        let eh_frame_hdr = unwind_data.split_off_prefix(eh_frame_hdr_size)?;
217        let eh_frame = unwind_data;
218        Ok(Self {
219            mapped_size,
220            eh_frame_hdr,
221            eh_frame,
222        })
223    }
224}