Files
swift-mirror/stdlib/public/RuntimeModule/ImageMap+Linux.swift
Alastair Houghton c9c5dc0de1 [Backtracing] Add platform and architecture information.
It's useful to capture the platform and platform version with the image map.
Also, display both the platform and architecture information when generating
a crash log.

rdar://124913332
2025-01-27 15:44:28 +00:00

170 lines
4.5 KiB
Swift

//===--- ImageMap+Linux.swift --------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
//
//===----------------------------------------------------------------------===//
//
// Linux specifics for ImageMap capture.
//
//===----------------------------------------------------------------------===//
#if os(Linux)
import Swift
#if canImport(Glibc)
internal import Glibc
#elseif canImport(Musl)
internal import Musl
#endif
internal import BacktracingImpl.ImageFormats.Elf
fileprivate func readOSRelease(fd: CInt) -> [String:String]? {
let len = lseek(fd, 0, SEEK_END)
guard len >= 0 else {
return nil
}
return withUnsafeTemporaryAllocation(byteCount: len, alignment: 16) {
(buffer: UnsafeMutableRawBufferPointer) -> [String:String]? in
_ = lseek(fd, 0, SEEK_SET)
let bytesRead = read(fd, buffer.baseAddress, buffer.count)
guard bytesRead == buffer.count else {
return nil
}
let asString = String(decoding: buffer, as: UTF8.self)
return Dictionary(OSReleaseScanner(asString),
uniquingKeysWith: { $1 })
}
}
fileprivate func readOSRelease() -> [String:String]? {
var fd = open("/etc/os-release", O_RDONLY)
if fd == -1 {
fd = open("/usr/lib/os-release", O_RDONLY)
}
if fd == -1 {
return nil
}
defer {
close(fd)
}
return readOSRelease(fd: fd)
}
extension ImageMap {
private static var platform = {
guard let info = readOSRelease(),
let pretty = info["PRETTY_NAME"] else {
return "Linux (unknown)"
}
return "Linux (\(pretty))"
}()
private struct AddressRange {
var low: Address = 0
var high: Address = 0
}
@_specialize(exported: true, kind: full, where M == UnsafeLocalMemoryReader)
@_specialize(exported: true, kind: full, where M == RemoteMemoryReader)
@_specialize(exported: true, kind: full, where M == LocalMemoryReader)
@_spi(Internal)
public static func capture<M: MemoryReader>(
using reader: M,
forProcess pid: Int? = nil
) -> ImageMap {
var images: [Image] = []
let wordSize: WordSize
#if arch(x86_64) || arch(arm64) || arch(arm64_32)
wordSize = .sixtyFourBit
#elseif arch(i386) || arch(arm)
wordSize = .thirtyTwoBit
#endif
let path: String
if let pid = pid {
path = "/proc/\(pid)/maps"
} else {
path = "/proc/self/maps"
}
guard let procMaps = readString(from: path) else {
return ImageMap(platform: ImageMap.platform, images: [], wordSize: wordSize)
}
// Find all the mapped files and get high/low ranges
var mappedFiles: [Substring:AddressRange] = [:]
for match in ProcMapsScanner(procMaps) {
let path = stripWhitespace(match.pathname)
if match.inode == "0" || path == "" {
continue
}
guard let start = Address(match.start, radix: 16),
let end = Address(match.end, radix: 16) else {
continue
}
if let range = mappedFiles[path] {
mappedFiles[path] = AddressRange(low: Swift.min(start, range.low),
high: Swift.max(end, range.high))
} else {
mappedFiles[path] = AddressRange(low: start,
high: end)
}
}
// Look at each mapped file to see if it's an ELF image
for (path, range) in mappedFiles {
// Extract the filename from path
let name: Substring
if let slashIndex = path.lastIndex(of: "/") {
name = path.suffix(from: path.index(after: slashIndex))
} else {
name = path
}
// Inspect the image and extract the UUID and end of text
guard let (endOfText, uuid) = getElfImageInfo(
at: M.Address(exactly: range.low)!,
using: reader
) else {
// Not an ELF image
continue
}
let image = Image(name: String(name),
path: String(path),
uniqueID: uuid,
baseAddress: range.low,
endOfText: Address(endOfText))
images.append(image)
}
images.sort(by: { $0.baseAddress < $1.baseAddress })
return ImageMap(
platform: ImageMap.platform,
images: images,
wordSize: wordSize
)
}
}
#endif // os(Linux)