Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions stdlib/public/Backtracing/ArrayImageSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@

import Swift

@_implementationOnly import OS.Libc

enum ArrayImageSourceError: Error {
case outOfBoundsRead(UInt64, UInt64)
}
Expand All @@ -35,16 +33,16 @@ struct ArrayImageSource<T>: ImageSource {
return Bounds(base: 0, size: Size(array.count * MemoryLayout<T>.stride))
}

public func fetch<U>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<U>) 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<U>.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)..<Int(addr+requested)])
}
}
}
1 change: 1 addition & 0 deletions stdlib/public/Backtracing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ set(BACKTRACING_SOURCES
Backtrace.swift
BacktraceFormatter.swift
ByteSwapping.swift
CachingMemoryReader.swift
Context.swift
Compression.swift
CoreSymbolication.swift
Expand Down
86 changes: 86 additions & 0 deletions stdlib/public/Backtracing/CachingMemoryReader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//===--- CachingMemoryReader.swift ----------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Wraps a MemoryReader in a layer that caches memory pages.
//
//===----------------------------------------------------------------------===//

import Swift

// The size of the pages in the page cache (must be a power of 2)
fileprivate let pageSize = 4096

fileprivate let pageMask = pageSize - 1

// The largest chunk we will try to cache data for
fileprivate let maxCachedSize = pageSize * 8

@_spi(MemoryReaders)
public class CachingMemoryReader<T: MemoryReader>: 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..<done+chunk].copyBytes(from: page[offset..<offset+chunk])

offset = 0
done += chunk
remaining -= chunk
pageAddress += Address(pageSize)
}
}
}
18 changes: 9 additions & 9 deletions stdlib/public/Backtracing/Compression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -427,9 +427,9 @@ internal struct ElfCompressedImageSource<Traits: ElfTraits>: ImageSource {
}
}

public func fetch<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
let toFetch = buffer.count * MemoryLayout<T>.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)
}
Expand Down Expand Up @@ -474,9 +474,9 @@ internal struct ElfGNUCompressedImageSource: ImageSource {
uncompressedSize: uncompressedSize)
}

public func fetch<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
let toFetch = buffer.count * MemoryLayout<T>.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)
}
Expand Down Expand Up @@ -509,9 +509,9 @@ internal struct LZMACompressedImageSource: ImageSource {
dataBounds: bounds)
}

public func fetch<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
let toFetch = buffer.count * MemoryLayout<T>.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)
}
Expand Down
53 changes: 28 additions & 25 deletions stdlib/public/Backtracing/FileImageSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
while true {
let size = MemoryLayout<T>.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..<start+buffer.count])
}
}
11 changes: 8 additions & 3 deletions stdlib/public/Backtracing/ImageSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ struct ImageSourceCursor {
self.pos = offset
}

public mutating func read(into buffer: UnsafeMutableRawBufferPointer) throws {
try source.fetch(from: pos, into: buffer)
pos += UInt64(buffer.count)
}

public mutating func read<T>(into buffer: UnsafeMutableBufferPointer<T>) throws {
try source.fetch(from: pos, into: buffer)
pos += UInt64(MemoryLayout<T>.stride * buffer.count)
Expand Down Expand Up @@ -138,9 +143,9 @@ struct SubImageSource<S: ImageSource>: ImageSource {
return parent.isMappedImage
}

public func fetch<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
let toFetch = buffer.count * MemoryLayout<T>.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)
}
Expand Down
4 changes: 2 additions & 2 deletions stdlib/public/Backtracing/MemoryImageSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class MemoryImageSource<M: MemoryReader>: ImageSource {
self.reader = reader
}

public func fetch<T>(from addr: Address,
into buffer: UnsafeMutableBufferPointer<T>) throws {
public func fetch(from addr: Address,
into buffer: UnsafeMutableRawBufferPointer) throws {
try reader.fetch(from: addr, into: buffer)
}
}
Loading