Skip to content

Commit aa9f129

Browse files
authored
Merge pull request #69510 from al45tair/eng/PR-117681625
[Backtracing] Add a caching wrapper for MemoryReader.
2 parents 3eea9d6 + b50b396 commit aa9f129

File tree

11 files changed

+169
-72
lines changed

11 files changed

+169
-72
lines changed

stdlib/public/Backtracing/ArrayImageSource.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@
1616

1717
import Swift
1818

19-
@_implementationOnly import OS.Libc
20-
2119
enum ArrayImageSourceError: Error {
2220
case outOfBoundsRead(UInt64, UInt64)
2321
}
@@ -35,16 +33,16 @@ struct ArrayImageSource<T>: ImageSource {
3533
return Bounds(base: 0, size: Size(array.count * MemoryLayout<T>.stride))
3634
}
3735

38-
public func fetch<U>(from addr: Address,
39-
into buffer: UnsafeMutableBufferPointer<U>) throws {
36+
public func fetch(from addr: Address,
37+
into buffer: UnsafeMutableRawBufferPointer) throws {
4038
try array.withUnsafeBytes{
4139
let size = Size($0.count)
42-
let requested = Size(buffer.count * MemoryLayout<U>.stride)
40+
let requested = Size(buffer.count)
4341
if addr > size || requested > size - addr {
4442
throw ArrayImageSourceError.outOfBoundsRead(addr, requested)
4543
}
4644

47-
memcpy(buffer.baseAddress!, $0.baseAddress! + Int(addr), Int(requested))
45+
buffer.copyBytes(from: $0[Int(addr)..<Int(addr+requested)])
4846
}
4947
}
5048
}

stdlib/public/Backtracing/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ set(BACKTRACING_SOURCES
2525
Backtrace.swift
2626
BacktraceFormatter.swift
2727
ByteSwapping.swift
28+
CachingMemoryReader.swift
2829
Context.swift
2930
Compression.swift
3031
CoreSymbolication.swift
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- CachingMemoryReader.swift ----------------------------*- swift -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Wraps a MemoryReader in a layer that caches memory pages.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
import Swift
18+
19+
// The size of the pages in the page cache (must be a power of 2)
20+
fileprivate let pageSize = 4096
21+
22+
fileprivate let pageMask = pageSize - 1
23+
24+
// The largest chunk we will try to cache data for
25+
fileprivate let maxCachedSize = pageSize * 8
26+
27+
@_spi(MemoryReaders)
28+
public class CachingMemoryReader<T: MemoryReader>: MemoryReader {
29+
private var reader: T
30+
private var cache: [Address:UnsafeRawBufferPointer]
31+
32+
public init(for reader: T) {
33+
self.reader = reader
34+
self.cache = [:]
35+
}
36+
37+
deinit {
38+
for (_, page) in cache {
39+
page.deallocate()
40+
}
41+
}
42+
43+
private func getPage(at address: Address) throws -> UnsafeRawBufferPointer {
44+
precondition((address & Address(pageMask)) == 0)
45+
46+
if let page = cache[address] {
47+
return page
48+
}
49+
50+
let page = UnsafeMutableRawBufferPointer.allocate(byteCount: pageSize,
51+
alignment: pageSize)
52+
try reader.fetch(from: address, into: page)
53+
54+
let result = UnsafeRawBufferPointer(page)
55+
56+
cache[address] = result
57+
58+
return result
59+
}
60+
61+
public func fetch(from address: Address,
62+
into buffer: UnsafeMutableRawBufferPointer) throws {
63+
guard buffer.count <= maxCachedSize else {
64+
try reader.fetch(from: address, into: buffer)
65+
return
66+
}
67+
68+
var pageAddress = address & ~Address(pageMask)
69+
var done = 0
70+
var offset = Int(address - pageAddress)
71+
var remaining = buffer.count
72+
73+
while remaining > 0 {
74+
let page = try getPage(at: pageAddress)
75+
let maxBytes = pageSize - offset
76+
let chunk = min(remaining, maxBytes)
77+
78+
buffer[done..<done+chunk].copyBytes(from: page[offset..<offset+chunk])
79+
80+
offset = 0
81+
done += chunk
82+
remaining -= chunk
83+
pageAddress += Address(pageSize)
84+
}
85+
}
86+
}

stdlib/public/Backtracing/Compression.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -427,9 +427,9 @@ internal struct ElfCompressedImageSource<Traits: ElfTraits>: ImageSource {
427427
}
428428
}
429429

