]>
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 testRepeatedContainer() { | |
738 | //repeated container of same name and key shoudl fail | |
739 | ||
740 | let buffer = NSMutableData(capacity:1000)! | |
741 | ||
742 | var item = kcdata_item() | |
743 | var payload64 : UInt64 | |
744 | var payload32 : UInt32 | |
745 | ||
746 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
747 | item.flags = 0 | |
748 | item.size = 0 | |
749 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
750 | ||
751 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
752 | item.flags = 0 | |
753 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
754 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
755 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
756 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
757 | ||
758 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
759 | item.flags = 0 | |
760 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
761 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
762 | payload64 = 42 | |
763 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
764 | ||
765 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
766 | item.flags = 0 | |
767 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
768 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
769 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
770 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
771 | ||
772 | ||
773 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
774 | item.flags = 0 | |
775 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
776 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
777 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
778 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
779 | ||
780 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
781 | item.flags = 0 | |
782 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
783 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
784 | payload64 = 42 | |
785 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
786 | ||
787 | item.type = UInt32(KCDATA_TYPE_CONTAINER_END) | |
788 | item.flags = 0 | |
789 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
790 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
791 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
792 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
793 | ||
794 | item.type = KCDATA_TYPE_BUFFER_END | |
795 | item.flags = 0 | |
796 | item.size = 0 | |
797 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
798 | ||
799 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
800 | } | |
801 | ||
802 | ||
803 | func testContainerNoEnd() { | |
804 | let buffer = NSMutableData(capacity:1000)! | |
805 | ||
806 | var item = kcdata_item() | |
807 | var payload64 : UInt64 | |
808 | var payload32 : UInt32 | |
809 | ||
810 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
811 | item.flags = 0 | |
812 | item.size = 0 | |
813 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
814 | ||
815 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
816 | item.flags = 0 | |
817 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
818 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
819 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
820 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
821 | ||
822 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
823 | item.flags = 0 | |
824 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
825 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
826 | payload64 = 42 | |
827 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
828 | ||
829 | item.type = KCDATA_TYPE_BUFFER_END | |
830 | item.flags = 0 | |
831 | item.size = 0 | |
832 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
833 | ||
834 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
835 | } | |
836 | ||
837 | func testContainerNoEndNoEnd() { | |
838 | let buffer = NSMutableData(capacity:1000)! | |
839 | ||
840 | var item = kcdata_item() | |
841 | var payload64 : UInt64 | |
842 | var payload32 : UInt32 | |
843 | ||
844 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
845 | item.flags = 0 | |
846 | item.size = 0 | |
847 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
848 | ||
849 | item.type = UInt32(KCDATA_TYPE_CONTAINER_BEGIN) | |
850 | item.flags = 0 | |
851 | item.size = UInt32(MemoryLayout<UInt32>.size) | |
852 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
853 | payload32 = UInt32(STACKSHOT_KCCONTAINER_TASK) | |
854 | buffer.append(&payload32, length:MemoryLayout<UInt32>.size) | |
855 | ||
856 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
857 | item.flags = 0 | |
858 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
859 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
860 | payload64 = 42 | |
861 | buffer.append(&payload64, length:MemoryLayout<UInt64>.size) | |
862 | ||
863 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
864 | } | |
865 | ||
866 | ||
867 | ||
868 | func testNoEnd() { | |
869 | let buffer = NSMutableData(capacity:1000)! | |
870 | ||
871 | var item = kcdata_item() | |
872 | ||
873 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
874 | item.flags = 0 | |
875 | item.size = 0 | |
876 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
877 | ||
878 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
879 | item.flags = 0 | |
880 | item.size = UInt32(MemoryLayout<UInt64>.size) | |
881 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
882 | ||
883 | var payload : UInt64 = 42 | |
884 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
885 | ||
886 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
887 | } | |
888 | ||
889 | ||
890 | func testCrazySize() { | |
891 | let buffer = NSMutableData(capacity:1000)! | |
892 | ||
893 | var item = kcdata_item() | |
894 | ||
895 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
896 | item.flags = 0 | |
897 | item.size = 0 | |
898 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
899 | ||
900 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
901 | item.flags = 0 | |
902 | item.size = 99999 | |
903 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
904 | ||
905 | var payload : UInt64 = 42 | |
906 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
907 | ||
908 | item.type = KCDATA_TYPE_BUFFER_END | |
909 | item.flags = 0 | |
910 | item.size = 0 | |
911 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
912 | ||
913 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
914 | } | |
915 | ||
916 | func testReadRepeatedArray() { | |
917 | // repeated arrays should be concatenated | |
918 | let n = 10 | |
919 | ||
920 | let buffer = NSMutableData(capacity:1000)! | |
921 | ||
922 | var item = kcdata_item() | |
923 | ||
924 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
925 | item.flags = 0 | |
926 | item.size = 0 | |
927 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
928 | ||
929 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
930 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
931 | item.size = UInt32(n * MemoryLayout<UInt64>.size) | |
932 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
933 | ||
934 | for i in 0..<n { | |
935 | var payload : UInt64 = UInt64(i) | |
936 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
937 | } | |
938 | ||
939 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
940 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
941 | item.size = UInt32(n * MemoryLayout<UInt64>.size) | |
942 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
943 | ||
944 | for i in 0..<n { | |
945 | var payload : UInt64 = UInt64(i) | |
946 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
947 | } | |
948 | ||
949 | item.type = KCDATA_TYPE_BUFFER_END | |
950 | item.flags = 0 | |
951 | item.size = 0 | |
952 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
953 | ||
954 | guard let dict = try? self.parseBuffer(buffer) | |
955 | else { XCTFail(); return } | |
956 | ||
957 | XCTAssert( 2*n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
958 | for i in 0..<2*n { | |
959 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
960 | let y = x?["crashed_threadid"] as? NSArray | |
961 | XCTAssert((y?[i]) as? Int == i % n) | |
962 | } | |
963 | } | |
964 | ||
965 | func testReadThreadidArray(n : Int, pad : Int) { | |
966 | let buffer = NSMutableData(capacity:1000)! | |
967 | ||
968 | var item = kcdata_item() | |
969 | ||
970 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
971 | item.flags = 0 | |
972 | item.size = 0 | |
973 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
974 | ||
975 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
976 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
977 | item.size = UInt32(n * MemoryLayout<UInt64>.size + pad) | |
978 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
979 | ||
980 | for i in 0..<n { | |
981 | var payload : UInt64 = UInt64(i) | |
982 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
983 | } | |
984 | ||
985 | for _ in 0..<pad { | |
986 | var payload : UInt8 = 0 | |
987 | buffer.append(&payload, length:1) | |
988 | } | |
989 | ||
990 | item.type = KCDATA_TYPE_BUFFER_END | |
991 | item.flags = 0 | |
992 | item.size = 0 | |
993 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
994 | ||
995 | guard let dict = try? self.parseBuffer(buffer) | |
996 | else { XCTFail(); return; } | |
997 | ||
998 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
999 | ||
1000 | for i in 0..<n { | |
1001 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1002 | let y = x?["crashed_threadid"] as? NSArray | |
1003 | XCTAssert((y?[i]) as? Int == i) | |
1004 | } | |
1005 | ||
1006 | } | |
1007 | ||
1008 | func testReadThreadidArray() { | |
1009 | // test that we can correctly read old arrays with a variety of sizes and paddings | |
1010 | self.testReadThreadidArray(n: 0, pad:0) | |
1011 | for n in 1..<100 { | |
1012 | for pad in 0..<16 { | |
1013 | self.testReadThreadidArray(n: n, pad:pad) | |
1014 | } | |
1015 | } | |
1016 | } | |
1017 | ||
1018 | func testReadThreadidArrayWrongSize1() { | |
1019 | /// 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 | |
1020 | ||
1021 | let n = 1 | |
1022 | ||
1023 | let buffer = NSMutableData(capacity:1000)! | |
1024 | ||
1025 | var item = kcdata_item() | |
1026 | ||
1027 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1028 | item.flags = 0 | |
1029 | item.size = 0 | |
1030 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1031 | ||
1032 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1033 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1034 | item.size = UInt32(4) | |
1035 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1036 | ||
1037 | var payload : UInt32 = UInt32(42) | |
1038 | buffer.append(&payload, length:MemoryLayout<UInt32>.size) | |
1039 | ||
1040 | item.type = KCDATA_TYPE_BUFFER_END | |
1041 | item.flags = 0 | |
1042 | item.size = 0 | |
1043 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1044 | ||
1045 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
1046 | } | |
1047 | ||
1048 | func testReadThreadidArrayWrongSize5() { | |
1049 | /// if the count is bigger than the buffer, parsing will just fail | |
1050 | ||
1051 | let n = 5 | |
1052 | ||
1053 | let buffer = NSMutableData(capacity:1000)! | |
1054 | ||
1055 | var item = kcdata_item() | |
1056 | ||
1057 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1058 | item.flags = 0 | |
1059 | item.size = 0 | |
1060 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1061 | ||
1062 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1063 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1064 | item.size = UInt32(4) | |
1065 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1066 | ||
1067 | var payload : UInt32 = UInt32(42) | |
1068 | buffer.append(&payload, length:MemoryLayout<UInt32>.size) | |
1069 | ||
1070 | item.type = KCDATA_TYPE_BUFFER_END | |
1071 | item.flags = 0 | |
1072 | item.size = 0 | |
1073 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1074 | ||
1075 | XCTAssert( (try? self.parseBuffer(buffer)) == nil ) | |
1076 | } | |
1077 | ||
1078 | ||
1079 | func testReadThreadidArrayPaddedSize() { | |
1080 | // test that we can tolerate a little padding at the end of an array | |
1081 | let n = 5 | |
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(n * MemoryLayout<UInt64>.size) + 1 | |
1095 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1096 | ||
1097 | for i in 0..<n { | |
1098 | var payload : UInt64 = UInt64(i) | |
1099 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1100 | } | |
1101 | var payload : UInt8 = 0 | |
1102 | buffer.append(&payload, length:1) | |
1103 | ||
1104 | item.type = KCDATA_TYPE_BUFFER_END | |
1105 | item.flags = 0 | |
1106 | item.size = 0 | |
1107 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1108 | ||
1109 | guard let dict = try? self.parseBuffer(buffer) | |
1110 | else { XCTFail(); return; } | |
1111 | ||
1112 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1113 | for i in 0..<n { | |
1114 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1115 | let y = x?["crashed_threadid"] as? NSArray | |
1116 | XCTAssert((y?[i]) as? Int == i) | |
1117 | } | |
1118 | } | |
1119 | ||
1120 | func testReadThreadidArrayPaddedSize15() { | |
1121 | // test that we can tolerate a little padding at the end of an array | |
1122 | let n = 5 | |
1123 | ||
1124 | let buffer = NSMutableData(capacity:1000)! | |
1125 | ||
1126 | var item = kcdata_item() | |
1127 | ||
1128 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1129 | item.flags = 0 | |
1130 | item.size = 0 | |
1131 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1132 | ||
1133 | item.type = UInt32(KCDATA_TYPE_ARRAY) | |
1134 | item.flags = UInt64(TASK_CRASHINFO_CRASHED_THREADID) << 32 | UInt64(n) | |
1135 | item.size = UInt32(n * MemoryLayout<UInt64>.size) + 15 | |
1136 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1137 | ||
1138 | for i in 0..<n { | |
1139 | var payload : UInt64 = UInt64(i) | |
1140 | buffer.append(&payload, length:MemoryLayout<UInt64>.size) | |
1141 | } | |
1142 | for _ in 0..<15 { | |
1143 | var payload : UInt8 = 0 | |
1144 | buffer.append(&payload, length:1) | |
1145 | } | |
1146 | ||
1147 | item.type = KCDATA_TYPE_BUFFER_END | |
1148 | item.flags = 0 | |
1149 | item.size = 0 | |
1150 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1151 | ||
1152 | guard let dict = try? self.parseBuffer(buffer) | |
1153 | else { XCTFail(); return; } | |
1154 | ||
1155 | XCTAssert( n == (dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") as! [Any]).count) | |
1156 | for i in 0..<n { | |
1157 | let x = dict["kcdata_crashinfo"] as? NSDictionary | |
1158 | let y = x?["crashed_threadid"] as? NSArray | |
1159 | XCTAssert((y?[i]) as? Int == i) | |
1160 | } | |
1161 | } | |
1162 | ||
1163 | ||
1164 | func testReadThreadidWrongSize(size : UInt32) { | |
1165 | let buffer = NSMutableData(capacity:1000)! | |
1166 | ||
1167 | var item = kcdata_item() | |
1168 | ||
1169 | item.type = KCDATA_BUFFER_BEGIN_CRASHINFO | |
1170 | item.flags = 0 | |
1171 | item.size = 0 | |
1172 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1173 | ||
1174 | item.type = UInt32(TASK_CRASHINFO_CRASHED_THREADID) | |
1175 | item.flags = 0 | |
1176 | item.size = size | |
1177 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1178 | ||
1179 | var payload : UInt64 = 42 | |
1180 | buffer.append(&payload, length:Int(size)) | |
1181 | ||
1182 | item.type = KCDATA_TYPE_BUFFER_END | |
1183 | item.flags = 0 | |
1184 | item.size = 0 | |
1185 | buffer.append(&item, length: MemoryLayout<kcdata_item>.size) | |
1186 | ||
1187 | guard let dict = try? self.parseBuffer(buffer) | |
1188 | else { XCTFail(); return; } | |
1189 | ||
1190 | XCTAssert(dict.value(forKeyPath:"kcdata_crashinfo.crashed_threadid") == nil) | |
1191 | } | |
1192 | ||
1193 | func testReadThreadidWrongSize0() { | |
1194 | self.testReadThreadidWrongSize(size: 0) | |
1195 | } | |
1196 | ||
1197 | func testReadThreadidWrongSize7() { | |
1198 | self.testReadThreadidWrongSize(size: 7) | |
1199 | } | |
1200 | ||
1201 | func dataWithResource(_ name:String) -> NSData? { | |
1202 | guard let filename = Bundle(for: self.classForCoder).path(forResource: name, ofType: nil) | |
1203 | else { return nil } | |
1204 | return NSData(contentsOfFile:filename)! | |
1205 | } | |
1206 | ||
1207 | func testSampleStackshot(_ name : String) { | |
1208 | // check that we agree with sample file | |
1209 | ||
1210 | guard let sampledata = self.dataWithResource(name) | |
1211 | else { XCTFail(); return } | |
1212 | var dict : NSDictionary? | |
1213 | ||
1214 | dict = try? self.parseBuffer(sampledata) as NSDictionary | |
1215 | ||
1216 | if (dict == nil) { | |
1217 | if let decoded = NSData(base64Encoded: sampledata as Data, options:.ignoreUnknownCharacters) { | |
1218 | dict = try? self.parseBuffer(decoded) as NSDictionary | |
1219 | } | |
1220 | } | |
1221 | ||
1222 | if (dict == nil) { | |
1223 | if let decompressed = try? decompress(sampledata) { | |
1224 | dict = try? self.parseBuffer(decompressed) as NSDictionary | |
1225 | } | |
1226 | } | |
1227 | ||
1228 | if (dict == nil) { | |
1229 | XCTFail(); return; | |
1230 | } | |
1231 | ||
1232 | guard let plistdata = self.dataWithResource(name + ".plist.gz") ?? | |
1233 | self.dataWithResource(name + ".plist") | |
1234 | else {XCTFail(); return} | |
1235 | ||
1236 | var dict2 = try? PropertyListSerialization.propertyList(from: plistdata as Data, options: [], format: nil) | |
1237 | if dict2 == nil { | |
1238 | dict2 = try? PropertyListSerialization.propertyList(from:decompress(plistdata) as Data, options:[], format: nil) | |
1239 | } | |
1240 | ||
1241 | XCTAssert(dict2 != nil) | |
1242 | ||
1243 | XCTAssert(dict == dict2 as? NSDictionary) | |
1244 | ||
1245 | // check that we agree with python | |
1246 | ||
1247 | #if os(OSX) | |
1248 | ||
1249 | let kcdatapy = Bundle(for: self.classForCoder).path(forResource: "kcdata.py", ofType: nil) | |
1250 | ||
1251 | let task = Process() | |
1252 | task.launchPath = kcdatapy | |
1253 | task.arguments = ["-p", | |
1254 | Bundle(for:self.classForCoder).path(forResource: name, ofType: nil)!] | |
1255 | let pipe = Pipe() | |
1256 | task.standardOutput = pipe | |
1257 | task.launch() | |
1258 | ||
1259 | let data = pipe.fileHandleForReading.readDataToEndOfFile() | |
1260 | ||
1261 | guard let dict3 = try? PropertyListSerialization.propertyList(from:data, options:[], format: nil) as? NSDictionary | |
1262 | else { XCTFail(); return } | |
1263 | ||
1264 | XCTAssert(dict == dict3) | |
1265 | ||
1266 | #endif | |
1267 | } | |
1268 | ||
1269 | func testSampleStackshot() { | |
1270 | self.testSampleStackshot("stackshot-sample") | |
1271 | } | |
1272 | ||
1273 | func testSampleStackshotOldArrays() { | |
1274 | self.testSampleStackshot("stackshot-sample-old-arrays") | |
1275 | } | |
1276 | ||
1277 | func testSampleStackshotNewArrays() { | |
1278 | self.testSampleStackshot("stackshot-sample-new-arrays") | |
1279 | } | |
1280 | ||
1281 | func testSampleDeltaStackshotOldArrays() { | |
1282 | self.testSampleStackshot("delta-stackshot-sample-old-arrays") | |
1283 | } | |
1284 | ||
1285 | func testSampleDeltaStackshotNewArrays() { | |
1286 | self.testSampleStackshot("delta-stackshot-sample-new-arrays") | |
1287 | } | |
1288 | ||
1289 | func testSampleCorpse() { | |
1290 | self.testSampleStackshot("corpse-sample") | |
1291 | } | |
1292 | ||
1293 | func testSampleStackshotTailspin() { | |
1294 | self.testSampleStackshot("stackshot-sample-tailspin") | |
1295 | } | |
1296 | ||
1297 | func testSampleStackshotTailspin2() { | |
1298 | self.testSampleStackshot("stackshot-sample-tailspin-2") | |
1299 | } | |
1300 | ||
1301 | func testSampleExitReason() { | |
1302 | self.testSampleStackshot("exitreason-sample") | |
1303 | } | |
1304 | ||
1305 | func testSampleThreadT() { | |
1306 | self.testSampleStackshot("stackshot-sample-ths-thread-t") | |
1307 | } | |
1308 | ||
1309 | func testSampleCpuTimes() { | |
1310 | self.testSampleStackshot("stackshot-sample-cputime") | |
1311 | } | |
1312 | ||
1313 | func testSampleDuration() { | |
1314 | self.testSampleStackshot("stackshot-sample-duration") | |
1315 | } | |
1316 | ||
1317 | func testSampleNested() { | |
1318 | self.testSampleStackshot("nested-sample") | |
1319 | } | |
1320 | ||
1321 | func testSampleTermWithReason() { | |
1322 | self.testSampleStackshot("test-twr-sample") | |
1323 | } | |
1324 | ||
1325 | func testSampleCorpseTermWithReason() { | |
1326 | self.testSampleStackshot("corpse-twr-sample") | |
1327 | } | |
1328 | ||
1329 | func testSampleCorpseTermWithReasonV2() { | |
1330 | self.testSampleStackshot("corpse-twr-sample-v2") | |
1331 | } | |
1332 | ||
1333 | func testSampleCodesigningExitReason() { | |
1334 | self.testSampleStackshot("exitreason-codesigning") | |
1335 | } | |
1336 | ||
1337 | func testSampleThreadGroups() { | |
1338 | self.testSampleStackshot("stackshot-sample-thread-groups") | |
1339 | } | |
1340 | ||
1341 | func testSampleThreadGroupsFlags() { | |
1342 | self.testSampleStackshot("stackshot-sample-thread-groups-flags") | |
1343 | } | |
1344 | ||
1345 | func testSampleCoalitions() { | |
1346 | self.testSampleStackshot("stackshot-sample-coalitions") | |
1347 | } | |
1348 | ||
1349 | func testStackshotSharedcacheV2() { | |
1350 | self.testSampleStackshot("stackshot-sample-sharedcachev2") | |
1351 | } | |
1352 | ||
1353 | func testStackshotFaultStats() { | |
1354 | self.testSampleStackshot("stackshot-fault-stats") | |
1355 | } | |
1356 | ||
1357 | func testStackshotwithKCID() { | |
1358 | self.testSampleStackshot("stackshot-with-kcid") | |
1359 | } | |
1360 | ||
1361 | func testXNUPostTestConfig() { | |
1362 | self.testSampleStackshot("xnupost_testconfig-sample") | |
1363 | } | |
1364 | ||
1365 | func testStackshotWithWaitinfo() { | |
1366 | self.testSampleStackshot("stackshot-with-waitinfo") | |
1367 | } | |
1368 | ||
1369 | func testStackshotWithThreadPolicy() { | |
1370 | self.testSampleStackshot("stackshot-sample-thread-policy") | |
1371 | } | |
1372 | ||
1373 | func testStackshotWithInstrsCycles() { | |
1374 | self.testSampleStackshot("stackshot-sample-instrs-cycles") | |
1375 | } | |
1376 | ||
1377 | func testTrivial() { | |
1378 | } | |
1379 | } |