]> git.saurik.com Git - apple/system_cmds.git/blob - KDBG/TraceFile.hpp
174493d051374f6b0bd57f9524b6cb334f901d91
[apple/system_cmds.git] / KDBG / TraceFile.hpp
1 //
2 // TraceFile.hpp
3 // system_cmds
4 //
5 // Created by James McIlree on 4/1/14.
6 //
7 //
8
9 #ifndef __system_cmds__TraceFile__
10 #define __system_cmds__TraceFile__
11
12 // These are not (yet) defined in debug.h
13 // Remove and use kdebug.h ASAP.
14 #define RAW_VERSION2 0x55aa0200 // RAW_VERSION2 is from Instruments/kperf
15 #define RAW_VERSION3 0x55aa0300 // RAW_VERSION3 is the new hotness from kperf
16
17 enum class kTraceFileVersion : uint32_t {
18 V0 = 0,
19 V1 = 1,
20 V1Plus = 2, // A 1+ is a 1 with a cpumap
21 V2 = 3, // Can type 2 contain a cpumap? Looks like no.
22 V3 = 4,
23 Unknown = UINT32_MAX
24 };
25
26 class TraceFile {
27 protected:
28 MappedFile _file;
29 kTraceFileVersion _version;
30 bool _is_64_bit;
31 bool _is_valid;
32 void* _threadmap;
33 uint32_t _threadmap_count;
34 KDCPUMapEntry* _cpumap;
35 uint32_t _cpumap_count;
36 void* _events;
37 uintptr_t _event_count;
38 std::vector<uint8_t> _time_sorted_events; // This is empty unless event sorting is requested.
39 std::vector<KDCPUMapEntry> _default_cpumap; // If the file does not contain a cpumap, this will be used instead
40
41 template <typename SIZE>
42 void sanity_check_event_data();
43
44 template <typename SIZE>
45 void parse(bool, uint32_t, uint32_t);
46
47 public:
48 TraceFile(const char* path, bool sort_events = false, uint32_t default_ap_count = 24, uint32_t default_iop_count = 0);
49
50 // Returns true if a Machine state can be created.
51 bool is_valid() const { return _is_valid; }
52 bool is_64_bit() const { return _is_64_bit; }
53 kTraceFileVersion version() const { return _version; }
54
55 // Exposed so iOS devices can report over sized trace
56 bool mmap_failed() const { return _file.mmap_failed(); }
57
58 const KDCPUMapEntry* cpumap() const { return _cpumap; }
59 uint32_t cpumap_count() const { return _cpumap_count; }
60
61 template <typename SIZE>
62 const KDThreadMapEntry<SIZE>* threadmap() const { return reinterpret_cast<KDThreadMapEntry<SIZE>*>(_threadmap); }
63 uint32_t threadmap_count() const { return _threadmap_count; }
64
65 template <typename SIZE>
66 const KDEvent<SIZE>* events() const { return reinterpret_cast<KDEvent<SIZE>*>(_events); }
67 uintptr_t event_count() const { return _event_count; }
68 };
69
70 //
71 // This is a very simple attempt to sanity check the event data and prevent
72 // crashes when reading 32b vs 64b trace data.
73 //
74 template <typename SIZE>
75 void TraceFile::sanity_check_event_data() {
76 uintptr_t event_check_count = std::min((uintptr_t)10, _event_count);
77
78 AbsTime last_timestamp;
79
80 for (uintptr_t i=0; i<event_check_count; i++) {
81 KDEvent<SIZE>& event = reinterpret_cast<KDEvent<SIZE>*>(_events)[i];
82
83 if (event.cpu() < 0) {
84 THROW("Event cpu id is less than 0");
85 }
86
87 if (event.cpu() >= _cpumap_count) {
88 THROW("Event cpu id is greater than the number of configured cpus");
89 }
90
91 if (event.timestamp() < last_timestamp) {
92 THROW("Event Data sanity check found out of order timestamps");
93 }
94
95 if (SIZE::is_64_bit) {
96 if (event.unused() != 0) {
97 THROW("Event has value set in unknown field");
98 }
99 }
100
101 last_timestamp = event.timestamp();
102 }
103 }
104
105 template <typename SIZE>
106 void TraceFile::parse(bool should_presort_events, uint32_t default_ap_count, uint32_t default_iop_count) {
107 if (TraceDataHeader<SIZE>* header = reinterpret_cast<TraceDataHeader<SIZE>*>(_file.address())) {
108 KDThreadMapEntry<SIZE>* threadmap = NULL;
109 uint32_t threadmap_count = 0;
110 KDCPUMapEntry* cpumap = NULL;
111 uint32_t cpumap_count = 0;
112 KDEvent<SIZE>* events = NULL;
113 kTraceFileVersion version;
114
115 switch (header->version()) {
116 case RAW_VERSION0:
117 // Should never happen!
118 ASSERT(false, "File is RAW_VERSION0");
119 THROW("RAW_VERSION0 is ILLEGAL");
120 break;
121
122 case RAW_VERSION1:
123 // Could be either v1 or v1+
124 break;
125
126 case RAW_VERSION2:
127 _version = kTraceFileVersion::V2;
128 // We do not know how to parse a V2 file
129 THROW("RAW_VERSION2 is unhandled");
130 break;
131
132 case RAW_VERSION3:
133 _version = kTraceFileVersion::V3;
134 // We do not know how to parse a V3 file
135 THROW("RAW_VERSION3 is unhandled");
136 break;
137
138 default:
139 // Could be a v0
140 break;
141 }
142
143 if (header->version() != RAW_VERSION1) {
144 // If the header is not a RAW_VERSION1, we must assume it is a
145 // RAW_VERSION0. The difficulty here is that RAW_VERSION0 consists
146 // of 4 bytes, which are the thread_count. We can't do much
147 // sanity checking. The first four bytes are already read into
148 // the existing header, reuse them. We must also reset the file
149 // offset.
150
151 threadmap_count = header->version();
152 threadmap = reinterpret_cast<KDThreadMapEntry<SIZE>*>(_file.address() + 4);
153
154 // Event data starts immediately following the threadmap
155 size_t offset = 4 + threadmap_count * sizeof(KDThreadMapEntry<SIZE>);
156 events = reinterpret_cast<KDEvent<SIZE>*>(_file.address() + offset);
157
158 version = kTraceFileVersion::V0;
159 } else {
160 //
161 // RAW_VERSION1
162 //
163 threadmap_count = header->thread_count();
164 threadmap = reinterpret_cast<KDThreadMapEntry<SIZE>*>(_file.address() + sizeof(TraceDataHeader<SIZE>));
165
166 size_t threadmap_size_in_bytes = threadmap_count * sizeof(KDThreadMapEntry<SIZE>);
167 size_t offset_to_event_data = (sizeof(TraceDataHeader<SIZE>) + threadmap_size_in_bytes + 4095) & ~4095;
168 size_t offset_to_cpumap_data = sizeof(TraceDataHeader<SIZE>) + threadmap_size_in_bytes;
169 size_t cpumap_bytes = offset_to_event_data - offset_to_cpumap_data;
170
171 //
172 // In a RAW_VERSION1, there *may* be a cpumap.
173 // If it exists, it will be between the header and the page aligned offset
174 // that event data begins at.
175 //
176 if (cpumap_bytes > sizeof(kd_cpumap_header) + sizeof(kd_cpumap)) {
177 kd_cpumap_header* cpumap_header = reinterpret_cast<kd_cpumap_header*>(_file.address() + offset_to_cpumap_data);
178 if (cpumap_header->version_no == RAW_VERSION1) {
179 cpumap = (KDCPUMapEntry*)&cpumap_header[1];
180 cpumap_count = cpumap_header->cpu_count;
181 }
182 }
183
184 // Event data starts at the next PAGE alignment boundary.
185 //
186 // Hmm, this could be pretty awful in iOS...
187 //
188 // Kernel page size is 4k. Userspace page size is 16kb in 64b.
189 // Kernel writes the data. Unless the kernel call fails, then userspace writes the data. Blech.
190 events = reinterpret_cast<KDEvent<SIZE>*>(_file.address() + offset_to_event_data);
191 }
192
193 uintptr_t event_count = (uintptr_t)_file.size() - (reinterpret_cast<uintptr_t>(events) - reinterpret_cast<uintptr_t>(_file.address()));
194 if (event_count % sizeof(KDEvent<SIZE>) != 0) {
195 // We're probably looking at the wrong k32/k64. Throw and try the other size.
196 THROW("Bytes in file does not match an even multiple of Event struct");
197 }
198 event_count /= sizeof(KDEvent<SIZE>);
199
200 if (cpumap == NULL || cpumap_count == 0) {
201 // No cpumap found, we need to fake one up using the default values.
202 //
203 // It would be nice if we could just read the events and derive the
204 // AP/IOP count, but the IOP events do not have valid tid(s), and
205 // must be ignored.
206
207 for (uint32_t i=0; i<default_ap_count; ++i) {
208 _default_cpumap.emplace_back(i, 0, "AP-???");
209 }
210 uint32_t iop_limit = default_ap_count + default_iop_count;
211 for (uint32_t i=default_ap_count; i<iop_limit; ++i) {
212 _default_cpumap.emplace_back(i, KDBG_CPUMAP_IS_IOP, "IOP-???");
213 }
214
215 cpumap = _default_cpumap.data();
216 cpumap_count = (uint32_t)_default_cpumap.size();
217
218 version = kTraceFileVersion::V1;
219 } else {
220 version = kTraceFileVersion::V1Plus;
221 }
222
223
224 // IOP's have been producing .trace files with out of order events.
225 // This is a hack fix to work around that. It costs a full copy of the data!
226 MemoryBuffer<KDEvent<SIZE>> presorted_events;
227 if (should_presort_events && event_count) {
228 _time_sorted_events.reserve(event_count * sizeof(KDEvent<SIZE>));
229 memcpy(_time_sorted_events.data(), events, event_count * sizeof(KDEvent<SIZE>));
230 events = reinterpret_cast<KDEvent<SIZE>*>(_time_sorted_events.data());
231 std::sort(events, events + event_count, [](KDEvent<SIZE> const& p0, KDEvent<SIZE> const& p1) -> bool {
232 return p0.timestamp() < p1.timestamp();
233 });
234 }
235
236 _threadmap = threadmap;
237 _threadmap_count = threadmap_count;
238
239 _cpumap = cpumap;
240 _cpumap_count = cpumap_count;
241
242 _events = events;
243 _event_count = event_count;
244
245 _version = version;
246 _is_64_bit = SIZE::is_64_bit;
247
248 sanity_check_event_data<SIZE>();
249
250 //
251 // Okay, success if we made it this far.
252 //
253 _is_valid = true;
254 }
255 }
256
257 #endif /* defined(__system_cmds__TraceFile__) */