430-
public func fetch<T>(from addr: Address,
431-
into buffer: UnsafeMutableBufferPointer<T>) throws {
432-
let toFetch = buffer.count * MemoryLayout<T>.stride
430+
public func fetch(from addr: Address,
431+
into buffer: UnsafeMutableRawBufferPointer) throws {
432+
let toFetch = buffer.count
433433
if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch {
434434
throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch)
435435
}
@@ -474,9 +474,9 @@ internal struct ElfGNUCompressedImageSource: ImageSource {
474474
uncompressedSize: uncompressedSize)
475475
}
476476

477-
public func fetch<T>(from addr: Address,
478-
into buffer: UnsafeMutableBufferPointer<T>) throws {
479-
let toFetch = buffer.count * MemoryLayout<T>.stride
477+
public func fetch(from addr: Address,
478+
into buffer: UnsafeMutableRawBufferPointer) throws {
479+
let toFetch = buffer.count
480480
if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch {
481481
throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch)
482482
}
@@ -509,9 +509,9 @@ internal struct LZMACompressedImageSource: ImageSource {
509509
dataBounds: bounds)
510510
}
511511

512-
public func fetch<T>(from addr: Address,
513-
into buffer: UnsafeMutableBufferPointer<T>) throws {
514-
let toFetch = buffer.count * MemoryLayout<T>.stride
512+
public func fetch(from addr: Address,
513+
into buffer: UnsafeMutableRawBufferPointer) throws {
514+
let toFetch = buffer.count
515515
if addr < 0 || addr > data.count || data.count - Int(addr) < toFetch {
516516
throw CompressedImageSourceError.outOfRangeFetch(addr, toFetch)
517517
}

stdlib/public/Backtracing/FileImageSource.swift

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,16 @@ class FileImageSource: ImageSource {
5858
_mapping.count)
5959
}
6060

61-
public func fetch<T>(from addr: Address,
62-
into buffer: UnsafeMutableBufferPointer<T>) throws {
61+
public func fetch(from addr: Address,
62+
into buffer: UnsafeMutableRawBufferPointer) throws {
6363
let start = Int(addr)
64-
_ = try buffer.withMemoryRebound(to: UInt8.self) { byteBuf in
65-
guard _mapping.indices.contains(start) else {
66-
throw FileImageSourceError.outOfRangeRead
67-
}
68-
let slice = _mapping[start...]
69-
guard slice.count >= byteBuf.count else {
70-
throw FileImageSourceError.outOfRangeRead
71-
}
72-
byteBuf.update(from: slice)
64+
guard _mapping.indices.contains(start) else {
65+
throw FileImageSourceError.outOfRangeRead
7366
}
67+
let slice = _mapping[start...]
68+
guard slice.count >= buffer.count else {
69+
throw FileImageSourceError.outOfRangeRead
70+
}
71+
buffer.copyBytes(from: slice[start..<start+buffer.count])
7472
}
7573
}

stdlib/public/Backtracing/ImageSource.swift

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ struct ImageSourceCursor {
6060
self.pos = offset
6161
}
6262

63+
public mutating func read(into buffer: UnsafeMutableRawBufferPointer) throws {
64+
try source.fetch(from: pos, into: buffer)
65+
pos += UInt64(buffer.count)
66+
}
67+
6368
public mutating func read<T>(into buffer: UnsafeMutableBufferPointer<T>) throws {
6469
try source.fetch(from: pos, into: buffer)
6570
pos += UInt64(MemoryLayout<T>.stride * buffer.count)
@@ -138,9 +143,9 @@ struct SubImageSource<S: ImageSource>: ImageSource {
138143
return parent.isMappedImage
139144
}
140145

141-
public func fetch<T>(from addr: Address,
142-
into buffer: UnsafeMutableBufferPointer<T>) throws {
143-
let toFetch = buffer.count * MemoryLayout<T>.stride
146+
public func fetch(from addr: Address,
147+
into buffer: UnsafeMutableRawBufferPointer) throws {
148+
let toFetch = buffer.count
144149
if addr < 0 || addr > length {
145150
throw SubImageSourceError.outOfRangeFetch(UInt64(addr), toFetch)
146151
}

stdlib/public/Backtracing/MemoryImageSource.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class MemoryImageSource<M: MemoryReader>: ImageSource {
2828
self.reader = reader
2929
}
3030

31-
public func fetch<T>(from addr: Address,
32-
into buffer: UnsafeMutableBufferPointer<T>) throws {
31+
public func fetch(from addr: Address,
32+
into buffer: UnsafeMutableRawBufferPointer) throws {
3333
try reader.fetch(from: addr, into: buffer)
3434
}
3535
}

stdlib/public/Backtracing/MemoryReader.swift

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ import Swift
3030
typealias Address = UInt64
3131
typealias Size = UInt64
3232

33+
/// Fill the specified buffer with data from the specified location in
34+
/// the source.
35+
func fetch(from address: Address,
36+
into buffer: UnsafeMutableRawBufferPointer) throws
37+
3338
/// Fill the specified buffer with data from the specified location in
3439
/// the source.
3540
func fetch<T>(from address: Address,
@@ -51,6 +56,11 @@ import Swift
5156

5257
extension MemoryReader {
5358

59+
public func fetch<T>(from address: Address,
60+
into buffer: UnsafeMutableBufferPointer<T>) throws {
61+
try fetch(from: address, into: UnsafeMutableRawBufferPointer(buffer))
62+
}
63+
5464
public func fetch<T>(from addr: Address,
5565
into pointer: UnsafeMutablePointer<T>) throws {
5666
try fetch(from: addr,
@@ -96,10 +106,12 @@ extension MemoryReader {
96106
@_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader {
97107
public init() {}
98108

99-
public func fetch<T>(from address: Address,
100-
into buffer: UnsafeMutableBufferPointer<T>) throws {
101-
buffer.baseAddress!.update(from: UnsafePointer<T>(bitPattern: UInt(address))!,
102-
count: buffer.count)
109+
public func fetch(from address: Address,
110+
into buffer: UnsafeMutableRawBufferPointer) throws {
111+
buffer.baseAddress!.copyMemory(
112+
from: UnsafeRawPointer(bitPattern: UInt(address))!,
113+
byteCount: buffer.count
114+
)
103115
}
104116
}
105117

@@ -116,9 +128,9 @@ extension MemoryReader {
116128
self.task = task as! task_t
117129
}
118130

119-
public func fetch<T>(from address: Address,
120-
into buffer: UnsafeMutableBufferPointer<T>) throws {
121-
let size = UInt64(MemoryLayout<T>.stride * buffer.count)
131+
public func fetch(from address: Address,
132+
into buffer: UnsafeMutableRawBufferPointer) throws {
133+
let size = buffer.count
122134
var sizeOut = UInt64(0)
123135
let result = mach_vm_read_overwrite(task,
124136
UInt64(address),
@@ -138,8 +150,8 @@ extension MemoryReader {
138150
public typealias Address = UInt64
139151
public typealias Size = UInt64
140152

141-
public func fetch<T>(from address: Address,
142-
into buffer: UnsafeMutableBufferPointer<T>) throws {
153+
public func fetch(from address: Address,
154+
into buffer: UnsafeMutableRawBufferPointer) throws {
143155
let reader = RemoteMemoryReader(task: mach_task_self())
144156
return try reader.fetch(from: address, into: buffer)
145157
}
@@ -221,34 +233,31 @@ extension MemoryReader {
221233
return response
222234
}
223235

224-
public func fetch<T>(from addr: Address,
225-
into buffer: UnsafeMutableBufferPointer<T>) throws {
226-
try buffer.withMemoryRebound(to: UInt8.self) {
227-
let bytes = UnsafeMutableRawBufferPointer($0)
228-
try sendRequest(for: Size(bytes.count), from: addr)
236+
public func fetch(from addr: Address,
237+
into buffer: UnsafeMutableRawBufferPointer) throws {
238+
try sendRequest(for: Size(buffer.count), from: addr)
229239

230-
var done = 0
231-
while done < bytes.count {
232-
let reply = try receiveReply()
233-
234-
if reply.len < 0 {
235-
throw MemserverError(message: "Unreadable at \(hex(addr))")
236-
}
240+
var done = 0
241+
while done < buffer.count {
242+
let reply = try receiveReply()
237243

238-
if done + Int(reply.len) > bytes.count {
239-
throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(bytes.count) bytes")
240-
}
244+
if reply.len < 0 {
245+
throw MemserverError(message: "Unreadable at \(hex(addr))")
246+
}
241247

242-
let ret = try safeRead(fd,
243-
UnsafeMutableRawBufferPointer(
244-
rebasing: bytes[done..<done+Int(reply.len)]))
248+
if buffer.count - done < Int(reply.len) {
249+
throw MemserverError(message: "Overrun at \(hex(addr)) trying to read \(buffer.count) bytes")
250+
}
245251

246-
if ret != reply.len {
247-
throw MemserverError(message: "Channel closed prematurely")
248-
}
252+
let ret = try safeRead(fd,
253+
UnsafeMutableRawBufferPointer(
254+
rebasing: buffer[done..<done+Int(reply.len)]))
249255

250-
done += Int(reply.len)
256+
if ret != reply.len {
257+
throw MemserverError(message: "Channel closed prematurely")
251258
}
259+
260+
done += Int(reply.len)
252261
}
253262
}
254263
}
@@ -260,9 +269,9 @@ extension MemoryReader {
260269
self.pid = pid as! pid_t
261270
}
262271

263-
public func fetch<T>(from address: Address,
264-
into buffer: UnsafeMutableBufferPointer<T>) throws {
265-
let size = size_t(MemoryLayout<T>.stride * buffer.count)
272+
public func fetch(from address: Address,
273+
into buffer: UnsafeMutableRawBufferPointer) throws {
274+
let size = buffer.count
266275
var fromIOVec = iovec(iov_base: UnsafeMutableRawPointer(
267276
bitPattern: UInt(address)),
268277
iov_len: size)
@@ -281,8 +290,8 @@ extension MemoryReader {
281290
reader = RemoteMemoryReader(pid: getpid())
282291
}
283292

284-
public func fetch<T>(from address: Address,
285-
into buffer: UnsafeMutableBufferPointer<T>) throws {
293+
public func fetch(from address: Address,
294+
into buffer: UnsafeMutableRawBufferPointer) throws {
286295
return try reader.fetch(from: address, into: buffer)
287296
}
288297
}

stdlib/public/Backtracing/Utils.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal func hex<T: FixedWidthInteger>(_ value: T,
3030
return "\(prefix)\(padding)\(digits)"
3131
}
3232

33-
internal func hex(_ bytes: [UInt8]) -> String {
33+
internal func hex(_ bytes: some Sequence<UInt8>) -> String {
3434
return bytes.map{ hex($0, prefix: false) }.joined(separator: "")
3535
}
3636

stdlib/public/libexec/swift-backtrace/TargetLinux.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ class Target {
7777
}
7878
}
7979

80-
var reader: MemserverMemoryReader
80+
var reader: CachingMemoryReader<MemserverMemoryReader>
8181

8282
// Get the name of a process
8383
private static func getProcessName(pid: pid_t) -> String {
@@ -110,7 +110,7 @@ class Target {
110110
let memserverFd: CInt = 4
111111

112112
pid = getppid()
113-
reader = MemserverMemoryReader(fd: memserverFd)
113+
reader = CachingMemoryReader(for: MemserverMemoryReader(fd: memserverFd))
114114
name = Self.getProcessName(pid: pid)
115115

116116
let crashInfo: CrashInfo

0 commit comments

Comments
 (0)