1use std::fmt;
2
3use byteorder::{ByteOrder, NativeEndian};
4
5use linux_perf_event_reader::{is_swapped_endian, RawData};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct ThreadMap<'a> {
18 swap_endian: bool,
19 data: RawData<'a>,
20}
21
22const THREAD_ENTRY_SIZE: usize = 8 + 16;
23
24impl<'a> ThreadMap<'a> {
25 pub fn parse<T: ByteOrder>(mut data: RawData<'a>) -> Result<Self, std::io::Error> {
26 let len = data.read_u64::<T>()?;
27 let len = usize::try_from(len).map_err(|_| std::io::ErrorKind::InvalidData)?;
28 let datalen = len
29 .checked_mul(THREAD_ENTRY_SIZE)
30 .ok_or(std::io::ErrorKind::InvalidData)?;
31 let data = data.split_off_prefix(datalen)?;
32 Ok(Self {
33 swap_endian: is_swapped_endian::<T>(),
34 data,
35 })
36 }
37
38 pub fn len(&self) -> usize {
39 self.data.len() / THREAD_ENTRY_SIZE
40 }
41
42 pub fn is_empty(&self) -> bool {
43 self.len() == 0
44 }
45
46 pub fn iter(&self) -> ThreadMapIter<'a> {
47 ThreadMapIter {
48 swap_endian: self.swap_endian,
49 index: 0,
50 len: self.len(),
51 data: self.data,
52 }
53 }
54}
55
56#[derive(Clone, Copy, PartialEq, Eq)]
57pub struct ThreadMapEntry<'a> {
58 pub tid: u64,
60 pub name: RawData<'a>,
63}
64
65impl fmt::Debug for ThreadMapEntry<'_> {
66 fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
67 use std::str;
68
69 let mut map = fmt.debug_map();
70 map.entry(&"tid", &self.tid);
71
72 if let Ok(string) = str::from_utf8(&self.name.as_slice()) {
73 map.entry(&"name", &string);
74 } else {
75 map.entry(&"name", &self.name);
76 }
77
78 map.finish()
79 }
80}
81
82pub struct ThreadMapIter<'a> {
83 swap_endian: bool,
84 data: RawData<'a>,
85 index: usize,
86 len: usize,
87}
88
89impl<'a> Iterator for ThreadMapIter<'a> {
90 type Item = ThreadMapEntry<'a>;
91
92 fn next(&mut self) -> Option<Self::Item> {
93 if self.index >= self.len {
94 return None;
95 }
96
97 let mut tid = self.data.read_u64::<NativeEndian>().unwrap();
98 if self.swap_endian {
99 tid = tid.swap_bytes();
100 }
101 let mut name = self.data.split_off_prefix(16).unwrap();
102 let name = name.read_string().unwrap_or(name);
103 self.index += 1;
104 Some(ThreadMapEntry { tid, name })
105 }
106
107 fn size_hint(&self) -> (usize, Option<usize>) {
108 (0, Some(self.len))
109 }
110}
111
112#[cfg(test)]
113mod test {
114 use byteorder::LittleEndian;
115 use linux_perf_event_reader::RawData;
116
117 use super::ThreadMap;
118
119 #[test]
120 fn parse_one() {
121 let data = RawData::Single(&[
122 1, 0, 0, 0, 0, 0, 0, 0, 108, 71, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
123 0, 0, 0, 0,
124 ]);
125 let map = ThreadMap::parse::<LittleEndian>(data).unwrap();
126 assert_eq!(map.len(), 1);
127 let vec: Vec<_> = map.iter().collect();
128 assert_eq!(vec.len(), 1);
129 assert_eq!(vec[0].tid, 542572);
130 assert_eq!(&vec[0].name.as_slice()[..], b"");
131 }
132
133 #[test]
134 fn parse_big() {
135 let data = RawData::Single(&[
136 12, 0, 0, 0, 0, 0, 0, 0, 165, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137 0, 0, 0, 0, 0, 169, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
138 0, 171, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172,
139 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 187, 115, 1, 0,
140 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 188, 115, 1, 0, 0, 0, 0, 0,
141 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 189, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,
142 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 190, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
143 0, 0, 0, 0, 0, 0, 0, 0, 191, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
144 0, 0, 0, 0, 194, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
145 197, 115, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 199, 115,
146 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
147 ]);
148 let map = ThreadMap::parse::<LittleEndian>(data).unwrap();
149 assert_eq!(map.len(), 12);
150 let vec: Vec<_> = map.iter().collect();
151 assert_eq!(vec.len(), 12);
152 assert_eq!(vec[8].tid, 95167);
153 assert_eq!(&vec[8].name.as_slice()[..], b"");
154 }
155}