String(contentsOfFile:encoding:) appears to hang on Linux when reading certain types of files. Famously in Unix 'everything is a file', so should I expect to be able to use the method on anything that can be 'less'ed on Linux?
e.g. on a RaspberryPi 3B+ (Raspian Stretch) and with Swift 4.2 the following code in case 1 works, and in case 2 hangs without throwing.
import Foundation
print("Test of getContentsOfFile:")
// Normal Text File (case 1)
var testFilePath = "/home/pi/swift/hello.txt"
do {
let fileText = try String(contentsOfFile: testFilePath, encoding: .utf8)
print("fileText: \(fileText)")
} catch {
print("could not read contents of file...")
}
// Linux Special 'File' (case 2)
testFilePath = "/sys/devices/w1_bus_master1/w1_master_name"
do {
let fileText = try String(contentsOfFile: testFilePath, encoding: .utf8)
print("fileText: \(fileText)")
} catch {
print("could not read contents of file...")
}
Again - apologies for being an idiot - but assume that I have very limited knowledge of command-line debugging. My experience is of dragging lovely blue arrows in Xcode. I have a rather perverse inverted knowledge of coding in a top-down way, knowing little about the fundamentals and more about composing Foundation/AppKit objects.
I think that pstack is, alas, not available on the architecture of the Pi (Armv7)
An alternative is to run the process in gdb or lldb, wait until it hangs, send it a signal for which the debugger will stop (say, hit ^C), then use the bt command.
Now I feel very stupid. That's not going to be of any use as I'm using a pre-compiled swift binary, from which I assume that all debugging info has been stripped.
Working out how to compile my own version of foundation and use that in place is way beyond the scope of this thread, so I think I'll bow out now and try and learn more outside this forum...
@Diggory can you run strace -p PID_OF_YOUR_PROGRAM when it's stuck? it'll probably print something like [read unfinished...] or something. You can then immediately press Ctrl+C and just paste the output here.
I assume it's stuck in some system call and the strace should tell you which one.
@millenomi suggested using lldb which should totally work too. Given that it was unable to symbolicate anything I assume you tried using the system's lldb (under /usr/bin/lldb) instead of the one that comes with Swift? There should be a lldb binary next to where your swift and swiftc binaries live. So if you Swift binary is /usr/local/bin/swift, there should be a /usr/local/bin/lldb too and that should work better. I realise you're on ARM though and I don't know how well Swift's lldb is tested under (Linux on) ARM so strace might be enough in this case.
But if you can't get the lldb that ships with Swift to work on ARM, please do file a bug on bugs.swift.org too.
By the way, I did some very caveman-like debugging a few days ago and looked through the source of String / URL and Data and worked through what functions would be called by String's contentsOfFile:encoding: method. I think that the hang takes place in Data's init(contentsOf:) function.
@Diggory ok, I just the code that does the file reading and there are quite a number of bugs there... The bugs there also explain your issue. The code in question relies on two things that both are not true:
files don't shrink
one can always read st_size bytes from any file
How does this code rely on those untrue conditions:
var remaining = Int(info.st_size)
var total = 0
while remaining > 0 {
let amt = read(fd, data.advanced(by: total), remaining)
if amt < 0 {
break
}
remaining -= amt
total += amt
}
This will spin in a loop until it has read remaining bytes in total which might never happen: For one the file might have shrunk between that fstat and the read calls and on top of that some pseudo-files in Linux (especially in /sys) return a large st_size and you can't actually read any bytes. For example on my machine: