diff --git a/Sources/CSystem/include/CSystemDarwin.h b/Sources/CSystem/include/CSystemDarwin.h new file mode 100644 index 00000000..2831886d --- /dev/null +++ b/Sources/CSystem/include/CSystemDarwin.h @@ -0,0 +1,16 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#ifdef __MACH__ +#include "libproc.h" +#else +#error "whoops" +#endif + +// diff --git a/Sources/CSystem/shims.c b/Sources/CSystem/shims.c index f492a2ae..f1dec8f7 100644 --- a/Sources/CSystem/shims.c +++ b/Sources/CSystem/shims.c @@ -16,3 +16,7 @@ #if defined(_WIN32) #include #endif + +#ifdef __MACH__ +#include +#endif diff --git a/Sources/System/Platform/Platform.swift b/Sources/System/Platform/Platform.swift index 167bc61e..74013a9b 100644 --- a/Sources/System/Platform/Platform.swift +++ b/Sources/System/Platform/Platform.swift @@ -9,6 +9,7 @@ @_implementationOnly import SystemInternals + // Public typealiases that can't be reexported from SystemInternals /// The C `mode_t` type. diff --git a/Sources/System/Process/ProcessID.swift b/Sources/System/Process/ProcessID.swift new file mode 100644 index 00000000..3861bb6c --- /dev/null +++ b/Sources/System/Process/ProcessID.swift @@ -0,0 +1,30 @@ + +// FIXME(DO NOT MERGE): We need to find a way around this. We want to declare +// a typealias to a struct from a header, but don't want downstream to import +// Darwin or the whole header just for that. +// +import Darwin +extension CInterop { + public typealias PID = Int32 + public typealias ProcTaskInfo = proc_taskinfo // FIXME + public typealias RUsageInfo = rusage_info_current // FIXME +} + +public struct ProcessID: RawRepresentable, Hashable, Codable { + /// The raw C process id. + @_alwaysEmitIntoClient + public let rawValue: CInterop.PID + + /// Creates a strongly-typed process id from a raw C pid + @_alwaysEmitIntoClient + public init(rawValue: CInterop.PID) { self.rawValue = rawValue } + + fileprivate init(_ rawValue: CInterop.PID) { self.init(rawValue: rawValue) } + +} + +extension ProcessID { + public static func current() -> ProcessID { + ProcessID(getpid()) + } +} diff --git a/Sources/System/Process/ResourceUsageInfo.swift b/Sources/System/Process/ResourceUsageInfo.swift new file mode 100644 index 00000000..619a9c72 --- /dev/null +++ b/Sources/System/Process/ResourceUsageInfo.swift @@ -0,0 +1,159 @@ + +extension ProcessID { + public struct ResourceUsageInfo: RawRepresentable/*, Hashable, Codable*/ { + /// The raw C process id. + @_alwaysEmitIntoClient + public let rawValue: CInterop.RUsageInfo + + /// Creates a strongly-typed process id from a raw C pid + @_alwaysEmitIntoClient + public init(rawValue: CInterop.RUsageInfo) { self.rawValue = rawValue } + + fileprivate init(_ rawValue: CInterop.RUsageInfo) { + self.init(rawValue: rawValue) + } + + fileprivate static var blank: ResourceUsageInfo { + ResourceUsageInfo(rusage_info_current()) + } + } +} + +// FIXME(DO NOT MERGE): system_foo wrappers for these and mocking +import CSystem +extension ProcessID { + public func getResourceUsageInfo() throws -> ResourceUsageInfo { + var current = ResourceUsageInfo.blank + try withUnsafeMutablePointer(to: ¤t) { + try $0.withMemoryRebound(to: rusage_info_t?.self, capacity: 1) { + guard 0 == proc_pid_rusage(self.rawValue, RUSAGE_INFO_CURRENT, $0) else { + throw Errno(rawValue: errno) + } + } + } + return current + } +} + +// FIXME: docs or comments, the headers have none... +// FIXME: names +extension ProcessID.ResourceUsageInfo { + // FIXME: UUID proper type + public typealias UUID = ( + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, + UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) + + /// `ri_uuid`: TBD + public var uuid: UUID { rawValue.ri_uuid } + + /// `ri_user_time`: TBD + public var userTime: UInt64 { rawValue.ri_user_time } + + /// `ri_system_time`: TBD + public var systemTime: UInt64 { rawValue.ri_system_time } + + /// `ri_pkg_idle_wkups`: TBD + public var pkgIdleWakeups: UInt64 { rawValue.ri_pkg_idle_wkups } + + /// `ri_interrupt_wkups`: TBD + public var interruptWakeups: UInt64 { rawValue.ri_interrupt_wkups } + + /// `ri_pageins`: TBD + public var pageIns: UInt64 { rawValue.ri_pageins } + + /// `ri_wired_size`: TBD + public var wiredSize: UInt64 { rawValue.ri_wired_size } + + /// `ri_resident_size`: TBD + public var residentSize: UInt64 { rawValue.ri_resident_size } + + /// `ri_phys_footprint`: TBD + public var physicalFootprint: UInt64 { rawValue.ri_phys_footprint } + + /// `ri_proc_start_abstime`: TBD + public var processStartAbsoluteTime: UInt64 { rawValue.ri_proc_start_abstime } + + /// `ri_proc_exit_abstime`: TBD + public var processExitAbsoluteTime: UInt64 { rawValue.ri_proc_exit_abstime } + + /// `ri_child_user_time`: TBD + public var childUserTime: UInt64 { rawValue.ri_child_user_time } + + /// `ri_child_system_time`: TBD + public var childSystemTime: UInt64 { rawValue.ri_child_system_time } + + /// `ri_child_pkg_idle_wkups`: TBD + public var childPkgIdleWakeups: UInt64 { rawValue.ri_child_pkg_idle_wkups } + + /// `ri_child_interrupt_wkups`: TBD + public var childInterruptWakeups: UInt64 { rawValue.ri_child_interrupt_wkups } + + /// `ri_child_pageins`: TBD + public var childPageIns: UInt64 { rawValue.ri_child_pageins } + + /// `ri_child_elapsed_abstime`: TBD + public var childElapsedAbsoluteTime: UInt64 { rawValue.ri_child_elapsed_abstime } + + /// `ri_diskio_bytesread`: TBD + public var diskIOBytesRead: UInt64 { rawValue.ri_diskio_bytesread } + + /// `ri_diskio_byteswritten`: TBD + public var diskIOBytesWritten: UInt64 { rawValue.ri_diskio_byteswritten } + + /// `ri_cpu_time_qos_default`: TBD + public var cpuTimeQOSDefault: UInt64 { rawValue.ri_cpu_time_qos_default } + + /// `ri_cpu_time_qos_maintenance`: TBD + public var cpuTimeQOSMaintenance: UInt64 { rawValue.ri_cpu_time_qos_maintenance } + + /// `ri_cpu_time_qos_background`: TBD + public var cpuTimeQOSBackground: UInt64 { rawValue.ri_cpu_time_qos_background } + + /// `ri_cpu_time_qos_utility`: TBD + public var cpuTimeQOSUtility: UInt64 { rawValue.ri_cpu_time_qos_utility } + + /// `ri_cpu_time_qos_legacy`: TBD + public var cpuTimeQOSLegacy: UInt64 { rawValue.ri_cpu_time_qos_legacy } + + /// `ri_cpu_time_qos_user_initiated`: TBD + public var cpuTimeQOSUserInitiated: UInt64 { rawValue.ri_cpu_time_qos_user_initiated } + + /// `ri_cpu_time_qos_user_interactive`: TBD + public var cpuTimeQOSUserInteractive: UInt64 { rawValue.ri_cpu_time_qos_user_interactive } + + /// `ri_billed_system_time`: TBD + public var billedSystemTime: UInt64 { rawValue.ri_billed_system_time } + + /// `ri_serviced_system_time`: TBD + public var servicedSystemTime: UInt64 { rawValue.ri_serviced_system_time } + + /// `ri_logical_writes`: TBD + public var logicalWrites: UInt64 { rawValue.ri_logical_writes } + + /// `ri_lifetime_max_phys_footprint`: TBD + public var lifetimeMaxPhysicalFootprint: UInt64 { rawValue.ri_lifetime_max_phys_footprint } + + /// `ri_instructions`: TBD + public var instructions: UInt64 { rawValue.ri_instructions } + + /// `ri_cycles`: TBD + public var cycles: UInt64 { rawValue.ri_cycles } + + /// `ri_billed_energy`: TBD + public var billedEnergy: UInt64 { rawValue.ri_billed_energy } + + /// `ri_serviced_energy`: TBD + public var servicedEnergy: UInt64 { rawValue.ri_serviced_energy } + + /// `ri_interval_max_phys_footprint`: TBD + public var intervalMaxPhysicalFootprint: UInt64 { rawValue.ri_interval_max_phys_footprint } + + /// `ri_runnable_time`: TBD + public var runnableTime: UInt64 { rawValue.ri_runnable_time } + + /// `ri_flags`: TBD + public var flags: UInt64 { rawValue.ri_flags } + +} + + diff --git a/Sources/System/Process/Signal.swift b/Sources/System/Process/Signal.swift new file mode 100644 index 00000000..8d20715c --- /dev/null +++ b/Sources/System/Process/Signal.swift @@ -0,0 +1,113 @@ + +public struct Signal: RawRepresentable, Hashable { + public var rawValue: CInt + public init(rawValue: CInt) { self.rawValue = rawValue } + fileprivate init(_ rawValue: CInt) { self.init(rawValue: rawValue) } +} + +// FIXME(DO NOT MERGE): Migrate these to the constants.swift file +import Darwin + +extension Signal { + #if os(Linux) + public static var unused: Signal { Signal(SIGUNUSED) } + #endif + + // TODO: better names + + /// SIGHUP (1): terminal line hangup (default behavior: terminate process) + public static var hangup: Signal { Signal(SIGHUP) } + + /// SIGINT (2): interrupt program (default behavior: terminate process) + public static var interrupt: Signal { Signal(SIGINT) } + + /// SIGQUIT (3): quit program (default behavior: create core image) + public static var quit: Signal { Signal(SIGQUIT) } + + /// SIGILL (4): illegal instruction (default behavior: create core image) + public static var illegalInstruction: Signal { Signal(SIGILL) } + + /// SIGTRAP (5): trace trap (default behavior: create core image) + public static var traceTrap: Signal { Signal(SIGTRAP) } + + /// SIGABRT (6): abort program (formerly SIGIOT) (default behavior: create core image) + public static var abort: Signal { Signal(SIGABRT) } + + /// SIGEMT (7): emulate instruction executed (default behavior: create core image) + public static var emulatorTrap: Signal { Signal(SIGEMT) } + + /// SIGFPE (8): floating-point exception (default behavior: create core image) + public static var floatingPointException: Signal { Signal(SIGFPE) } + + /// SIGKILL (9): kill program (default behavior: terminate process) + public static var kill: Signal { Signal(SIGKILL) } + + /// SIGBUS (10): bus error (default behavior: create core image) + public static var busError: Signal { Signal(SIGBUS) } + + /// SIGSEGV (11): segmentation violation (default behavior: create core image) + public static var segmentationViolation: Signal { Signal(SIGSEGV) } + + /// SIGSYS (12): non-existent system call invoked (default behavior: create core image) + public static var unknownSystemCall: Signal { Signal(SIGSYS) } + + /// SIGPIPE (13): write on a pipe with no reader (default behavior: terminate process) + public static var brokenPipe: Signal { Signal(SIGPIPE) } + + /// SIGALRM (14): real-time timer expired (default behavior: terminate process) + public static var alarm: Signal { Signal(SIGALRM) } + + /// SIGTERM (15): software termination signal (default behavior: terminate process) + public static var terminate: Signal { Signal(SIGTERM) } + + /// SIGURG (16): urgent condition present on socket (default behavior: discard signal) + public static var urgentCondition: Signal { Signal(SIGURG) } + + /// SIGSTOP (17): stop (cannot be caught or ignored) (default behavior: stop process) + public static var stop: Signal { Signal(SIGSTOP) } + + /// SIGTSTP (18): stop signal generated from keyboard (default behavior: stop process) + public static var temporaryStop: Signal { Signal(SIGTSTP) } + + /// SIGCONT (19): continue after stop (default behavior: discard signal) + public static var `continue`: Signal { Signal(SIGCONT) } + + /// SIGCHLD (20): child status has changed (default behavior: discard signal) + public static var childProcessStatusChange: Signal { Signal(SIGCHLD) } + + /// SIGTTIN (21): background read attempted from control terminal (default behavior: stop process) + public static var backgroundReadFromControllingTerminal: Signal { Signal(SIGTTIN) } + + /// SIGTTOU (22): background write attempted to control terminal (default behavior: stop process) + public static var backgroundWriteToControllingTerminal: Signal { Signal(SIGTTOU) } + + /// SIGIO (23): I/O is possible on a descriptor (see fcntl(2)) (default behavior: discard signal) + public static var ioAvailable: Signal { Signal(SIGIO) } + + /// SIGXCPU (24): cpu time limit exceeded (see setrlimit(2)) (default behavior: terminate process) + public static var cpuLimitExceeded: Signal { Signal(SIGXCPU) } + + /// SIGXFSZ (25): file size limit exceeded (see setrlimit(2)) (default behavior: terminate process) + public static var fileSizeLimitExceeded: Signal { Signal(SIGXFSZ) } + + /// SIGVTALRM (26): virtual time alarm (see setitimer(2)) (default behavior: terminate process) + public static var virtualAlarm: Signal { Signal(SIGVTALRM) } + + /// SIGPROF (27): profiling timer alarm (see setitimer(2)) (default behavior: terminate process) + public static var profilingAlarm: Signal { Signal(SIGPROF) } + + /// SIGWINCH (28): Window size change (default behavior: discard signal) + public static var windowSizeChange: Signal { Signal(SIGWINCH) } + + /// SIGINFO (29): status request from keyboard (default behavior: discard signal) + public static var info: Signal { Signal(SIGINFO) } + + /// SIGUSR1 (30): User defined signal 1 (default behavior: terminate process) + public static var user1: Signal { Signal(SIGUSR1) } + + /// SIGUSR2 (31): User defined signal 2 (default behavior: terminate process) + public static var user2: Signal { Signal(SIGUSR2) } + +} + +// TODO: unavailable renamed diff --git a/Sources/System/Process/SignalSet.swift b/Sources/System/Process/SignalSet.swift new file mode 100644 index 00000000..936cd7c1 --- /dev/null +++ b/Sources/System/Process/SignalSet.swift @@ -0,0 +1,86 @@ + +extension CInterop { + public typealias SigSet = UInt32 // TODO: Linux? +} + + +public struct SignalSet: Hashable, Codable { + internal var pointee: CInterop.SigSet + + internal init(pointee: CInterop.SigSet) { self.pointee = pointee } + + // Only to be used prior to producing an empty or full set + fileprivate init(uninitialized: ()) { + self.pointee = 0 + } +} + +extension SignalSet { + internal func withUnsafePointer( + _ f: (UnsafePointer) throws -> T + ) rethrows -> T { + try Swift.withUnsafePointer(to: self.pointee) { try f($0) } + } + internal mutating func withUnsafeMutablePointer( + _ f: (UnsafeMutablePointer) throws -> T + ) rethrows -> T { + try Swift.withUnsafeMutablePointer(to: &self.pointee) { try f($0) } + } +} + +// FIXME(DO NOT MERGE): Make foo wrappers, for mocking etc. +import Darwin + +extension SignalSet { + public mutating func insert(_ sig: Signal) { + _ = withUnsafeMutablePointer { sigaddset($0, sig.rawValue) } + } + public mutating func remove(_ sig: Signal) { + _ = withUnsafeMutablePointer { sigdelset($0, sig.rawValue) } + } + + public func contains(_ sig: Signal) -> Bool { + 1 == withUnsafePointer { sigismember($0, sig.rawValue) } + } + + public static var empty: SignalSet { + var ret = SignalSet(uninitialized: ()) + _ = ret.withUnsafeMutablePointer { sigemptyset($0) } + return ret + } + + public static var full: SignalSet { + var ret = SignalSet(uninitialized: ()) + _ = ret.withUnsafeMutablePointer { sigfillset($0) } + return ret + } +} + +extension SignalSet: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: Signal...) { + self = SignalSet.empty + elements.forEach { self.insert($0) } + } + + public typealias ArrayLiteralElement = Signal +} + +extension SignalSet { + // TODO: Proper name for this concept + public static var _defaultForSPM: SignalSet { + #if os(macOS) + var ret = SignalSet.full + ret.remove(.kill) + ret.remove(.stop) + #else + var ret = SignalSet.empty + for i in 1 ..< Signal.unused.rawValue { + let sig = Signal(rawValue: i) + guard sig != .kill && sig != .stop else { continue } + ret.insert(sig) + } + #endif + return ret + } + +} diff --git a/Sources/System/Process/TaskInfo.swift b/Sources/System/Process/TaskInfo.swift new file mode 100644 index 00000000..daf6ad02 --- /dev/null +++ b/Sources/System/Process/TaskInfo.swift @@ -0,0 +1,82 @@ + +// FIXME(DO NOT MERGE): system_foo wrappers for these and mocking +import CSystem + +extension ProcessID { + public struct TaskInfo: RawRepresentable/*, Hashable, Codable*/ { + /// The raw C process id. + @_alwaysEmitIntoClient + public let rawValue: CInterop.ProcTaskInfo + + /// Creates a strongly-typed process id from a raw C pid + @_alwaysEmitIntoClient + public init(rawValue: CInterop.ProcTaskInfo) { self.rawValue = rawValue } + + fileprivate init(_ rawValue: CInterop.ProcTaskInfo) { + self.init(rawValue: rawValue) + } + } + public func getTaskInfo() throws -> TaskInfo { + var result = TaskInfo.RawValue() + try withUnsafeMutableBytes(of: &result) { + let val = proc_pidinfo(self.rawValue, PROC_PIDTASKINFO, 0, $0.baseAddress, Int32($0.count)) + // What is this wacky shenanigans? + guard MemoryLayout.stride == val else { + throw Errno(rawValue: val) + } + } + return TaskInfo(result) + } +} + +extension ProcessID.TaskInfo { + + /// `pti_virtual_size`: virtual memory size (bytes) + public var virtualSize: UInt64 { UInt64(rawValue.pti_virtual_size) } + + /// `pti_resident_size`: resident memory size (bytes) + public var residentSize: UInt64 { UInt64(rawValue.pti_resident_size) } + + /// `pti_total_user`: total time + public var totalUserTime: UInt64 { UInt64(rawValue.pti_total_user) } + + /// `pti_threads_user`: existing threads only + public var userThreads: UInt64 { UInt64(rawValue.pti_threads_user) } + + /// `pti_policy`: default policy for new threads + public var policy: Int { Int(rawValue.pti_policy) } + + /// `pti_faults`: number of page faults + public var pageFaults: Int { Int(rawValue.pti_faults) } + + /// `pti_pageins`: number of actual pageins + public var pageIns: Int { Int(rawValue.pti_pageins) } + + /// `pti_cow_faults`: number of copy-on-write faults + public var cowFaults: Int { Int(rawValue.pti_cow_faults) } + + /// `pti_messages_sent`: number of messages sent + public var messagesSent: Int { Int(rawValue.pti_messages_sent) } + + /// `pti_messages_received`: number of messages received + public var messagesReceived: Int { Int(rawValue.pti_messages_received) } + + /// `pti_syscalls_mach`: number of mach system calls + public var syscallsMach: Int { Int(rawValue.pti_syscalls_mach) } + + /// `pti_syscalls_unix`: number of unix system calls + public var syscallsUnix: Int { Int(rawValue.pti_syscalls_unix) } + + /// `pti_csw`: number of context switches + public var contextSwitches: Int { Int(rawValue.pti_csw) } + + /// `pti_threadnum`: number of threads in the task + public var taskThreads: Int { Int(rawValue.pti_threadnum) } + + /// `pti_numrunning`: number of running threads + public var runningThreads: Int { Int(rawValue.pti_numrunning) } + + /// `pti_priority`: task priority + public var taskPriority: Int { Int(rawValue.pti_priority) } + +}