]>
Commit | Line | Data |
---|---|---|
1 | // | |
2 | // Tests.swift | |
3 | // | |
4 | // Some of these tests here verify that kdd is able to parse old | |
5 | // kcdata files and generate the correct output. To do so, we include | |
6 | // compressed versions of the raw kcdata and as well as the expected | |
7 | // plist output. | |
8 | // | |
9 | // NOTE: If you're adding sample data/plist files, you'll need to first | |
10 | // add them to the project and then make sure each is part of the | |
11 | // tests target. | |
12 | // | |
13 | // Other tests verify the expected behavior of libkdd for certain | |
14 | // situations. | |
15 | // | |
16 | // | |
17 | ||
18 | import XCTest | |
19 | import Foundation | |
20 | ||
21 | // Swift's bridging to uuid_t is awkward. | |
22 | ||
23 | func nsuuid2uuid_t(_ nsuuid : NSUUID) -> uuid_t { | |
24 | let dat = nsuuid2array(nsuuid) | |
25 | return nsarray2uuid(dat) | |
26 | } | |
27 | ||
28 | func nsarray2uuid(_ a : [Int]) -> uuid_t { | |
29 | return uuid_t(UInt8(a[0]), | |
30 | UInt8(a[1]), | |
31 | UInt8(a[2]), | |
32 | UInt8(a[3]), | |
33 | UInt8(a[4]), | |
34 | UInt8(a[5]), | |
35 | UInt8(a[6]), | |
36 | UInt8(a[7]), | |
37 | UInt8(a[8]), | |
38 | UInt8(a[9]), | |
39 | UInt8(a[10]), | |
40 | UInt8(a[11]), | |
41 | UInt8(a[12]), | |
42 | UInt8(a[13]), | |
43 | UInt8(a[14]), | |
44 | UInt8(a[15])) | |
45 | } | |
46 | ||
47 | func nsuuid2array(_ uuid: NSUUID) -> [Int] { | |
48 | var ret = [Int]() | |
49 | let ptr = UnsafeMutablePointer<UInt8>.allocate(capacity: 16) | |
50 | ||
51 | defer { ptr.deallocate(capacity:16) } | |
52 | ||
53 | uuid.getBytes(ptr) | |
54 | for i in 0..<16 { | |
55 | ret.append(Int(ptr[i])) | |
56 | } | |
57 | return ret | |
58 | } | |
59 | ||
60 | func decompress(_ data:NSData) throws -> NSData { | |
61 | var stream = z_stream(next_in: nil, avail_in: 0, total_in: 0, next_out: nil, avail_out: 0, total_out: 0, msg: nil, state: nil, zalloc: nil, zfree: nil, opaque: nil, data_type: 0, adler: 0, reserved: 0) | |
62 | ||
63 | let bufsize : Int = 1000 | |
64 | let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsize) | |
65 | defer { buffer.deallocate(capacity:bufsize) } | |
66 | let output = NSMutableData() | |
67 | stream.next_out = buffer | |
68 | stream.avail_out = UInt32(bufsize) | |
69 | stream.next_in = UnsafeMutablePointer(mutating:data.bytes.assumingMemoryBound(to:Bytef.self)) | |
70 | stream.avail_in = UInt32(data.length) | |
71 | inflateInit2_(&stream, 16+MAX_WBITS, ZLIB_VERSION, Int32(MemoryLayout<z_stream>.size)) | |
72 | ||
73 | while (true) { | |
74 | let z = inflate(&stream, Z_NO_FLUSH); | |
75 | if (z == Z_OK || z == Z_STREAM_END) { | |
76 | output.append(buffer, length: bufsize - Int(stream.avail_out)) | |
77 | stream.avail_out = UInt32(bufsize) | |
78 | stream.next_out = buffer | |
79 | if (z == Z_STREAM_END) { | |
80 | return output; | |
81 | } | |
82 | } else { | |
83 | throw NSError(domain: "zlib", code: Int(z), userInfo: nil) | |
84 | } | |
85 | } | |
86 | } | |
87 | ||
88 | ||
89 | extension Dictionary { | |
90 | func value(forKeyPath s:String) -> Any? { | |
91 | return (self as NSDictionary).value(forKeyPath:s) | |
92 | } | |
93 | } | |
94 | ||
95 | ||
96 | class Tests: XCTestCase { | |
97 | ||
98 | override func setUp() { | |
99 | super.setUp() | |
100 | continueAfterFailure = false | |
101 | } | |
102 | ||
103 | override func tearDown() { | |
104 | // Put teardown code here. This method is called after the invocation of each test method in the class. | |
105 | super.tearDown() | |
106 | } | |
107 | ||
108 | func parseBuffer(_ buffer:NSData) throws -> [AnyHashable:Any] { | |
109 | var error : NSError? | |
110 | guard let dict = parseKCDataBuffer(UnsafeMutablePointer(mutating:buffer.bytes.assumingMemoryBound(to:UInt8.self)), UInt32(buffer.length), &error) | |
111 | else { | |
112 | XCTAssert(error != nil) | |
113 | throw error! | |
114 | } | |
115 | return dict | |
116 | } | |
117 | ||
118 | func testPaddingFlags(_ pad : Int) { | |
119 | let buffer = NSMutableData(capacity:1000)! | |
120 | ||
121 | var item = kcdata_item() | |
122 | ||
123 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
124 | item.flags = 0 | |
125 | item.size = 0 | |
126 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
127 | ||
128 | item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) | |
129 | item.flags = UInt64(pad) | |
130 | item.size = UInt32(MemoryLayout<dyld_uuid_info_32>.size) | |
131 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
132 | ||
133 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
134 | ||
135 | var payload = dyld_uuid_info_32(imageLoadAddress: 42, imageUUID: nsuuid2uuid_t(uuid)) | |
136 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size) | |
137 | ||
138 | item.type = KCDATA_TYPE_BUFFER_END | |
139 | item.flags = 0 | |
140 | item.size = 0 | |
141 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
142 | ||
143 | guard let dict = try? self.parseBuffer(buffer) | |
144 | else { XCTFail(); return; } | |
145 | ||
146 | var uuidarray = nsuuid2array(uuid) | |
147 | for _ in 0..<pad { | |
148 | uuidarray.removeLast() | |
149 | } | |
150 | ||
151 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageLoadAddress") as? Int == 42) | |
152 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageUUID") as! [Int] == uuidarray) | |
153 | } | |
154 | ||
155 | func testPaddingFlags() { | |
156 | for i in 0..<15 { | |
157 | testPaddingFlags(i) | |
158 | } | |
159 | } | |
160 | func testBootArgs() { | |
161 | let s = "hello, I am some boot args" | |
162 | ||
163 | let buffer = NSMutableData(capacity:1000)! | |
164 | ||
165 | var item = kcdata_item() | |
166 | ||
167 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
168 | item.flags = 0 | |
169 | item.size = 0 | |
170 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
171 | ||
172 | item.type = UInt32(STACKSHOT_KCTYPE_BOOTARGS) | |
173 | item.flags = 0 | |
174 | item.size = UInt32(s.utf8.count + 1) | |
175 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
176 | s.utf8CString.withUnsafeBufferPointer({ | |
177 | buffer.append($0.baseAddress!, length:s.utf8.count + 1) | |
178 | }) | |
179 | item.type = KCDATA_TYPE_BUFFER_END | |
180 | item.flags = 0 | |
181 | item.size = 0 | |
182 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
183 | ||
184 | guard let dict = try? self.parseBuffer(buffer) else { XCTFail(); return; } | |
185 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.boot_args") as? String == s) | |
186 | } | |
187 | ||
188 | func testBootArgsMissingNul() { | |
189 | let s = "hello, I am some boot args" | |
190 | ||
191 | let buffer = NSMutableData(capacity:1000)! | |
192 | ||
193 | var item = kcdata_item() | |
194 | ||
195 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
196 | item.flags = 0 | |
197 | item.size = 0 | |
198 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
199 | ||
200 | item.type = UInt32(STACKSHOT_KCTYPE_BOOTARGS) | |
201 | item.flags = 0 | |
202 | item.size = UInt32(s.utf8.count) | |
203 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
204 | s.utf8CString.withUnsafeBufferPointer({ | |
205 | buffer.append($0.baseAddress!, length:s.utf8.count) | |
206 | }) | |
207 | ||
208 | item.type = KCDATA_TYPE_BUFFER_END | |
209 | item.flags = 0 | |
210 | item.size = 0 | |
211 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
212 | ||
213 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
214 | } | |
215 | ||
216 | func testLoadInfo() { | |
217 | let buffer = NSMutableData(capacity:1000)! | |
218 | ||
219 | var item = kcdata_item() | |
220 | ||
221 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
222 | item.flags = 0 | |
223 | item.size = 0 | |
224 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
225 | ||
226 | item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) | |
227 | item.flags = 0 | |
228 | item.size = UInt32(MemoryLayout<dyld_uuid_info_32>.size) | |
229 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
230 | ||
231 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
232 | ||
233 | var payload = dyld_uuid_info_32(imageLoadAddress: 42, imageUUID: nsuuid2uuid_t(uuid)) | |
234 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size) | |
235 | ||
236 | item.type = KCDATA_TYPE_BUFFER_END | |
237 | item.flags = 0 | |
238 | item.size = 0 | |
239 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
240 | ||
241 | guard let dict = try? self.parseBuffer(buffer) | |
242 | else { XCTFail(); return; } | |
243 | ||
244 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageLoadAddress") as? Int == 42) | |
245 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageUUID") as! [Int] == nsuuid2array(uuid)) | |
246 | } | |
247 | ||
248 | func testLoadInfoWrongSize() { | |
249 | // test what happens when a struct size is short | |
250 | ||
251 | let buffer = NSMutableData(capacity:1000)! | |
252 | ||
253 | var item = kcdata_item() | |
254 | ||
255 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
256 | item.flags = 0 | |
257 | item.size = 0 | |
258 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
259 | ||
260 | item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) | |
261 | item.flags = 0 | |
262 | item.size = UInt32(MemoryLayout<dyld_uuid_info_32>.size) - 1 | |
263 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
264 | ||
265 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
266 | ||
267 | var payload = dyld_uuid_info_32(imageLoadAddress: 42, imageUUID: nsuuid2uuid_t(uuid)) | |
268 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size - 1) | |
269 | ||
270 | item.type = KCDATA_TYPE_BUFFER_END | |
271 | item.flags = 0 | |
272 | item.size = 0 | |
273 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
274 | ||
275 | guard let dict = try? self.parseBuffer(buffer) | |
276 | else { XCTFail(); return; } | |
277 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageLoadAddress") as? Int == 42) | |
278 | var uuidarray = nsuuid2array(uuid) | |
279 | uuidarray.removeLast() | |
280 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageUUID") as! [Int] == uuidarray) | |
281 | } | |
282 | ||
283 | func testLoadInfoWayWrongSize() { | |
284 | // test what happens when a struct size is short | |
285 | ||
286 | let buffer = NSMutableData(capacity:1000)! | |
287 | ||
288 | var item = kcdata_item() | |
289 | ||
290 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
291 | item.flags = 0 | |
292 | item.size = 0 | |
293 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
294 | ||
295 | item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) | |
296 | item.flags = 0 | |
297 | item.size = UInt32(MemoryLayout<dyld_uuid_info_32>.size) - 16 | |
298 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
299 | ||
300 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
301 | ||
302 | var payload = dyld_uuid_info_32(imageLoadAddress: 42, imageUUID: nsuuid2uuid_t(uuid)) | |
303 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size - 16) | |
304 | ||
305 | item.type = KCDATA_TYPE_BUFFER_END | |
306 | item.flags = 0 | |
307 | item.size = 0 | |
308 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
309 | guard let dict = try? self.parseBuffer(buffer) | |
310 | else { XCTFail(); return; } | |
311 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageLoadAddress") as? Int == 42) | |
312 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageUUID") == nil) | |
313 | } | |
314 | ||
315 | func testLoadInfoPreposterousWrongSize() { | |
316 | // test what happens when a struct size is short | |
317 | ||
318 | let buffer = NSMutableData(capacity:1000)! | |
319 | ||
320 | var item = kcdata_item() | |
321 | ||
322 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
323 | item.flags = 0 | |
324 | item.size = 0 | |
325 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
326 | ||
327 | item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) | |
328 | item.flags = 0 | |
329 | item.size = UInt32(1) | |
330 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
331 | ||
332 | var payload = UInt8(42) | |
333 | buffer.append(&payload, length:1) | |
334 | ||
335 | item.type = KCDATA_TYPE_BUFFER_END | |
336 | item.flags = 0 | |
337 | item.size = 0 | |
338 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
339 | ||
340 | guard let dict = try? self.parseBuffer(buffer) | |
341 | else { XCTFail(); return; } | |
342 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageLoadAddress") == nil) | |
343 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info.imageUUID") == nil) | |
344 | } | |
345 | ||
346 | ||
347 | func testNewArray(n : Int, pad : Int) { | |
348 | let buffer = NSMutableData(capacity:1000)! | |
349 | var item = kcdata_item() | |
350 | ||
351 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
352 | item.flags = 0 | |
353 | item.size = 0 | |
354 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
355 | ||
356 | item.type = UInt32(KCDATA_TYPE_ARRAY_PAD0) + UInt32(pad) | |
357 | item.flags = UInt64(STACKSHOT_KCTYPE_DONATING_PIDS) << 32 | UInt64(n) | |
358 | item.size = UInt32(n * MemoryLayout<UInt32>.size + pad) | |
359 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
360 | ||
361 | for i in 0..<n { | |
362 | var payload = UInt32(42 * i) | |
363 | buffer.append(&payload, length:MemoryLayout<UInt32>.size) | |
364 | } | |
365 | ||
366 | for i in 0..<pad { | |
367 | var payload = UInt8(42-i) | |
368 | buffer.append(&payload, length:MemoryLayout<UInt8>.size) | |
369 | } | |
370 | ||
371 | item.type = KCDATA_TYPE_BUFFER_END | |
372 | item.flags = 0 | |
373 | item.size = 0 | |
374 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
375 | ||
376 | guard let dict = try? self.parseBuffer(buffer) | |
377 | else { XCTFail(); return; } | |
378 | XCTAssert((dict.value(forKeyPath:"kcdata_crashinfo.donating_pids") as! [Any]).count == n) | |
379 | for i in 0..<n { | |
380 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
381 | let y = x?["donating_pids"] as? NSArray | |
382 | XCTAssert((y?[i]) as? Int == 42 * i) | |
383 | } | |
384 | } | |
385 | ||
386 | func testNewArrays() { | |
387 | self.testNewArray(n:0,pad:0) | |
388 | for i in 1..<20 { | |
389 | for pad in 0..<16 { | |
390 | self.testNewArray(n:i, pad:pad) | |
391 | } | |
392 | } | |
393 | } | |
394 | ||
395 | ||
396 | func testArrayLoadInfo(n : Int) { | |
397 | let buffer = NSMutableData(capacity:1000)! | |
398 | var item = kcdata_item() | |
399 | ||
400 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
401 | item.flags = 0 | |
402 | item.size = 0 | |
403 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
404 | ||
405 | item.type = UInt32(KCDATA_TYPE_ARRAY_PAD0) | |
406 | item.flags = UInt64(KCDATA_TYPE_LIBRARY_LOADINFO) << 32 | UInt64(n) | |
407 | item.size = UInt32(n * MemoryLayout<dyld_uuid_info_32>.size) | |
408 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
409 | ||
410 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
411 | ||
412 | ||
413 | for i in 0..<n { | |
414 | var payload = dyld_uuid_info_32(imageLoadAddress:UInt32(i+42), imageUUID: nsuuid2uuid_t(uuid)) | |
415 | ||
416 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size) | |
417 | } | |
418 | ||
419 | item.type = KCDATA_TYPE_BUFFER_END | |
420 | item.flags = 0 | |
421 | item.size = 0 | |
422 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
423 | ||
424 | guard let dict = try? self.parseBuffer(buffer) | |
425 | else { XCTFail(); return; } | |
426 | XCTAssert((dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as! [Any]).count == n) | |
427 | for i in 0..<n { | |
428 | guard let loadinfo = dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as? [Any] | |
429 | else { XCTFail(); return; } | |
430 | guard let loadinfo_i = loadinfo[i] as? [AnyHashable:Any] | |
431 | else { XCTFail(); return; } | |
432 | XCTAssert(loadinfo_i["imageLoadAddress"] as? Int == 42 + i) | |
433 | XCTAssert(loadinfo_i["imageUUID"] as! [Int] == nsuuid2array(uuid)) | |
434 | } | |
435 | } | |
436 | ||
437 | func testArrayLoadInfo() { | |
438 | for n in 0..<20 { | |
439 | testArrayLoadInfo(n: n) | |
440 | } | |
441 | } | |
442 | ||
443 | func testArrayLoadInfoWrongSize() { | |
444 | // test what happens when array element sizes are too short | |
445 | ||
446 | let n = 7 | |
447 | let wrong = 1 | |
448 | let buffer = NSMutableData(capacity:1000)! | |
449 | var item = kcdata_item() | |
450 | ||
451 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
452 | item.flags = 0 | |
453 | item.size = 0 | |
454 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
455 | ||
456 | item.type = UInt32(KCDATA_TYPE_ARRAY_PAD0) | |
457 | item.flags = UInt64(KCDATA_TYPE_LIBRARY_LOADINFO) << 32 | UInt64(n) | |
458 | item.size = UInt32(n * (MemoryLayout<dyld_uuid_info_32>.size - wrong)) | |
459 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
460 | ||
461 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
462 | ||
463 | for i in 0..<n { | |
464 | var payload = dyld_uuid_info_32(imageLoadAddress:UInt32(i+42), imageUUID: nsuuid2uuid_t(uuid)) | |
465 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size-wrong) | |
466 | } | |
467 | ||
468 | item.type = KCDATA_TYPE_BUFFER_END | |
469 | item.flags = 0 | |
470 | item.size = 0 | |
471 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
472 | var uuidarray = nsuuid2array(uuid) | |
473 | uuidarray.removeLast() | |
474 | ||
475 | guard let dict = try? self.parseBuffer(buffer) | |
476 | else { XCTFail(); return; } | |
477 | XCTAssert((dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as! [Any]).count == n) | |
478 | for i in 0..<n { | |
479 | guard let loadinfo = dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as? [Any] | |
480 | else { XCTFail(); return; } | |
481 | guard let loadinfo_i = loadinfo[i] as? [AnyHashable:Any] | |
482 | else { XCTFail(); return; } | |
483 | XCTAssert(loadinfo_i["imageLoadAddress"] as? Int == 42 + i) | |
484 | XCTAssert(loadinfo_i["imageUUID"] as! [Int] == uuidarray) | |
485 | } | |
486 | ||
487 | } | |
488 | ||
489 | ||
490 | func testArrayLoadInfoWayWrongSize() { | |
491 | // test what happens when array element sizes are too short | |
492 | ||
493 | let n = 7 | |
494 | let wrong = 16 | |
495 | let buffer = NSMutableData(capacity:1000)! | |
496 | var item = kcdata_item() | |
497 | ||
498 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
499 | item.flags = 0 | |
500 | item.size = 0 | |
501 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
502 | ||
503 | item.type = UInt32(KCDATA_TYPE_ARRAY_PAD0) | |
504 | item.flags = UInt64(KCDATA_TYPE_LIBRARY_LOADINFO) << 32 | UInt64(n) | |
505 | item.size = UInt32(n * (MemoryLayout<dyld_uuid_info_32>.size - wrong)) | |
506 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
507 | ||
508 | let uuid = NSUUID(uuidString: "de305d54-75b4-431b-adb2-eb6b9e546014")! | |
509 | ||
510 | for i in 0..<n { | |
511 | var payload = dyld_uuid_info_32(imageLoadAddress:UInt32(i+42), imageUUID: nsuuid2uuid_t(uuid)) | |
512 | buffer.append(&payload, length:MemoryLayout<dyld_uuid_info_32>.size-wrong) | |
513 | } | |
514 | ||
515 | item.type = KCDATA_TYPE_BUFFER_END | |
516 | item.flags = 0 | |
517 | item.size = 0 | |
518 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
519 | ||
520 | ||
521 | guard let dict = try? self.parseBuffer(buffer) | |
522 | else { XCTFail(); return; } | |
523 | XCTAssert((dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as! [Any]).count == n) | |
524 | for i in 0..<n { | |
525 | guard let loadinfo = dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as? [Any] | |
526 | else { XCTFail(); return; } | |
527 | guard let loadinfo_i = loadinfo[i] as? [AnyHashable:Any] | |
528 | else { XCTFail(); return; } | |
529 | XCTAssert(loadinfo_i["imageLoadAddress"] as? Int == 42 + i) | |
530 | XCTAssert(loadinfo_i["imageUUID"] == nil) | |
531 | } | |
532 | } | |
533 | ||
534 | func testArrayLoadInfoPreposterouslyWrongSize() { | |
535 | // test what happens when array element sizes are too short | |
536 | ||
537 | let n = 7 | |
538 | let wrong = 19 | |
539 | let buffer = NSMutableData(capacity:1000)! | |
540 | var item = kcdata_item() | |
541 | ||
542 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
543 | item.flags = 0 | |
544 | item.size = 0 | |
545 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
546 | ||
547 | item.type = UInt32(KCDATA_TYPE_ARRAY_PAD0) | |
548 | item.flags = UInt64(KCDATA_TYPE_LIBRARY_LOADINFO) << 32 | UInt64(n) | |
549 | item.size = UInt32(n * (MemoryLayout<dyld_uuid_info_32>.size - wrong)) | |
550 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
551 | ||
552 | for i in 0..<n { | |
553 | var payload = UInt8(42*i) | |
554 | buffer.append(&payload, length:1) | |
555 | } | |
556 | ||
557 | item.type = KCDATA_TYPE_BUFFER_END | |
558 | item.flags = 0 | |
559 | item.size = 0 | |
560 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
561 | ||
562 | ||
563 | guard let dict = try? self.parseBuffer(buffer) | |
564 | else { XCTFail(); return; } | |
565 | XCTAssert((dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as! [Any]).count == n) | |
566 | for i in 0..<n { | |
567 | guard let loadinfo = dict.value(forKeyPath:"kcdata_crashinfo.dyld_load_info") as? [Any] | |
568 | else { XCTFail(); return; } | |
569 | guard let loadinfo_i = loadinfo[i] as? [AnyHashable:Any] | |
570 | else { XCTFail(); return; } | |
571 | XCTAssert(loadinfo_i["imageLoadAddress"] == nil) | |
572 | XCTAssert(loadinfo_i["imageUUID"] == nil) | |
573 | } | |
574 | } | |
575 | ||
576 | ||
577 | func testNested() { | |
578 | let buffer = NSMutableData(capacity:1000)! | |
579 | ||
580 | var item = kcdata_item() | |
581 | ||
582 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
583 | item.flags = 0 | |
584 | item.size = 0 | |
585 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
586 | ||
587 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
588 | item.flags = 0 | |
589 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
590 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
591 | ||
592 | var payload : UInt64 = 42 | |
593 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
594 | ||
595 | item.type = KCDATA_TYPE_BUFFER_END | |
596 | item.flags = 0 | |
597 | item.size = 0 | |
598 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
599 | ||
600 | let buffer2 = NSMutableData(capacity:1000)! | |
601 | ||
602 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
603 | item.flags = 0 | |
604 | item.size = 0 | |
605 | buffer2.append(&item, length: MemoryLayout<kcdata_item>.size) | |
606 | ||
607 | item.type = UInt32(KCDATA_TYPE_NESTED_KCDATA) | |
608 | item.flags = 0 | |
609 | item.size = UInt32(buffer.length) | |
610 | buffer2.append(&item, length: MemoryLayout<kcdata_item>.size) | |
611 | buffer2.append(buffer as Data) | |
612 | ||
613 | item.type = KCDATA_TYPE_BUFFER_END | |
614 | item.flags = 0 | |
615 | item.size = 0 | |
616 | buffer2.append(&item, length: MemoryLayout<kcdata_item>.size) | |
617 | ||
618 | guard let dict2 = try? self.parseBuffer(buffer2) | |
619 | else { XCTFail(); return; } | |
620 | ||
621 | XCTAssert(dict2.value(forKeyPath:"kcdata_crashinfo.kcdata_crashinfo.crashed_threadid") as? Int == 42) | |
622 | } | |
623 | ||
624 | ||
625 | func testReadThreadid() { | |
626 | let buffer = NSMutableData(capacity:1000)! | |
627 | ||
628 | var item = kcdata_item() | |
629 | ||
630 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
631 | item.flags = 0 | |
632 | item.size = 0 | |
633 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
634 | ||
635 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
636 | item.flags = 0 | |
637 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
638 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
639 | ||
640 | var payload : UInt64 = 42 | |
641 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
642 | ||
643 | item.type = KCDATA_TYPE_BUFFER_END | |
644 | item.flags = 0 | |
645 | item.size = 0 | |
646 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
647 | ||
648 | guard let dict = try? self.parseBuffer(buffer) | |
649 | else { XCTFail(); return; } | |
650 | ||
651 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as? Int == 42) | |
652 | } | |
653 | ||
654 | ||
655 | func testRepeatedKey() { | |
656 | // test a repeated item of the same key causes error | |
657 | ||
658 | let buffer = NSMutableData(capacity:1000)! | |
659 | ||
660 | var item = kcdata_item() | |
661 | ||
662 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
663 | item.flags = 0 | |
664 | item.size = 0 | |
665 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
666 | ||
667 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
668 | item.flags = 0 | |
669 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
670 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
671 | ||
672 | var payload : UInt64 = 42 | |
673 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
674 | ||
675 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
676 | item.flags = 0 | |
677 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
678 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
679 | ||
680 | payload = 42 | |
681 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
682 | ||
683 | item.type = KCDATA_TYPE_BUFFER_END | |
684 | item.flags = 0 | |
685 | item.size = 0 | |
686 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
687 | ||
688 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
689 | } | |
690 | ||
691 | ||
692 | func testContainer() { | |
693 | let buffer = NSMutableData(capacity:1000)! | |
694 | ||
695 | var item = kcdata_item() | |
696 | var payload64 : UInt64 | |
697 | var payload32 : UInt32 | |
698 | ||
699 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
700 | item.flags = 0 | |
701 | item.size = 0 | |
702 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
703 | ||
704 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
705 | item.flags = 0 | |
706 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
707 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
708 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
709 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
710 | ||
711 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
712 | item.flags = 0 | |
713 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
714 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
715 | payload64 = 42 | |
716 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
717 | ||
718 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
719 | item.flags = 0 | |
720 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
721 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
722 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
723 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
724 | ||
725 | ||
726 | item.type = KCDATA_TYPE_BUFFER_END | |
727 | item.flags = 0 | |
728 | item.size = 0 | |
729 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
730 | ||
731 | guard let dict = try? self.parseBuffer(buffer) | |
732 | else { XCTFail(); return; } | |
733 | ||
734 | XCTAssert(dict.value(forKeyPath: "kcdata_crashinfo.task_snapshots.0.crashed_threadid") as? Int == 42) | |
735 | } | |
736 | ||
737 | func testDispatchQueueLabel() { | |
738 | let buffer = NSMutableData(capacity:1000)! | |
739 | ||
740 | var item = kcdata_item() | |
741 | let dql = "houston.we.had.a.problem" | |
742 | var payload32 : UInt32 | |
743 | ||
744 | item.type = KCDATA_BUFFER_BEGIN_STACKSHOT | |
745 | item.flags = 0 | |
746 | item.size = 0 | |
747 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
748 | ||
749 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
750 | item.flags = 0 | |
751 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
752 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
753 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
754 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
755 | ||
756 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
757 | item.flags = 0 | |
758 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
759 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
760 | payload32 = UInt32(STACKSHOT_KCCONTAINER_THREAD) | |
761 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
762 | ||
763 | item.type = UInt32(STACKSHOT_KCTYPE_THREAD_DISPATCH_QUEUE_LABEL) | |
764 | item.flags = 0 | |
765 | item.size = UInt32(dql.utf8.count + 1) | |
766 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
767 | dql.utf8CString.withUnsafeBufferPointer({ | |
768 | buffer.append($0.baseAddress!, length:dql.utf8.count + 1) | |
769 | }) | |
770 | ||
771 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
772 | item.flags = 0 | |
773 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
774 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
775 | payload32 = UInt32(STACKSHOT_KCCONTAINER_THREAD) | |
776 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
777 | ||
778 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
779 | item.flags = 0 | |
780 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
781 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
782 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
783 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
784 | ||
785 | ||
786 | item.type = KCDATA_TYPE_BUFFER_END | |
787 | item.flags = 0 | |
788 | item.size = 0 | |
789 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
790 | ||
791 | guard let dict = try? self.parseBuffer(buffer) | |
792 | else { XCTFail(); return; } | |
793 | ||
794 | XCTAssert(dict.value(forKeyPath: "kcdata_stackshot.task_snapshots.0.thread_snapshots.0.dispatch_queue_label") as? String == dql) | |
795 | } | |
796 | ||
797 | func testRepeatedContainer() { | |
798 | //repeated container of same name and key shoudl fail | |
799 | ||
800 | let buffer = NSMutableData(capacity:1000)! | |
801 | ||
802 | var item = kcdata_item() | |
803 | var payload64 : UInt64 | |
804 | var payload32 : UInt32 | |
805 | ||
806 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
807 | item.flags = 0 | |
808 | item.size = 0 | |
809 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
810 | ||
811 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
812 | item.flags = 0 | |
813 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
814 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
815 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
816 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
817 | ||
818 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
819 | item.flags = 0 | |
820 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
821 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
822 | payload64 = 42 | |
823 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
824 | ||
825 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
826 | item.flags = 0 | |
827 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
828 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
829 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
830 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
831 | ||
832 | ||
833 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
834 | item.flags = 0 | |
835 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
836 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
837 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
838 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
839 | ||
840 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
841 | item.flags = 0 | |
842 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
843 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
844 | payload64 = 42 | |
845 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
846 | ||
847 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
848 | item.flags = 0 | |
849 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
850 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
851 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
852 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
853 | ||
854 | item.type = KCDATA_TYPE_BUFFER_END | |
855 | item.flags = 0 | |
856 | item.size = 0 | |
857 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
858 | ||
859 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
860 | } | |
861 | ||
862 | ||
863 | func testContainerNoEnd() { | |
864 | let buffer = NSMutableData(capacity:1000)! | |
865 | ||
866 | var item = kcdata_item() | |
867 | var payload64 : UInt64 | |
868 | var payload32 : UInt32 | |
869 | ||
870 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
871 | item.flags = 0 | |
872 | item.size = 0 | |
873 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
874 | ||
875 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
876 | item.flags = 0 | |
877 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
878 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
879 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
880 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
881 | ||
882 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
883 | item.flags = 0 | |
884 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
885 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
886 | payload64 = 42 | |
887 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
888 | ||
889 | item.type = KCDATA_TYPE_BUFFER_END | |
890 | item.flags = 0 | |
891 | item.size = 0 | |
892 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
893 | ||
894 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
895 | } | |
896 | ||
897 | func testContainerNoEndNoEnd() { | |
898 | let buffer = NSMutableData(capacity:1000)! | |
899 | ||
900 | var item = kcdata_item() | |
901 | var payload64 : UInt64 | |
902 | var payload32 : UInt32 | |
903 | ||
904 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
905 | item.flags = 0 | |
906 | item.size = 0 | |
907 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
908 | ||
909 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
910 | item.flags = 0 | |
911 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
912 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
913 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
914 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
915 | ||
916 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
917 | item.flags = 0 | |
918 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
919 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
920 | payload64 = 42 | |
921 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
922 | ||
923 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
924 | } | |
925 | ||
926 | ||
927 | ||
928 | func testNoEnd() { | |
929 | let buffer = NSMutableData(capacity:1000)! | |
930 | ||
931 | var item = kcdata_item() | |
932 | ||
933 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
934 | item.flags = 0 | |
935 | item.size = 0 | |
936 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
937 | ||
938 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
939 | item.flags = 0 | |
940 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
941 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
942 | ||
943 | var payload : UInt64 = 42 | |
944 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
945 | ||
946 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
947 | } | |
948 | ||
949 | ||
950 | func testCrazySize() { | |
951 | let buffer = NSMutableData(capacity:1000)! | |
952 | ||
953 | var item = kcdata_item() | |
954 | ||
955 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
956 | item.flags = 0 | |
957 | item.size = 0 | |
958 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
959 | ||
960 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
961 | item.flags = 0 | |
962 | item.size = 99999 | |
963 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
964 | ||
965 | var payload : UInt64 = 42 | |
966 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
967 | ||
968 | item.type = KCDATA_TYPE_BUFFER_END | |
969 | item.flags = 0 | |
970 | item.size = 0 | |
971 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
972 | ||
973 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
974 | } | |
975 | ||
976 | func testReadRepeatedArray() { | |
977 | // repeated arrays should be concatenated | |
978 | let n = 10 | |
979 | ||
980 | let buffer = NSMutableData(capacity:1000)! | |
981 | ||
982 | var item = kcdata_item() | |
983 | ||
984 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
985 | item.flags = 0 | |
986 | item.size = 0 | |
987 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
988 | ||
989 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
990 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
991 | item.size = UInt32(n * MemoryLayout<UInt64>.size) | |
992 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
993 | ||
994 | for i in 0..<n { | |
995 | var payload : UInt64 = UInt64(i) | |
996 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
997 | } | |
998 | ||
999 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1000 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1001 | item.size = UInt32(n * MemoryLayout<UInt64>.size) | |
1002 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1003 | ||
1004 | for i in 0..<n { | |
1005 | var payload : UInt64 = UInt64(i) | |
1006 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1007 | } | |
1008 | ||
1009 | item.type = KCDATA_TYPE_BUFFER_END | |
1010 | item.flags = 0 | |
1011 | item.size = 0 | |
1012 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1013 | ||
1014 | guard let dict = try? self.parseBuffer(buffer) | |
1015 | else { XCTFail(); return } | |
1016 | ||
1017 | XCTAssert( 2*n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1018 | for i in 0..<2*n { | |
1019 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1020 | let y = x?["crashed_threadid"] as? NSArray | |
1021 | XCTAssert((y?[i]) as? Int == i % n) | |
1022 | } | |
1023 | } | |
1024 | ||
1025 | func testReadThreadidArray(n : Int, pad : Int) { | |
1026 | let buffer = NSMutableData(capacity:1000)! | |
1027 | ||
1028 | var item = kcdata_item() | |
1029 | ||
1030 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1031 | item.flags = 0 | |
1032 | item.size = 0 | |
1033 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1034 | ||
1035 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1036 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1037 | item.size = UInt32(n * MemoryLayout<UInt64>.size + pad) | |
1038 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1039 | ||
1040 | for i in 0..<n { | |
1041 | var payload : UInt64 = UInt64(i) | |
1042 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1043 | } | |
1044 | ||
1045 | for _ in 0..<pad { | |
1046 | var payload : UInt8 = 0 | |
1047 | buffer.append(&payload, length:1) | |
1048 | } | |
1049 | ||
1050 | item.type = KCDATA_TYPE_BUFFER_END | |
1051 | item.flags = 0 | |
1052 | item.size = 0 | |
1053 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1054 | ||
1055 | guard let dict = try? self.parseBuffer(buffer) | |
1056 | else { XCTFail(); return; } | |
1057 | ||
1058 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1059 | ||
1060 | for i in 0..<n { | |
1061 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1062 | let y = x?["crashed_threadid"] as? NSArray | |
1063 | XCTAssert((y?[i]) as? Int == i) | |
1064 | } | |
1065 | ||
1066 | } | |
1067 | ||
1068 | func testReadThreadidArray() { | |
1069 | // test that we can correctly read old arrays with a variety of sizes and paddings | |
1070 | self.testReadThreadidArray(n: 0, pad:0) | |
1071 | for n in 1..<100 { | |
1072 | for pad in 0..<16 { | |
1073 | self.testReadThreadidArray(n: n, pad:pad) | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | func testReadThreadidArrayWrongSize1() { | |
1079 | /// for old style arrays, if the element size is determined by the type. If the array of that size element at the given count doesn't fit, then parsing should fail | |
1080 | ||
1081 | let n = 1 | |
1082 | ||
1083 | let buffer = NSMutableData(capacity:1000)! | |
1084 | ||
1085 | var item = kcdata_item() | |
1086 | ||
1087 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1088 | item.flags = 0 | |
1089 | item.size = 0 | |
1090 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1091 | ||
1092 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1093 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1094 | item.size = UInt32(4) | |
1095 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1096 | ||
1097 | var payload : UInt32 = UInt32(42) | |
1098 | buffer.append(&payload, length:MemoryLayout<UInt32>.size) | |
1099 | ||
1100 | item.type = KCDATA_TYPE_BUFFER_END | |
1101 | item.flags = 0 | |
1102 | item.size = 0 | |
1103 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1104 | ||
1105 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
1106 | } | |
1107 | ||
1108 | func testReadThreadidArrayWrongSize5() { | |
1109 | /// if the count is bigger than the buffer, parsing will just fail | |
1110 | ||
1111 | let n = 5 | |
1112 | ||
1113 | let buffer = NSMutableData(capacity:1000)! | |
1114 | ||
1115 | var item = kcdata_item() | |
1116 | ||
1117 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1118 | item.flags = 0 | |
1119 | item.size = 0 | |
1120 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1121 | ||
1122 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1123 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1124 | item.size = UInt32(4) | |
1125 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1126 | ||
1127 | var payload : UInt32 = UInt32(42) | |
1128 | buffer.append(&payload, length:MemoryLayout<UInt32>.size) | |
1129 | ||
1130 | item.type = KCDATA_TYPE_BUFFER_END | |
1131 | item.flags = 0 | |
1132 | item.size = 0 | |
1133 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1134 | ||
1135 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
1136 | } | |
1137 | ||
1138 | ||
1139 | func testReadThreadidArrayPaddedSize() { | |
1140 | // test that we can tolerate a little padding at the end of an array | |
1141 | let n = 5 | |
1142 | ||
1143 | let buffer = NSMutableData(capacity:1000)! | |
1144 | ||
1145 | var item = kcdata_item() | |
1146 | ||
1147 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1148 | item.flags = 0 | |
1149 | item.size = 0 | |
1150 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1151 | ||
1152 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1153 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1154 | item.size = UInt32(n * MemoryLayout<UInt64>.size) + 1 | |
1155 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1156 | ||
1157 | for i in 0..<n { | |
1158 | var payload : UInt64 = UInt64(i) | |
1159 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1160 | } | |
1161 | var payload : UInt8 = 0 | |
1162 | buffer.append(&payload, length:1) | |
1163 | ||
1164 | item.type = KCDATA_TYPE_BUFFER_END | |
1165 | item.flags = 0 | |
1166 | item.size = 0 | |
1167 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1168 | ||
1169 | guard let dict = try? self.parseBuffer(buffer) | |
1170 | else { XCTFail(); return; } | |
1171 | ||
1172 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1173 | for i in 0..<n { | |
1174 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1175 | let y = x?["crashed_threadid"] as? NSArray | |
1176 | XCTAssert((y?[i]) as? Int == i) | |
1177 | } | |
1178 | } | |
1179 | ||
1180 | func testReadThreadidArrayPaddedSize15() { | |
1181 | // test that we can tolerate a little padding at the end of an array | |
1182 | let n = 5 | |
1183 | ||
1184 | let buffer = NSMutableData(capacity:1000)! | |
1185 | ||
1186 | var item = kcdata_item() | |
1187 | ||
1188 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1189 | item.flags = 0 | |
1190 | item.size = 0 | |
1191 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1192 | ||
1193 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1194 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1195 | item.size = UInt32(n * MemoryLayout<UInt64>.size) + 15 | |
1196 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1197 | ||
1198 | for i in 0..<n { | |
1199 | var payload : UInt64 = UInt64(i) | |
1200 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1201 | } | |
1202 | for _ in 0..<15 { | |
1203 | var payload : UInt8 = 0 | |
1204 | buffer.append(&payload, length:1) | |
1205 | } | |
1206 | ||
1207 | item.type = KCDATA_TYPE_BUFFER_END | |
1208 | item.flags = 0 | |
1209 | item.size = 0 | |
1210 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1211 | ||
1212 | guard let dict = try? self.parseBuffer(buffer) | |
1213 | else { XCTFail(); return; } | |
1214 | ||
1215 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1216 | for i in 0..<n { | |
1217 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1218 | let y = x?["crashed_threadid"] as? NSArray | |
1219 | XCTAssert((y?[i]) as? Int == i) | |
1220 | } | |
1221 | } | |
1222 | ||
1223 | ||
1224 | func testReadThreadidWrongSize(size : UInt32) { | |
1225 | let buffer = NSMutableData(capacity:1000)! | |
1226 | ||
1227 | var item = kcdata_item() | |
1228 | ||
1229 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1230 | item.flags = 0 | |
1231 | item.size = 0 | |
1232 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1233 | ||
1234 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
1235 | item.flags = 0 | |
1236 | item.size = size | |
1237 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1238 | ||
1239 | var payload : UInt64 = 42 | |
1240 | buffer.append(&payload, length:Int(size)) | |
1241 | ||
1242 | item.type = KCDATA_TYPE_BUFFER_END | |
1243 | item.flags = 0 | |
1244 | item.size = 0 | |
1245 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1246 | ||
1247 | guard let dict = try? self.parseBuffer(buffer) | |
1248 | else { XCTFail(); return; } | |
1249 | ||
1250 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") == nil) | |
1251 | } | |
1252 | ||
1253 | func testReadThreadidWrongSize0() { | |
1254 | self.testReadThreadidWrongSize(size: 0) | |
1255 | } | |
1256 | ||
1257 | func testReadThreadidWrongSize7() { | |
1258 | self.testReadThreadidWrongSize(size: 7) | |
1259 | } | |
1260 | ||
1261 | func dataWithResource(_ name:String) -> NSData? { | |
1262 | guard let filename = Bundle(for: self.classForCoder).path(forResource: name, ofType: nil) | |
1263 | else { return nil } | |
1264 | return NSData(contentsOfFile:filename)! | |
1265 | } | |
1266 | ||
1267 | func testSampleStackshot(_ name : String) { | |
1268 | // check that we agree with sample file | |
1269 | ||
1270 | guard let sampledata = self.dataWithResource(name) | |
1271 | else { XCTFail("failed to open bundle resource named " + name); return } | |
1272 | var dict : NSDictionary? | |
1273 | ||
1274 | dict = try? self.parseBuffer(sampledata) as NSDictionary | |
1275 | ||
1276 | if (dict == nil) { | |
1277 | if let decoded = NSData(base64Encoded: sampledata as Data, options:.ignoreUnknownCharacters) { | |
1278 | dict = try? self.parseBuffer(decoded) as NSDictionary | |
1279 | } | |
1280 | } | |
1281 | ||
1282 | if (dict == nil) { | |
1283 | if let decompressed = try? decompress(sampledata) { | |
1284 | dict = try? self.parseBuffer(decompressed) as NSDictionary | |
1285 | } | |
1286 | } | |
1287 | ||
1288 | if (dict == nil) { | |
1289 | XCTFail(); return; | |
1290 | } | |
1291 | ||
1292 | guard let plistdata = self.dataWithResource(name + ".plist.gz") ?? | |
1293 | self.dataWithResource(name + ".plist") | |
1294 | else {XCTFail(); return} | |
1295 | ||
1296 | var opt_dict2 = try? PropertyListSerialization.propertyList(from: plistdata as Data, options: [], format: nil) | |
1297 | if opt_dict2 == nil { | |
1298 | opt_dict2 = try? PropertyListSerialization.propertyList(from:decompress(plistdata) as Data, options:[], format: nil) | |
1299 | } | |
1300 | guard let dict2 = opt_dict2 | |
1301 | else { XCTFail(); return} | |
1302 | ||
1303 | XCTAssertEqual(dict, dict2 as! NSDictionary); | |
1304 | ||
1305 | //XCTAssert(dict == dict2 as? NSDictionary) | |
1306 | ||
1307 | // check that we agree with python | |
1308 | ||
1309 | #if os(OSX) | |
1310 | ||
1311 | let kcdatapy = Bundle(for: self.classForCoder).path(forResource: "kcdata.py", ofType: nil) | |
1312 | ||
1313 | let task = Process() | |
1314 | task.launchPath = kcdatapy | |
1315 | task.arguments = ["-p", | |
1316 | Bundle(for:self.classForCoder).path(forResource: name, ofType: nil)!] | |
1317 | let pipe = Pipe() | |
1318 | task.standardOutput = pipe | |
1319 | task.launch() | |
1320 | ||
1321 | let data = pipe.fileHandleForReading.readDataToEndOfFile() | |
1322 | ||
1323 | guard let dict3 = try? PropertyListSerialization.propertyList(from:data, options:[], format: nil) as? NSDictionary | |
1324 | else { XCTFail(); return } | |
1325 | ||
1326 | XCTAssert(dict == dict3) | |
1327 | ||
1328 | #endif | |
1329 | } | |
1330 | ||
1331 | func testSampleStackshot() { | |
1332 | self.testSampleStackshot("stackshot-sample") | |
1333 | } | |
1334 | ||
1335 | func testSampleStackshotOldArrays() { | |
1336 | self.testSampleStackshot("stackshot-sample-old-arrays") | |
1337 | } | |
1338 | ||
1339 | func testSampleStackshotNewArrays() { | |
1340 | self.testSampleStackshot("stackshot-sample-new-arrays") | |
1341 | } | |
1342 | ||
1343 | func testSampleDeltaStackshotOldArrays() { | |
1344 | self.testSampleStackshot("delta-stackshot-sample-old-arrays") | |
1345 | } | |
1346 | ||
1347 | func testSampleDeltaStackshotNewArrays() { | |
1348 | self.testSampleStackshot("delta-stackshot-sample-new-arrays") | |
1349 | } | |
1350 | ||
1351 | func testSampleCorpse() { | |
1352 | self.testSampleStackshot("corpse-sample") | |
1353 | } | |
1354 | ||
1355 | func testSampleStackshotTailspin() { | |
1356 | self.testSampleStackshot("stackshot-sample-tailspin") | |
1357 | } | |
1358 | ||
1359 | func testSampleStackshotTailspin2() { | |
1360 | self.testSampleStackshot("stackshot-sample-tailspin-2") | |
1361 | } | |
1362 | ||
1363 | func testSampleExitReason() { | |
1364 | self.testSampleStackshot("exitreason-sample") | |
1365 | } | |
1366 | ||
1367 | func testSampleThreadT() { | |
1368 | self.testSampleStackshot("stackshot-sample-ths-thread-t") | |
1369 | } | |
1370 | ||
1371 | func testSampleCpuTimes() { | |
1372 | self.testSampleStackshot("stackshot-sample-cputime") | |
1373 | } | |
1374 | ||
1375 | func testSampleDuration() { | |
1376 | self.testSampleStackshot("stackshot-sample-duration") | |
1377 | } | |
1378 | ||
1379 | func testSampleNested() { | |
1380 | self.testSampleStackshot("nested-sample") | |
1381 | } | |
1382 | ||
1383 | func testSampleTermWithReason() { | |
1384 | self.testSampleStackshot("test-twr-sample") | |
1385 | } | |
1386 | ||
1387 | func testSampleCorpseTermWithReason() { | |
1388 | self.testSampleStackshot("corpse-twr-sample") | |
1389 | } | |
1390 | ||
1391 | func testSampleCorpseTermWithReasonV2() { | |
1392 | self.testSampleStackshot("corpse-twr-sample-v2") | |
1393 | } | |
1394 | ||
1395 | func testSampleCodesigningExitReason() { | |
1396 | self.testSampleStackshot("exitreason-codesigning") | |
1397 | } | |
1398 | ||
1399 | func testSampleThreadGroups() { | |
1400 | self.testSampleStackshot("stackshot-sample-thread-groups") | |
1401 | } | |
1402 | ||
1403 | func testSampleThreadGroupsFlags() { | |
1404 | self.testSampleStackshot("stackshot-sample-thread-groups-flags") | |
1405 | } | |
1406 | ||
1407 | func testSampleCoalitions() { | |
1408 | self.testSampleStackshot("stackshot-sample-coalitions") | |
1409 | } | |
1410 | ||
1411 | func testSampleTurnstileInfo() { | |
1412 | self.testSampleStackshot("stackshot-sample-turnstileinfo") | |
1413 | } | |
1414 | ||
1415 | func testStackshotSharedcacheV2() { | |
1416 | self.testSampleStackshot("stackshot-sample-sharedcachev2") | |
1417 | } | |
1418 | ||
1419 | func testStackshotFaultStats() { | |
1420 | self.testSampleStackshot("stackshot-fault-stats") | |
1421 | } | |
1422 | ||
1423 | func testStackshotwithKCID() { | |
1424 | self.testSampleStackshot("stackshot-with-kcid") | |
1425 | } | |
1426 | ||
1427 | func testXNUPostTestConfig() { | |
1428 | self.testSampleStackshot("xnupost_testconfig-sample") | |
1429 | } | |
1430 | ||
1431 | func testStackshotWithWaitinfo() { | |
1432 | self.testSampleStackshot("stackshot-with-waitinfo") | |
1433 | } | |
1434 | ||
1435 | func testStackshotWithThreadPolicy() { | |
1436 | self.testSampleStackshot("stackshot-sample-thread-policy") | |
1437 | } | |
1438 | ||
1439 | func testDeltaStackshotWithThreadPolicy() { | |
1440 | self.testSampleStackshot("stackshot-sample-delta-thread-policy") | |
1441 | } | |
1442 | ||
1443 | func testStackshotWithInstrsCycles() { | |
1444 | self.testSampleStackshot("stackshot-sample-instrs-cycles") | |
1445 | } | |
1446 | ||
1447 | func testStackshotWithStacktop() { | |
1448 | self.testSampleStackshot("stackshot-sample-stacktop") | |
1449 | } | |
1450 | ||
1451 | func testStackshotWithASID() { | |
1452 | self.testSampleStackshot("stackshot-sample-asid") | |
1453 | } | |
1454 | ||
1455 | func testStackshotWithPageTables() { | |
1456 | self.testSampleStackshot("stackshot-sample-asid-pagetable") | |
1457 | } | |
1458 | ||
1459 | func testStackshotCPUTimes() { | |
1460 | self.testSampleStackshot("stackshot-sample-cpu-times") | |
1461 | } | |
1462 | ||
1463 | func testStackshotWithSharedCacheLayout() { | |
1464 | self.testSampleStackshot("stackshot-with-shared-cache-layout") | |
1465 | } | |
1466 | ||
1467 | func testStackshotDispatchQueueLabel() { | |
1468 | self.testSampleStackshot("stackshot-sample-dispatch-queue-label") | |
1469 | } | |
1470 | ||
1471 | func testTrivial() { | |
1472 | } | |
1473 | } |