//===--- ProcMapsScanner.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 // //===----------------------------------------------------------------------===// // // Defines ProcMapsScanner, which is for scanning the /proc//maps // pseudofiles on Linux. // //===----------------------------------------------------------------------===// #if os(Linux) import Swift // Lines in /proc/pid/maps files match the following regex: // // ^(?[A-Fa-f0-9]+)-(?[A-Fa-f0-9]+)\s+ // (?[-rwxsp]{4})\s+ // (?[A-Fa-f0-9]+)\s+ // (?[A-Fa-f0-9]+):(?[A-Fa-f0-9]+)\s+ // (?\d+)\s+ // (?.*)\s*$ struct ProcMapsScanner: Sequence, IteratorProtocol { typealias SS = S.SubSequence struct Match { var start: SS var end: SS var perms: SS var offset: SS var major: SS var minor: SS var inode: SS var pathname: SS } private enum State { case start case scanningStart case scanningEnd case afterEnd case scanningPerms(Int) case afterPerms case scanningOffset case afterOffset case scanningMajor case scanningMinor case afterMinor case scanningInode case afterInode case scanningPathname case scanningPathnameWhitespace } private var procMaps: S private var procMapsUTF8: S.UTF8View private var ndx: S.UTF8View.Index init(_ string: S) { procMaps = string procMapsUTF8 = string.utf8 ndx = procMapsUTF8.startIndex } mutating func scanMatch() -> Match? { var match: Match = Match(start: "", end: "", perms: "", offset: "", major: "", minor: "", inode: "", pathname: "") var currentChunk = ndx var state: State = .start func isPerm(_ ch: UInt8) -> Bool { return ch == UInt8(ascii: "-") || ch == UInt8(ascii: "r") || ch == UInt8(ascii: "w") || ch == UInt8(ascii: "x") || ch == UInt8(ascii: "s") || ch == UInt8(ascii: "p") } func isDecimal(_ ch: UInt8) -> Bool { return ch >= UInt8(ascii: "0") && ch <= UInt8(ascii: "9") } func isHex(_ ch: UInt8) -> Bool { return ch >= UInt8(ascii: "A") && ch <= UInt8(ascii: "F") || ch >= UInt8(ascii: "a") && ch <= UInt8(ascii: "f") || ch >= UInt8(ascii: "0") && ch <= UInt8(ascii: "9") } func isWhitespace(_ ch: UInt8) -> Bool { return ch == UInt8(ascii: " ") || ch == UInt8(ascii: "\t") } while ndx < procMapsUTF8.endIndex { let ch = procMapsUTF8[ndx] let next = procMapsUTF8.index(after: ndx) switch state { case .start: if !isHex(ch) { return nil } state = .scanningStart case .scanningStart: if ch == UInt8(ascii: "-") { match.start = procMaps[currentChunk.. Match? { while ndx != procMapsUTF8.endIndex { if let match = scanMatch() { return match } // If we failed to match, skip to end of line and retry while ndx != procMapsUTF8.endIndex { let ch = procMapsUTF8[ndx] ndx = procMapsUTF8.index(after: ndx) if ch == 0x0a { break } } } return nil } } #endif // os(Linux)