Diggory
(Diggory)
1
Hello,
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...")
}
millenomi
(Aura Lily Vulcano)
2
Can you append a backtrace (via sample $PID) to a bug at https://bugs.swift.org/?
Diggory
(Diggory)
3
Sorry - I'm very much an amateur at this. I've done some Obj-C development on MacOS, but I'm rather new to Linux.
Isn't 'sample' a MacOS/BSD tool? This is an issue on Swift on Linux.
I don't seem to have sample available via the OS or via apt-get.
millenomi
(Aura Lily Vulcano)
4
Welp, you are correct and I was distracted. I meant pstack $PID.
Diggory
(Diggory)
5
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)
millenomi
(Aura Lily Vulcano)
6
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.
Diggory
(Diggory)
7
Thanks, I'll do some reading and see what I can generate..
Diggory
(Diggory)
8
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...
Thanks.
GetSensorName
path: /sys/bus/w1/devices/28-031590d3d1ff
nameFilePath: /sys/bus/w1/devices/28-031590d3d1ff/name
ContentsOfFile:
Process 833 stopped
* thread #1, name = 'OneWireTemperat', stop reason = signal SIGSTOP
frame #0: 0x7610c39c
-> 0x7610c39c: ldr r7, [sp], #4
0x7610c3a0: cmn r0, #4096
0x7610c3a4: bxlo lr
0x7610c3a8: b 0x7610c3e8
Target 0: (No executable module.) stopped.
(lldb) bt
* thread #1, name = 'OneWireTemperat', stop reason = signal SIGSTOP
* frame #0: 0x7610c39c
frame #1: 0x76a47af4
frame #2: 0x76a3eb80
frame #3: 0x76a3ead4
frame #4: 0x76c884d4
frame #5: 0x76c89478
frame #6: 0x76c88b2c
frame #7: 0x76c97e54
frame #8: 0x004053d4
frame #9: 0x00404d1c
frame #10: 0x00406fc8
frame #11: 0x00402308
(lldb) quit
@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.
Diggory
(Diggory)
10
Lots of this: I'll see what I can do with the swift version of lldb.
read(5, "", 4080) = 0
read(5, "", 4080) = 0
read(5, "", 4080) = 0
Diggory
(Diggory)
11
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:
$ ls -la /sys/devices/pnp0/00\:00/uevent
-rw-r--r-- 1 root root 4096 Oct 15 13:54 /sys/devices/pnp0/00:00/uevent
$ stat /sys/devices/pnp0/00\:00/uevent
File: '/sys/devices/pnp0/00:00/uevent'
Size: 4096 Blocks: 0 IO Block: 4096 regular file
Device: 77h/119d Inode: 2450 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2018-10-15 14:01:25.211472722 +0000
Modify: 2018-10-15 14:01:25.211472722 +0000
Change: 2018-10-15 14:01:25.211472722 +0000
Birth: -
so the file has st_size = 4096 but
$ cat /sys/devices/pnp0/00\:00/uevent | wc -c
0
so we can't actually read anything from it... Testing this with Foundation shows this problem nicely
1> import Foundation
2> String(contentsOfFile: "/sys/devices/pnp0/00:00/uevent")
[... hangs forever ...]
which is exactly the problem you're hitting. I filed a bug.
4 Likes