// // Tests.swift // // Some of these tests here verify that kdd is able to parse old // kcdata files and generate the correct output. To do so, we include // compressed versions of the raw kcdata and as well as the expected // plist output. // // NOTE: If you're adding sample data/plist files, you'll need to first // add them to the project and then make sure each is part of the // tests target. // // Other tests verify the expected behavior of libkdd for certain // situations. // // import XCTest import Foundation // Swift's bridging to uuid_t is awkward. func nsuuid2uuid_t(nsuuid : NSUUID) -> uuid_t { let dat = nsuuid2array(nsuuid) return nsarray2uuid(dat) } func nsarray2uuid(x : AnyObject) -> uuid_t { let a = x as! NSArray return uuid_t(UInt8(a[0] as! Int), UInt8(a[1] as! Int), UInt8(a[2] as! Int), UInt8(a[3] as! Int), UInt8(a[4] as! Int), UInt8(a[5] as! Int), UInt8(a[6] as! Int), UInt8(a[7] as! Int), UInt8(a[8] as! Int), UInt8(a[9] as! Int), UInt8(a[10] as! Int), UInt8(a[11] as! Int), UInt8(a[12] as! Int), UInt8(a[13] as! Int), UInt8(a[14] as! Int), UInt8(a[15] as! Int)) } func nsuuid2array(uuid : NSUUID) -> [Int] { var ret = [Int]() let ptr = UnsafeMutablePointer.alloc(16) defer { ptr.dealloc(16) } uuid.getUUIDBytes(ptr) for i in 0..<16 { ret.append(Int(ptr[i])) } return ret } func decompress(data:NSData) throws -> NSData { 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) let bufsize : Int = 1000 let buffer = UnsafeMutablePointer.alloc(bufsize) defer { buffer.dealloc(bufsize) } let output = NSMutableData() stream.next_out = buffer stream.avail_out = UInt32(bufsize) stream.next_in = UnsafeMutablePointer(data.bytes) stream.avail_in = UInt32(data.length) inflateInit2_(&stream, 16+MAX_WBITS, ZLIB_VERSION, Int32(sizeof(z_stream))) while (true) { let z = inflate(&stream, Z_NO_FLUSH); if (z == Z_OK || z == Z_STREAM_END) { output.appendBytes(buffer, length: bufsize - Int(stream.avail_out)) stream.avail_out = UInt32(bufsize) stream.next_out = buffer if (z == Z_STREAM_END) { return output; } } else { throw NSError(domain: "zlib", code: Int(z), userInfo: nil) } } } class Tests: XCTestCase { override func setUp() { super.setUp() continueAfterFailure = false } override func tearDown() { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } func parseBuffer(buffer:NSData) throws -> NSDictionary { var error : NSError? guard let dict = parseKCDataBuffer(UnsafeMutablePointer(buffer.bytes), UInt32(buffer.length), &error) else { XCTAssert(error != nil) throw error! } return dict } func testPaddingFlags(pad : Int) { let buffer = NSMutableData(capacity:1000)! var item = kcdata_item() item.type = KCDATA_BUFFER_BEGIN_CRASHINFO item.flags = 0 item.size = 0 buffer.appendBytes(&item, length: sizeof(kcdata_item)) item.type = UInt32(KCDATA_TYPE_LIBRARY_LOADINFO) item.flags = UInt64(pad) item.size = UInt32(sizeof(dyld_uuid_info_32)) buffer.appendBytes(&item, length: sizeof(kcdata_item)) let uuid = NSUUID(UUIDString: "de305d54-75b4-431b-adb2-eb6b9e546014")! var payload = dyld_uuid_info_32(imageLoadAddress: 42, imageUUID: nsuuid2uuid_t(uuid)) buffer.appendBytes(&payload, length:sizeof(dyld_uuid_info_32)) item.type = KCDATA_TYPE_BUFFER_END item.flags = 0 item.size = 0 buffer.appendBytes(&item, length: sizeof(kcdata_item)) guard let dict = try? self.parseBuffer(buffer) else { XCTFail(); return; } var uuidarray = nsuuid2array(uuid) for _ in 0.. NSData? { guard let filename = NSBundle(forClass: self.classForCoder).pathForResource(name, ofType: nil) else { return nil } return NSData(contentsOfFile:filename)! } func testSampleStackshot(name : String) { // check that we agree with sample file guard let sampledata = self.dataWithResource(name) else { XCTFail(); return } var dict : NSDictionary? dict = try? self.parseBuffer(sampledata) if (dict == nil) { if let decoded = NSData(base64EncodedData: sampledata, options:.IgnoreUnknownCharacters) { dict = try? self.parseBuffer(decoded) } } if (dict == nil) { if let decompressed = try? decompress(sampledata) { dict = try? self.parseBuffer(decompressed) } } if (dict == nil) { XCTFail(); return; } guard let plistdata = self.dataWithResource(name + ".plist.gz") ?? self.dataWithResource(name + ".plist") else {XCTFail(); return} var dict2 = try? NSPropertyListSerialization.propertyListWithData(plistdata, options: NSPropertyListReadOptions.Immutable, format: nil) if dict2 == nil { dict2 = try? NSPropertyListSerialization.propertyListWithData(decompress(plistdata), options: .Immutable, format: nil) } XCTAssert(dict2 != nil) XCTAssert(dict == dict2 as? NSDictionary) // check that we agree with python #if os(OSX) let kcdatapy = NSBundle(forClass: self.classForCoder).pathForResource("kcdata.py", ofType: nil) let task = NSTask() task.launchPath = kcdatapy task.arguments = ["-p", NSBundle(forClass:self.classForCoder).pathForResource(name, ofType: nil)!] let pipe = NSPipe() task.standardOutput = pipe task.launch() let data = pipe.fileHandleForReading.readDataToEndOfFile() guard let dict3 = try? NSPropertyListSerialization.propertyListWithData(data, options: .Immutable, format: nil) as? NSDictionary else { XCTFail(); return } XCTAssert(dict == dict3) #endif } func testSampleStackshot() { self.testSampleStackshot("stackshot-sample") } func testSampleStackshotOldArrays() { self.testSampleStackshot("stackshot-sample-old-arrays") } func testSampleStackshotNewArrays() { self.testSampleStackshot("stackshot-sample-new-arrays") } func testSampleDeltaStackshotOldArrays() { self.testSampleStackshot("delta-stackshot-sample-old-arrays") } func testSampleDeltaStackshotNewArrays() { self.testSampleStackshot("delta-stackshot-sample-new-arrays") } func testSampleCorpse() { self.testSampleStackshot("corpse-sample") } func testSampleStackshotTailspin() { self.testSampleStackshot("stackshot-sample-tailspin") } func testSampleStackshotTailspin2() { self.testSampleStackshot("stackshot-sample-tailspin-2") } func testSampleExitReason() { self.testSampleStackshot("exitreason-sample") } func testSampleThreadT() { self.testSampleStackshot("stackshot-sample-ths-thread-t") } func testSampleCpuTimes() { self.testSampleStackshot("stackshot-sample-cputime") } func testSampleDuration() { self.testSampleStackshot("stackshot-sample-duration") } func testSampleNested() { self.testSampleStackshot("nested-sample") } func testSampleTermWithReason() { self.testSampleStackshot("test-twr-sample") } func testSampleCorpseTermWithReason() { self.testSampleStackshot("corpse-twr-sample") } func testSampleCorpseTermWithReasonV2() { self.testSampleStackshot("corpse-twr-sample-v2") } func testSampleCodesigningExitReason() { self.testSampleStackshot("exitreason-codesigning") } func testStackshotSharedcacheV2() { self.testSampleStackshot("stackshot-sample-sharedcachev2") } func testStackshotFaultStats() { self.testSampleStackshot("stackshot-fault-stats") } func testStackshotwithKCID() { self.testSampleStackshot("stackshot-with-kcid") } func testXNUPostTestConfig() { self.testSampleStackshot("xnupost_testconfig-sample") } func testStackshotWithWaitinfo() { self.testSampleStackshot("stackshot-with-waitinfo") } func testTrivial() { } }