What does code 9 signify ( and how to fix the issue ) when Xcode crashes during the execution of WatchOS tests?
Scenario:
I have an XCTestCase that reads around 100 CSV test resource files. These files are comma-delimited, have approximately 6,000 lines, and have an average size of 64K. During my test case, I read these files into memory, and one my one, I verify that the input file ( after some processing ) matches the output file. Here is some code to demonstrate the flow:
import XCTest
#testable import MyWatch_Extension
class MyTest: XCTestCase {
func testMyAlgo() throws {
let testingData = discoverAvailableTestFiles(filter: "dataset_1");
XCTAssertGreaterThan(testingData.count, 0, "have data to process")
for (_, testingEntry) in testingData.enumerated() {
var input : [Double] = [];
var expectations : [Double] = [];
readInputOutputTestData(entries: &input, fileName: testingEntry + "_input");
readInputOutputTestData(entries: &expectations, fileName: testingEntry + "_expected_output");
// do something with the input, and store it into results
let results = MyAglo().doSomething();
compareResultsAndExpectations(testingEntry: testingEntry, results: results, expectations: expectations);
}
}
func discoverAvailableTestFiles(filter: String) -> Set<String> {
let bundle = Bundle(for: type(of: self))
let paths = bundle.paths(forResourcesOfType: "csv", inDirectory: nil);
var results = Set<String>()
for path in paths {
if (path.contains(filter)) {
let fileNameSubstring = path[path.index(path.lastIndex(of: "/")!, offsetBy: 1)...]
let qaFileName = fileNameSubstring[...fileNameSubstring.index(fileNameSubstring.lastIndex(of: "_")!, offsetBy: -1)]
results.insert(String(qaFileName))
}
}
return results;
}
func readInputOutputTestData(entries : inout [Double], fileName : String) {
let bundle = Bundle(for: type(of: self))
let path = bundle.path(forResource: fileName, ofType: "csv")!
do {
let data = try String(contentsOfFile: path, encoding: .utf8)
let myStrings = data.components(separatedBy: .newlines);
for idx in 0..<myStrings.count {
let parts = myStrings[idx].split(separator: ",");
if (parts.count > 0) {
for part in parts {
entries.append((part as NSString).doubleValue);
}
}
}
} catch {
print(error)
}
}
func compareResultsAndExpectations(testingEntry: String, results: [Double], expectations: [Double]) {
print("## testing \(testingEntry)");
XCTAssertEqual(results.count, expectations.count / 3, "mismatch in data count \(testingEntry)")
var counter = 0;
for idx in stride(from: 0, to: expectations.count, by: 3) {
XCTAssertEqual(results[counter], expectations[idx], accuracy: 0.5, "\(idx + 1) value mismatch")
counter += 1;
}
}
}
When I execute the testMyAlgo testcase, I might read the first 20 files, and I get the error message :
The test runner exited with code 9 before finishing running tests.
If I run each file individually or in smaller batches ( maybe only 20 of them, as opposed to the entire loop of 100 ), everything is fine. This leads me to believe that I am exhausting the memory space of the watch or should be executing the test case differently. Any idea what the problem is, or perhaps, how I should re-structure the test case to get away from this error? ( Maybe free resources or something similar before each test case )
You can create a dumb Xcode project that runs on an iPhone and copy/paste only this test to it. If it runs OK, that means you're exceeding the limits on the watch. If that's the case, you can create a framework with your algorithm and run the tests on the framework, and import the framework on your watch extension
Related
I am writing an app that contains a small benchmark for I/O operations.
For write operations, I am using a 'FileHandle' which works pretty well. I am testing my old USB stick and my calculation results in values of roughly 20MB/s which seems correct.
However, when reading, the values jump up to 8 GB/s. Although I would love to have an USB stick that fast...I think this has to do with some sort of cacheing.
Here is the code that I am using (some bits were removed):
guard let handle = FileHandle(forUpdatingAtPath: url.path) else { return }
let data = Data(repeating: 0, count: 2 * 1024 * 1024)
var startTime = Date.timestamp
// Write Test
while Date.timestamp - startTime < 5.0
{
handle.write(data)
try? handle.synchronize()
// ...
}
// Go back to beginning of file.
try? handle.seek(toOffset: 0)
// Remove everything at the end of the file
try? handle.truncate(atOffset: blockSize)
startTime = Date.timestamp
// Read Test
while Date.timestamp - startTime < 5.0
{
autoreleasepool
{
if let handle = try? FileHandle(forReadingFrom: fileUrl), let data = try? handle.readToEnd()
{
let count = UInt64(data.count)
self.readData += count
self.totalReadData += count
handle.close()
}
// I also tried FileManager.default.contents(atPath: ) - same result
}
}
I also tried this piece of code (it's either from Martin R. here on SO or from Quinn on the Apple forums):
let fd = open(fileUrl.path, O_RDONLY)
_ = fcntl(fd, F_NOCACHE, 1)
var buffer = Data(count: 1024 * 1024)
buffer.withUnsafeMutableBytes { ptr in
let amount = read(fd, ptr.baseAddress, ptr.count)
self.readData += UInt64(amount)
self.totalReadData += UInt64(amount)
}
close(fd)
The code itself works...but there is still cacheing.
TL;DR How can I disable cacheing when writing to and reading from a file using Swift?
Regards
I implemented a password generator script in Swift which utilizes Process() to execute Mac OS X command line tasks. The passwords themselves are just random Strings which then are encrypted (bcrypt) by the command line task as follows:
/usr/sbin/htpasswd -bnBC 10 '' this_is_the_password | /usr/bin/tr -d ':\n'
Also multiple threads are used to generate passwords with their hashes in parallel.
Note: Both the multithreading and the command line task (compared to serveral other native Swift libraries I tried) improved performance in terms of execution time drastically.
The Problem
The Programm runs fine for the first ~3148 rounds and always crashes around this number (problably correlated to the number of threads running).
For example, if I configure 2000 passwords the code executes as expected terminates without any errors.
Common Error Messages
Setting a breakpoint in Process+Pipe.swift in the catch block of the execute(...) function at BREAKPOINT_1 results in:
Thread 1: signal SIGCHLD
po error.localizedDescription
"The operation couldn\\U2019t be completed. (NSPOSIXErrorDomain error 9 - Bad file descriptor)"
When uncommenting the four //return self.hash(string, cost: cost) code snippets to ignore the error the following errors finally crash the execution (again in execute(...), but not necessarily in the catch block):
Program stops ...
Thread 32: EXC_BAD_ACCESS (code=2, address=0x700003e6bfd4)
... on manual continue ...
Thread 2: EXC_BAD_ACCESS (code=2, address=0x700007e85fd4)
po process
error: Trying to put the stack in unreadable memory at: 0x700003e6bf40.
Code
The relevant code componets are the Main.swift which initialized and starts (and later stops) the PasswordGenerator and then loops n times to get passwords via nextPassword() from the PasswordGenerator. The PasswordGenerator itself utilized execute(...) from the Process extension to run commandline tasks which generate the hash.
Main.swift
class Main {
private static func generate(...) {
...
PasswordGenerator.start()
for _ in 0..<n {
let nextPassword = PasswordGenerator.nextPassword()
let readablePassword = nextPassword.readable
let password = nextPassword.hash
...
}
PasswordGenerator.stop()
...
}
}
PasswordGenerator.swift
The PasswordGenerator runs multiple Threads in parallel.
nextPassword() tries to get a password (if there is one in the passwords Array) or else waits for 100 seconds.
struct PasswordGenerator {
typealias Password = (readable: String, hash: String)
private static let semaphore = DispatchSemaphore(value: 1)
private static var active = false
private static var passwords: [Password] = []
static func nextPassword() -> Password {
self.semaphore.wait()
if let password = self.passwords.popLast() {
self.semaphore.signal()
return password
} else {
self.semaphore.signal()
sleep(100)
return self.nextPassword()
}
}
static func start(
numberOfWorkers: UInt = 32,
passwordLength: UInt = 10,
cost: UInt = 10
) {
self.active = true
for id in 0..<numberOfWorkers {
self.runWorker(id: id, passwordLength: passwordLength, cost: cost)
}
}
static func stop() {
self.semaphore.wait()
self.active = false
self.semaphore.signal()
}
private static func runWorker(
id: UInt,
passwordLength: UInt = 10,
cost: UInt = 10
) {
DispatchQueue.global().async {
var active = true
repeat {
// Update active.
self.semaphore.wait()
active = self.active
print("numberOfPasswords: \(self.passwords.count)")
self.semaphore.signal()
// Generate Password.
// Important: The bycrypt(cost: ...) step must be done outside the Semaphore!
let readable = String.random(length: Int(passwordLength))
let password = Password(readable: readable, hash: Encryption.hash(readable, cost: cost))
// Add Password.
self.semaphore.wait()
self.passwords.append(password)
self.semaphore.signal()
} while active
}
}
}
Encryption.swift
struct Encryption {
static func hash(_ string: String, cost: UInt = 10) -> String {
// /usr/sbin/htpasswd -bnBC 10 '' this_is_the_password | /usr/bin/tr -d ':\n'
let command = "/usr/sbin/htpasswd"
let arguments: [String] = "-bnBC \(cost) '' \(string)".split(separator: " ").map(String.init)
let result1 = Process.execute(
command: command,//"/usr/sbin/htpasswd",
arguments: arguments//["-bnBC", "\(cost)", "''", string]
)
let errorString1 = String(
data: result1?.error?.fileHandleForReading.readDataToEndOfFile() ?? Data(),
encoding: String.Encoding.utf8
) ?? ""
guard errorString1.isEmpty else {
// return self.hash(string, cost: cost)
fatalError("Error: Command \(command) \(arguments.joined(separator: " ")) failed with error: \(errorString1)")
}
guard let output1 = result1?.output else {
// return self.hash(string, cost: cost)
fatalError("Error: Command \(command) \(arguments.joined(separator: " ")) failed! No output.")
}
let command2 = "/usr/bin/tr"
let arguments2: [String] = "-d ':\n'".split(separator: " ").map(String.init)
let result2 = Process.execute(
command: command2,
arguments: arguments2,
standardInput: output1
)
let errorString2 = String(
data: result2?.error?.fileHandleForReading.readDataToEndOfFile() ?? Data(),
encoding: String.Encoding.utf8
) ?? ""
guard errorString2.isEmpty else {
// return self.hash(string, cost: cost)
fatalError("Error: Command \(command) \(arguments.joined(separator: " ")) failed with error: \(errorString2)")
}
guard let output2 = result2?.output else {
// return self.hash(string, cost: cost)
fatalError("Error: Command \(command) \(arguments.joined(separator: " ")) failed! No output.")
}
guard
let hash = String(
data: output2.fileHandleForReading.readDataToEndOfFile(),
encoding: String.Encoding.utf8
)?.replacingOccurrences(of: "$2y$", with: "$2a$")
else {
fatalError("Hash: String replacement failed!")
}
return hash
}
}
Process+Pipe.swift
extension Process {
static func execute(
command: String,
arguments: [String] = [],
standardInput: Any? = nil
) -> (output: Pipe?, error: Pipe?)? {
let process = Process()
process.executableURL = URL(fileURLWithPath: command)
process.arguments = arguments
let outputPipe = Pipe()
let errorPipe = Pipe()
process.standardOutput = outputPipe
process.standardError = errorPipe
if let standardInput = standardInput {
process.standardInput = standardInput
}
do {
try process.run()
} catch {
print(error.localizedDescription)
// BREAKPOINT_1
return nil
}
process.waitUntilExit()
return (output: outputPipe, error: errorPipe)
}
}
Question(s)
Why does the program crash?
Why does it not crash for also huge numbers like 2000 passwords?
Is the multithreading implemented correct?
Is there a problem in the execute(...) code?
I have found a fix while researching this bug. It seems that, despite what the documentation claims, Pipe will not automatically close its reading filehandle.
So if you add a try outputPipe.fileHandleForReading.close() after reading from it, that will fix the issue.
It seems this is a Swift bug. I did some testing and could reproduce it by just running a lot of Process.run(). Filed an issue against Swift:
https://bugs.swift.org/browse/SR-15522
I have these two functions:
func search_DocumentMultimedia(documentId: Int) -> Array<Int>{
var array:[Int]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("DocumentMultimedia")
let documentMultimediaIdField = Expression<Int>("DocumentMultimediaId")
let documentIdField = Expression<Int>("DocumentId")
for media in try db.prepare(documentMultimediaTable.select(documentMultimediaIdField, documentIdField).where(documentIdField == documentId)) {
array.append(media[documentMultimediaIdField])
}
} catch{
print(error)
}
return array
}
func search_Multimedia(multimediaID: [Int]) -> Array<mediaData>{
var array:[mediaData]
let downloadUrl = FileManager.default.urls(for:.downloadsDirectory, in: .userDomainMask)[0]
let path = downloadUrl.appendingPathComponent("mwb_" + language + "_" + issue + ".db")
ForEach(0..<multimediaID.count, id: \.self) { index in
do{
let db = try Connection(path.absoluteString)
let documentMultimediaTable = Table("Multimedia")
let MultimediaIdField = Expression<Int>("MultimediaId")
let keySymbol = Expression<String>("KeySymbol")
let track = Expression<Int>("Track")
let issueTagNumber = Expression<Int>("IssueTagNumber")
let mimeType = Expression<String>("MimeType")
let filePath = Expression<String>("FilePath")
for media in try db.prepare(documentMultimediaTable.select(MultimediaIdField, keySymbol, track, issueTagNumber, mimeType, filePath).where(MultimediaIdField == multimediaID[index])) {
let data = mediaData(mediaID: media[MultimediaIdField], mediaType: media[keySymbol], track: media[track], issueTagNumber: media[issueTagNumber], mimeType: media[mimeType], filePath: media[filePath])
array.append(data)
}
} catch{
print(error)
}
}
return array
}
I call them like this:
func getMedia(documentId: Int, nextDocumentId: Int) /*-> Array<videoData>*/ {
let multimediaId:[Int] = JW.search_DocumentMultimedia(documentId: documentId)
let mediaData:[mediaData] = JW.search_Multimedia(multimediaID: multimediaId)
print(mediaData)
}
In search_Multimedia I keep getting an error that says 'Ambiguous reference to member 'count''. I get this error on ForEach statement that uses multimediaID.count. I have tried everything but can't find how to resolve this. Please could you lend a hand? I saw a similar question on here but it seems to be outdated - hence my post.
You are using ForEach where ForEach is not appropriate. The purpose of ForEach is to turn a collection of values into a collection of SwiftUI Views inside the body of some collection view like a List or a VStack.
You don't need to create a collection of Views here. Use a regular Swift for/in statement. Replace this:
ForEach(0..<multimediaID.count, id: \.self) { index in
with this:
for index in 0 ..< multimediaID.count {
I think you have other errors, but this one is particularly important. ForEach (like most SwiftUI types) relies heavily on type inference. Mistakes in a ForEach call can seriously confuse the Swift compiler's type inference implementation. Then the compiler often prints useless or misleading error messages. So if you replace your ForEach with for/in loop, the compiler will probably give you better messages about any other errors you have made in this code.
I need to collect a lot of data about picture and movie files in a directory (or a directory tree). This includes file name, date of creation and modification, filesize (and maybe later on additional data like size of picture - if available - which is not covered here). There are 10.000s of them. Wenn requesting 17.000 files the programm needs 10 min for doing this task.
I'm doing it with the following code, but is there a way to speed it up significantly?
This is the code I use:
// get files matching extensions
func getPicFilenames(_ exts: [String]? = nil, includeFolders: Bool = false, skipHiddenFiles: Bool = true)
-> Array<URL>
{
// all to upper once (and drop optional dot)
let extUpper = exts == nil ?
[String]() :
exts!.map { ($0.hasPrefix(".") ? String($0.dropFirst(1)) : $0).uppercased() }
// let resourceKeys = [URLResourceKey.nameKey, URLResourceKey.isDirectoryKey]
// we also need size of files
let resourceKeys = [ URLResourceKey.nameKey, URLResourceKey.isDirectoryKey,
URLResourceKey.creationDateKey, URLResourceKey.contentModificationDateKey,
URLResourceKey.attributeModificationDateKey, URLResourceKey.fileSizeKey]
var fileURLs: [URL] = []
let directoryEnumerator = FileManager().enumerator(at: self, includingPropertiesForKeys: resourceKeys,
options: skipHiddenFiles ? [.skipsHiddenFiles] : [], errorHandler: nil)!
for case let fileURL as NSURL in directoryEnumerator {
guard let resourceValues = try? fileURL.resourceValues(forKeys: resourceKeys),
let isDirectory = resourceValues[URLResourceKey.isDirectoryKey] as? Bool
else {
continue
}
// we can skip complete descendants! >> directoryEnumerator.skipDescendants()
if !isDirectory || (isDirectory && includeFolders) {
if let ext = fileURL.pathExtension {
// matching extension? (ignore case!)
if extUpper.count == 0 || extUpper.contains((ext.uppercased())) {
fileURLs.append( fileURL.absoluteURL! )
}
}
}
}
return fileURLs
}
This needs 1 min per 1.700 files. Wenn analyzing 100.000s files it should be much faster (if possible).
I'm using Swift 4.2 on XCode 10.1 with OSX 10.11.6.
I have a simple piece of code that I guess I'm using local and global variables in it. But, I have a hard time understanding what's going wrong in here. I am setting "var hhhh:Int = 0" at first. Then, inside the if statement, I set "hhhh = appleCount["count"] as! Int". Since appleCount["count"] is not zero and has some value, hhhh gets its' value (I tried that uisng a print statement and hhhh is not zero inside if statement), but, later when I print hhhh with print("(hhhh)") outside if, I again get zero for its' value. Does it have something to do with local and global variables? I'm trying to communicate with Parse in the code by the way.
Thanks a lot for your kind help
import UIKit
import Parse
class NewsPageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad(
var hhhh:Int = 0
var tttt:Int = 0
var cccc:Int = 1
if cccc == 1 {
var query = PFQuery(className: "Count")
query.getObjectInBackgroundWithId("RhC25gVjZm", block: { (object: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let appleCount = object {
appleCount["count"] = appleCount["count"] as! Int + 1
hhhh = appleCount["count"] as! Int
appleCount.saveInBackground()
}
})
}
print(hhhh)
}
}
It does not have to do with local and global variables. It has to do with background threads. The code in brackets {} after the parameter label "block" will run in a background thread at a later time.
Your print(hhhh) is running before the block has had a chance to change hhhh. Move the print statement back inside the block so you can see the variable being set.
osteven response helped me a lot understanding the problem. Thanks a lot man. In addition to osteven's response, I just waned to add that a major part of my problem was coming because I was trying to do some mathematical operations on the objects I was trying to save in Parse. So, I also figured that I could create an array, save my objects inside that array, and then access the key and update the values. Here is a sample code of what I am using right now. It does some mathematical operation on two different objects saved in Parse and updates the label's text on screen. For accessing the two objects in Parse and updating them I'm using an array.
Hope the answers here will help someone in future as the awesome people of StackOverFlow are helping me now.
Peace!
var hhhh : [Int] = []
#IBOutlet weak var jPercent: UILabel!
#IBOutlet weak var yPercent: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className: "Count")
if cccc == 1 {
query.getObjectInBackgroundWithId("DcU9tdRdnl", block: { (object: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let jCount = object {
jCount["count"] = jCount["count"] as! Int + 1
jCount.saveInBackground()
}
})
} else if cccc == 2 {
query.getObjectInBackgroundWithId("5Bq4HJbFa3", block: { (object: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let yCount = object {
yCount["count"] = yCount["count"] as! Int + 1
yCount.saveInBackground()
}
})
}
//shouldn't use same query for findObjectsInBackgroundWithBlock and getObjectInBackgroundWithId otherwise you'll get a runtime error
var query2 = PFQuery(className: "Count")
query2.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if let users = objects {
for object in users {
if let user = object["count"] as? Int {
self.hhhh.append(user)
}
}
}
var gggg = 100*Float(self.hhhh[0])/(Float(self.hhhh[0]+self.hhhh[1]))
self.yPercent.text = String(format: "%.1f", gggg) + "%"
self.jPercent.text = String(format: "%.1f", 100 - gggg) + "%"
print(self.hhhh[0])
}
}