diff --git a/stdlib/public/Backtracing/ArrayImageSource.swift b/stdlib/public/Backtracing/ArrayImageSource.swift index db38033e1b789..e9266806cb243 100644 --- a/stdlib/public/Backtracing/ArrayImageSource.swift +++ b/stdlib/public/Backtracing/ArrayImageSource.swift @@ -16,8 +16,6 @@ import Swift -@_implementationOnly import OS.Libc - enum ArrayImageSourceError: Error { case outOfBoundsRead(UInt64, UInt64) } @@ -35,16 +33,16 @@ struct ArrayImageSource: ImageSource { return Bounds(base: 0, size: Size(array.count * MemoryLayout.stride)) } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { try array.withUnsafeBytes{ let size = Size($0.count) - let requested = Size(buffer.count * MemoryLayout.stride) + let requested = Size(buffer.count) if addr > size || requested > size - addr { throw ArrayImageSourceError.outOfBoundsRead(addr, requested) } - memcpy(buffer.baseAddress!, $0.baseAddress! + Int(addr), Int(requested)) + buffer.copyBytes(from: $0[Int(addr)..: MemoryReader { + private var reader: T + private var cache: [Address:UnsafeRawBufferPointer] + + public init(for reader: T) { + self.reader = reader + self.cache = [:] + } + + deinit { + for (_, page) in cache { + page.deallocate() + } + } + + private func getPage(at address: Address) throws -> UnsafeRawBufferPointer { + precondition((address & Address(pageMask)) == 0) + + if let page = cache[address] { + return page + } + + let page = UnsafeMutableRawBufferPointer.allocate(byteCount: pageSize, + alignment: pageSize) + try reader.fetch(from: address, into: page) + + let result = UnsafeRawBufferPointer(page) + + cache[address] = result + + return result + } + + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + guard buffer.count <= maxCachedSize else { + try reader.fetch(from: address, into: buffer) + return + } + + var pageAddress = address & ~Address(pageMask) + var done = 0 + var offset = Int(address - pageAddress) + var remaining = buffer.count + + while remaining > 0 { + let page = try getPage(at: pageAddress) + let maxBytes = pageSize - offset + let chunk = min(remaining, maxBytes) + + buffer[done..: ImageSource { } } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let toFetch = buffer.count * MemoryLayout.stride + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let toFetch = buffer.count if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } @@ -474,9 +474,9 @@ internal struct ElfGNUCompressedImageSource: ImageSource { uncompressedSize: uncompressedSize) } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let toFetch = buffer.count * MemoryLayout.stride + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let toFetch = buffer.count if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } @@ -509,9 +509,9 @@ internal struct LZMACompressedImageSource: ImageSource { dataBounds: bounds) } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let toFetch = buffer.count * MemoryLayout.stride + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let toFetch = buffer.count if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch { throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch) } diff --git a/stdlib/public/Backtracing/FileImageSource.swift b/stdlib/public/Backtracing/FileImageSource.swift index 94ed7b420f94d..2048252e8acd4 100644 --- a/stdlib/public/Backtracing/FileImageSource.swift +++ b/stdlib/public/Backtracing/FileImageSource.swift @@ -20,51 +20,54 @@ import Swift enum FileImageSourceError: Error { case posixError(Int32) - case truncatedRead + case outOfRangeRead } class FileImageSource: ImageSource { - private var fd: Int32 + private var _mapping: UnsafeRawBufferPointer public var isMappedImage: Bool { return false } private var _path: String public var path: String? { return _path } - public lazy var bounds: Bounds? = { - let size = lseek(fd, 0, SEEK_END) - if size < 0 { - return nil - } - return Bounds(base: 0, size: Size(size)) - }() + public var bounds: Bounds? { + return Bounds(base: 0, size: Size(_mapping.count)) + } public init(path: String) throws { _path = path - fd = _swift_open(path, O_RDONLY, 0) + let fd = _swift_open(path, O_RDONLY, 0) if fd < 0 { throw FileImageSourceError.posixError(_swift_get_errno()) } + defer { close(fd) } + let size = lseek(fd, 0, SEEK_END) + if size < 0 { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + let base = mmap(nil, Int(size), PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0) + if base == nil || base! == UnsafeRawPointer(bitPattern: -1)! { + throw FileImageSourceError.posixError(_swift_get_errno()) + } + _mapping = UnsafeRawBufferPointer(start: base, count: Int(size)) } deinit { - close(fd) + munmap(UnsafeMutableRawPointer(mutating: _mapping.baseAddress), + _mapping.count) } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - while true { - let size = MemoryLayout.stride * buffer.count - let result = pread(fd, buffer.baseAddress, size, off_t(addr)) - - if result < 0 { - throw FileImageSourceError.posixError(_swift_get_errno()) - } - - if result != size { - throw FileImageSourceError.truncatedRead - } - break + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let start = Int(addr) + guard _mapping.indices.contains(start) else { + throw FileImageSourceError.outOfRangeRead + } + let slice = _mapping[start...] + guard slice.count >= buffer.count else { + throw FileImageSourceError.outOfRangeRead } + buffer.copyBytes(from: slice[start..(into buffer: UnsafeMutableBufferPointer) throws { try source.fetch(from: pos, into: buffer) pos += UInt64(MemoryLayout.stride * buffer.count) @@ -138,9 +143,9 @@ struct SubImageSource: ImageSource { return parent.isMappedImage } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let toFetch = buffer.count * MemoryLayout.stride + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let toFetch = buffer.count if addr < 0 || addr > length { throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch) } diff --git a/stdlib/public/Backtracing/MemoryImageSource.swift b/stdlib/public/Backtracing/MemoryImageSource.swift index 29baa7d163cd8..c22bb27307ec3 100644 --- a/stdlib/public/Backtracing/MemoryImageSource.swift +++ b/stdlib/public/Backtracing/MemoryImageSource.swift @@ -28,8 +28,8 @@ class MemoryImageSource: ImageSource { self.reader = reader } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { try reader.fetch(from: addr, into: buffer) } } diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift index b63461a1a6e16..f8382c72aee47 100644 --- a/stdlib/public/Backtracing/MemoryReader.swift +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -30,6 +30,11 @@ import Swift typealias Address = UInt64 typealias Size = UInt64 + /// Fill the specified buffer with data from the specified location in + /// the source. + func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws + /// Fill the specified buffer with data from the specified location in /// the source. func fetch(from address: Address, @@ -51,6 +56,11 @@ import Swift extension MemoryReader { + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + try fetch(from: address, into: UnsafeMutableRawBufferPointer(buffer)) + } + public func fetch(from addr: Address, into pointer: UnsafeMutablePointer) throws { try fetch(from: addr, @@ -96,10 +106,12 @@ extension MemoryReader { @_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { public init() {} - public func fetch(from address: Address, - into buffer: UnsafeMutableBufferPointer) throws { - buffer.baseAddress!.update(from: UnsafePointer(bitPattern: UInt(address))!, - count: buffer.count) + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + buffer.baseAddress!.copyMemory( + from: UnsafeRawPointer(bitPattern: UInt(address))!, + byteCount: buffer.count + ) } } @@ -116,9 +128,9 @@ extension MemoryReader { self.task = task as! task_t } - public func fetch(from address: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let size = UInt64(MemoryLayout.stride * buffer.count) + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let size = buffer.count var sizeOut = UInt64(0) let result = mach_vm_read_overwrite(task, UInt64(address), @@ -138,8 +150,8 @@ extension MemoryReader { public typealias Address = UInt64 public typealias Size = UInt64 - public func fetch(from address: Address, - into buffer: UnsafeMutableBufferPointer) throws { + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { let reader = RemoteMemoryReader(task: mach_task_self()) return try reader.fetch(from: address, into: buffer) } @@ -221,34 +233,31 @@ extension MemoryReader { return response } - public func fetch(from addr: Address, - into buffer: UnsafeMutableBufferPointer) throws { - try buffer.withMemoryRebound(to: UInt8.self) { - let bytes = UnsafeMutableRawBufferPointer($0) - try sendRequest(for: Size(bytes.count), from: addr) + public func fetch(from addr: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + try sendRequest(for: Size(buffer.count), from: addr) - var done = 0 - while done < bytes.count { - let reply = try receiveReply() - - if reply.len < 0 { - throw MemserverError(message: "Unreadable at \(hex(addr))") - } + var done = 0 + while done < buffer.count { + let reply = try receiveReply() - if done + Int(reply.len) > bytes.count { - throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes") - } + if reply.len < 0 { + throw MemserverError(message: "Unreadable at \(hex(addr))") + } - let ret = try safeRead(fd, - UnsafeMutableRawBufferPointer( - rebasing: bytes[done..(from address: Address, - into buffer: UnsafeMutableBufferPointer) throws { - let size = size_t(MemoryLayout.stride * buffer.count) + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { + let size = buffer.count var fromIOVec = iovec(iov_base: UnsafeMutableRawPointer( bitPattern: UInt(address)), iov_len: size) @@ -281,8 +290,8 @@ extension MemoryReader { reader = RemoteMemoryReader(pid: getpid()) } - public func fetch(from address: Address, - into buffer: UnsafeMutableBufferPointer) throws { + public func fetch(from address: Address, + into buffer: UnsafeMutableRawBufferPointer) throws { return try reader.fetch(from: address, into: buffer) } } diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift index 7a5f033a45e0c..636979cd1b499 100644 --- a/stdlib/public/Backtracing/Utils.swift +++ b/stdlib/public/Backtracing/Utils.swift @@ -30,7 +30,7 @@ internal func hex(_ value: T, return "\(prefix)\(padding)\(digits)" } -internal func hex(_ bytes: [UInt8]) -> String { +internal func hex(_ bytes: some Sequence) -> String { return bytes.map{ hex($0, prefix: false) }.joined(separator: "") } diff --git a/stdlib/public/Backtracing/modules/OS/Libc.h b/stdlib/public/Backtracing/modules/OS/Libc.h index 17893a89c9da1..37ead15f19c01 100644 --- a/stdlib/public/Backtracing/modules/OS/Libc.h +++ b/stdlib/public/Backtracing/modules/OS/Libc.h @@ -21,6 +21,10 @@ #include #include +#if __has_include() +#include +#endif + #if __has_include() #include #endif diff --git a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift index f6dda1b37d895..f07b6914004cf 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetLinux.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetLinux.swift @@ -77,7 +77,7 @@ class Target { } } - var reader: MemserverMemoryReader + var reader: CachingMemoryReader // Get the name of a process private static func getProcessName(pid: pid_t) -> String { @@ -110,7 +110,7 @@ class Target { let memserverFd: CInt = 4 pid = getppid() - reader = MemserverMemoryReader(fd: memserverFd) + reader = CachingMemoryReader(for: MemserverMemoryReader(fd: memserverFd)) name = Self.getProcessName(pid: pid) let crashInfo: CrashInfo diff --git a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift index 32bbc7bc26683..a67e0fc0389df 100644 --- a/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift +++ b/stdlib/public/libexec/swift-backtrace/TargetMacOS.swift @@ -98,7 +98,7 @@ class Target { } } - var reader: RemoteMemoryReader + var reader: CachingMemoryReader var mcontext: MContext @@ -158,7 +158,7 @@ class Target { task = parentTask - reader = RemoteMemoryReader(task: task_t(task)) + reader = CachingMemoryReader(for: RemoteMemoryReader(task: task_t(task))) name = Self.getProcessName(pid: pid)