├── API.md ├── LICENSE ├── Makefile ├── README.md ├── SystemMonitor.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── jacques.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── SystemMonitor.xcscheme └── xcuserdata │ └── jacques.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── SystemMonitor ├── BatteryHandler.swift ├── DiskHandler.swift ├── GPUHandler.swift ├── Info.plist ├── MemoryHandler.swift ├── NetworkHandler.swift ├── ProcessorHandler.swift ├── SensorsHandler.swift ├── SystemHandler.swift ├── SystemMonitor.h ├── SystemMonitor.swift └── Utils.swift ├── SystemMonitorTests ├── Info.plist └── SystemMonitorTests.swift └── TODO.md /API.md: -------------------------------------------------------------------------------- 1 | # SystemMonitor API 2 | 3 | After the installation, to use the SystemMonitor framework, you must import it first in your code with: 4 | 5 | ```swift 6 | import SystemMonitor 7 | ``` 8 | 9 | Then you can call function to get informations from your system (see below for structures description): 10 | 11 | ```swift 12 | // To initialize the monitor instance 13 | let monitor = try SystemMonitor() 14 | 15 | // Returns SystemInfos struct with all informations 16 | let infos = try monitor.getInfos() 17 | 18 | 19 | 20 | // Returns SystemSpecificInfos struct with system specific informations 21 | let systemInfos = try monitor.getSystemInfos() 22 | 23 | // Returns CPUInfos struct with processor specific informations 24 | let cpuInfos = try monitor.getProcessorInfos() 25 | 26 | // Returns MemoryUsage struct with memory specific informations 27 | let memoryInfos = try monitor.getMemoryInfos() 28 | 29 | // Returns VolumesDisksInfos struct with volumes and disks specific informations 30 | let disksVolumesInfos = try monitor.getDiskInfos() 31 | 32 | // Returns NetworkInterfaceInfos struct array with network interfaces specific informations 33 | let networkInfos = try monitor.getNetworkInfos() 34 | 35 | // Returns GPUInfos struct array with graphics specific informations 36 | let gpuInfos = try monitor.getGPUInfos() 37 | 38 | // Returns BatteryInfos struct with power and bbattery specific informations 39 | let batteryInfos = try monitor.getBatteryInfos() 40 | 41 | // Returns SensorsInfos struct with sensors specific informations 42 | let sensorsInfos = try monitor.getSensorsInfos() 43 | ``` 44 | 45 | ## Informations structures description 46 | 47 | #### General 48 | 49 | ```swift 50 | // Structure with contains all informations 51 | public struct SystemInfos { 52 | let memory: MemoryUsage 53 | let processor: CPUInfos 54 | let disk: VolumesDisksInfos 55 | let network: [NetworkInterfaceInfos] 56 | let graphics: [GPUInfos] 57 | let system: SystemSpecificInfos 58 | let battery: BatteryInfos 59 | let sensors: SensorsInfos 60 | } 61 | ``` 62 | 63 | #### System 64 | 65 | ```swift 66 | // System specifics informations 67 | public struct SystemSpecificInfos { 68 | let boottime: Int 69 | let hostname: String 70 | let kernel: String 71 | let kernelVersion: String 72 | } 73 | ``` 74 | 75 | #### Processor 76 | 77 | ```swift 78 | // All processor informations 79 | public struct CPUInfos { 80 | let coreNumber: Int32 81 | let model: String 82 | let cpuFeatures: [String] 83 | let usage: CPUUsage 84 | } 85 | // Processor usage 86 | public struct CPUUsage { 87 | let cores: [CPUCoreUsage] 88 | let total: CPUCoreUsage 89 | 90 | func toPercent(unixLike: Bool) -> CPUUsagePercent 91 | } 92 | // Processor usage in percents 93 | public struct CPUUsagePercent { 94 | let cores: [CPUCoreUsagePercent] 95 | let total: CPUCoreUsagePercent 96 | } 97 | // Single CPU core usage 98 | public struct CPUCoreUsage { 99 | let user: UInt32 100 | let system: UInt32 101 | let idle: UInt32 102 | let nice: UInt32 103 | 104 | func toPercent() -> CPUCoreUsagePercent 105 | } 106 | // Single CPU core usage in percents 107 | public struct CPUCoreUsagePercent { 108 | let user: Float 109 | let system: Float 110 | let idle: Float 111 | let nice: Float 112 | } 113 | ``` 114 | 115 | #### Memory 116 | 117 | ```swift 118 | // RAM and swap informations 119 | public struct MemoryUsage { 120 | let swapUsage: SwapUsage 121 | let ramUsage: RAMUsage 122 | } 123 | // Swap usage in bytes 124 | public struct SwapUsage { 125 | let total: UInt64 126 | let used: UInt64 127 | let free: UInt64 128 | 129 | func convertTo(unit: String) throws -> ConvertedSwapUsage 130 | } 131 | // RAM usage in memory pages (4096 bytes) 132 | public struct RAMUsage { 133 | let wired: UInt 134 | let active: UInt 135 | let appMemory: UInt 136 | let compressed: UInt 137 | let available: UInt 138 | 139 | func convertTo(unit: String) throws -> ConvertedRAMUsage 140 | } 141 | // Swap usage in human a readable unit 142 | public struct ConvertedSwapUsage { 143 | let total: Float 144 | let used: Float 145 | let free: Float 146 | let unit: String 147 | } 148 | // RAM usage in human a readable unit 149 | public struct ConvertedRAMUsage { 150 | let wired: Float 151 | let active: Float 152 | let appMemory: Float 153 | let compressed: Float 154 | let available: Float 155 | let unit: String 156 | } 157 | ``` 158 | 159 | #### Disks and Volumes 160 | 161 | ```swift 162 | // All disks and volumes informations 163 | public struct VolumesDisksInfos { 164 | let volumes: [VolumeInfos] 165 | let disks: [DiskInfos] 166 | } 167 | // Disk informations 168 | public struct DiskInfos { 169 | let name: String 170 | let blocksize: UInt32 171 | let size: UInt64 172 | let usage: DiskUsage 173 | } 174 | // Disk usage in bytes 175 | public struct DiskUsage { 176 | let bytesread: UInt64 177 | let byteswritten: UInt64 178 | let operationsread: UInt64 179 | let operationswritten: UInt64 180 | } 181 | // Volume informations 182 | public struct VolumeInfos { 183 | let filesystem: String 184 | let mountpoint: String 185 | let mountname: String 186 | let usage: VolumeUsage 187 | } 188 | // Volume usage in volume block 189 | public struct VolumeUsage { 190 | let blocksize: UInt32 191 | let iosize: Int32 192 | let blocks: UInt64 193 | let free: UInt64 194 | let available: UInt64 195 | let files: UInt64 196 | let filesfree: UInt64 197 | 198 | func convertTo(unit: String) throws -> ConvertedVolumeUsage 199 | } 200 | // Volume usage in human a readable unit 201 | public struct ConvertedVolumeUsage { 202 | let total: Float 203 | let free: Float 204 | let available: Float 205 | let unit: String 206 | } 207 | ``` 208 | 209 | #### Network interfaces 210 | 211 | ```swift 212 | // All informations for a network interface 213 | public struct NetworkInterfaceInfos { 214 | let name: String 215 | let bytessend: UInt 216 | let bytesreceived: UInt 217 | let addresses: [InterfaceAddress] 218 | } 219 | // Informations for a network interface address 220 | public struct InterfaceAddress { 221 | let address: String 222 | let netmask: String 223 | let destaddress: String 224 | let type: String 225 | let flags: InterfaceAddressFlags 226 | } 227 | // Flags for a network interface address 228 | public struct InterfaceAddressFlags { 229 | let iff_up: Bool 230 | let iff_broadcast: Bool 231 | let iff_debug: Bool 232 | let iff_loopback: Bool 233 | let iff_pointopoint: Bool 234 | let iff_notrailers: Bool 235 | let iff_running: Bool 236 | let iff_noarp: Bool 237 | let iff_promisc: Bool 238 | let iff_allmulti: Bool 239 | let iff_oactive: Bool 240 | let iff_simplex: Bool 241 | let iff_link0: Bool 242 | let iff_link1: Bool 243 | let iff_link2: Bool 244 | let iff_altphys: Bool 245 | let iff_multicast: Bool 246 | } 247 | ``` 248 | 249 | #### Graphics 250 | 251 | ```swift 252 | // All informations for a GPU 253 | public struct GPUInfos { 254 | let name: String 255 | let isOn: Bool 256 | let utilization: UInt 257 | let vramTotalMB: UInt 258 | let vramFreeMB: UInt 259 | let coreClockMHz: UInt 260 | let memoryClockMHz: UInt 261 | let sensors: GPUSensors 262 | } 263 | // GPU sensors informations 264 | public struct GPUSensors { 265 | let totalPower: UInt 266 | let temperature: UInt 267 | let fanSpeedPercent: UInt 268 | let fanSpeedRPM: UInt 269 | } 270 | ``` 271 | 272 | #### Power and Battery 273 | 274 | ```swift 275 | // Battery informations 276 | public struct BatteryInfos { 277 | let serialNumber: String 278 | let manufactureDate: Date 279 | let cycleCount: Int 280 | 281 | let designCapacity: Int 282 | let maxCapacity: Int 283 | let currentCapacity: Int 284 | let voltage: Int 285 | let amperage: Int 286 | let instantAmperage: Int 287 | 288 | let timeRemaining: Int 289 | let timeToFull: Int 290 | let timeToEmpty: Int 291 | let isCharging: Bool 292 | let isFullyCharged: Bool 293 | let chargingCurrent: Int 294 | } 295 | ``` 296 | 297 | #### Sensors 298 | 299 | ```swift 300 | // All sensors informations 301 | public struct SensorsInfos { 302 | let fans: [FanSensor] 303 | let temperatures: [String:Float] 304 | let amperages: [String:Float] 305 | let voltages: [String:Float] 306 | let powers: [String:Float] 307 | } 308 | // Single fan's speed informations 309 | public struct FanSensor { 310 | let min: Float 311 | let max: Float 312 | let actual: Float 313 | let target: Float 314 | } 315 | ``` 316 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jacques Lorentz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME = SystemMonitor 2 | PROJECT = $(NAME).xcodeproj 3 | SCHEME = $(NAME) 4 | 5 | all: 6 | xcodebuild -project $(PROJECT) 7 | 8 | test: 9 | xcodebuild test -project $(PROJECT) -scheme $(SCHEME) 10 | 11 | clean: 12 | rm -rf ./build -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SystemMonitor 2 | Swift Library for macOS to monitor your system https://github.com/jacqueslorentz/SystemMonitor 3 | 4 | ## Requirements 5 | 6 | - Xcode 10.0 or higher (usage of Swift 4) 7 | - macOS 10.9 or higher (usage of Swift) 8 | - Tested on macOS 10.14 beta 5 (on a MacBookPro13,3) 9 | 10 | ## Installation 11 | 12 | You can build the project with Xcode and add the compiled framework to your project. 13 | 14 | ### Carthage 15 | 16 | You can install the framework with [Carthage](https://github.com/Carthage/Carthage) ([here](https://github.com/Carthage/Carthage#installing-carthage) for installation). 17 | 18 | To integrate the framework into your Xcode project using Carthage, specify it in your Cartfile: 19 | 20 | ``` 21 | github "jacqueslorentz/SystemMonitor" 22 | ``` 23 | 24 | And build the SystemMonitor.framework: 25 | 26 | ``` 27 | carthage update 28 | ``` 29 | 30 | Do not forget to add the framework to your macOS project ([see Carthage documentation](https://github.com/Carthage/Carthage#if-youre-building-for-os-x)). 31 | 32 | ## Usage 33 | 34 | See the [API.md](API.md) file, there is a description of data collected by SystemMonitor and how to use it. 35 | 36 | Note that you need to disable Application Sandboxing in the Xcode project. 37 | 38 | Furthermore in the test file [SystemMonitorTests.swift](SystemMonitorTests/SystemMonitorTests.swift) is a function call **testNiceDisplay** that display the following output, you can use it to inspirate yourself. 39 | 40 | ``` 41 | System Infos: 42 | Hostname: MacBookPro 43 | Kernel: Darwin 44 | Kernel Version: 18.0.0 45 | Boottime: 2018-08-10 22:14:25 +0000 46 | Processor/CPU Infos: 47 | Core number: 8 48 | Model: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz 49 | CPU Features: ["FPU", "VME", "DE", "PSE", "TSC", "MSR", "PAE", "MCE", "CX8", "APIC", "SEP", "MTRR", "PGE", "MCA", "CMOV", "PAT", "PSE36", "CLFSH", "DS", "ACPI", "MMX", "FXSR", "SSE", "SSE2","SS", "HTT", "TM", "PBE", "SSE3", "PCLMULQDQ", "DTES64", "MON", "DSCPL", "VMX", "EST", "TM2", "SSSE3", "FMA", "CX16", "TPR", "PDCM", "SSE4.1", "SSE4.2", "x2APIC", "MOVBE", "POPCNT", "AES", "PCID", "XSAVE", "OSXSAVE", "SEGLIM64", "TSCTMR", "AVX1.0", "RDRAND", "F16C"] 50 | CPU Usage: User System Idle Nice 51 | CPU0: 18.8% 15.4% 65.8% 0.0% (1135580, 934299, 3986371, 0) 52 | CPU1: 2.0% 1.8% 96.2% 0.0% (118735, 109779, 5827165, 0) 53 | CPU2: 16.4% 10.8% 72.8% 0.0% (990105, 656607, 4408970, 0) 54 | CPU3: 2.0% 1.6% 96.4% 0.0% (121302, 97873, 5836502, 0) 55 | CPU4: 14.4% 8.8% 76.8% 0.0% (871645, 534731, 4649304, 0) 56 | CPU5: 2.1% 1.5% 96.4% 0.0% (124725, 90699, 5840253, 0) 57 | CPU6: 12.1% 7.1% 80.8% 0.0% (733247, 427107, 4895326, 0) 58 | CPU7: 2.1% 1.4% 96.5% 0.0% (129104, 83747, 5842824, 0) 59 | Total: 8.7% 6.1% 85.2% 0.0% (4224443, 2934842, 41286715, 0) 60 | Memory Infos: 61 | RAM Usage: 62 | Active: 5.07 GB ( 1329325 pages ) 63 | Wired: 2.87 GB ( 751900 pages ) 64 | Application: 5.59 GB ( 1464431 pages ) 65 | Compressed: 2.81 GB ( 737906 pages ) 66 | Availablbe: 5.24 GB ( 1374820 pages ) 67 | Swap Usage: 68 | Total: 1.0 GB ( 1073741824 bytes ) 69 | Used: 0.03 GB ( 33554432 bytes ) 70 | Free: 0.97 GB ( 1040187392 bytes ) 71 | Disks/Volumes Infos: 72 | Volumes Infos: 73 | /dev/disk2s2 [hfs] /Volumes/Sauvegarde 58.07GB available on 372.53GB (58.07GB free) 74 | /dev/disk1s1 [apfs] / 21.4GB available on 233.47GB (28.05GB free) 75 | /dev/disk2s3 [exfat] /Volumes/Données 187.68GB available on 558.52GB (187.68GB free) 76 | Disks Infos: 77 | disk0 233.76GB (read: 35.04GB, writen: 41.76GB) 78 | disk2 931.51GB (read: 1.65GB, writen: 5.25GB) 79 | Network Infos: 80 | Interface lo0 (send: 1444864, received: 1444864) 81 | [ipv4] 127.0.0.1 255.0.0.0 127.0.0.1 82 | [ipv6] ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ::1 83 | [ipv6] fe80::1%lo0 ffff:ffff:ffff:ffff:: 84 | Interface gif0 (send: 0, received: 0) 85 | Interface stf0 (send: 0, received: 0) 86 | Interface XHC1 (send: 0, received: 0) 87 | Interface XHC20 (send: 0, received: 0) 88 | Interface XHC0 (send: 0, received: 0) 89 | Interface en0 (send: 726126592, received: 637428736) 90 | [ether] 78:4f:43:7d:f9:fb 91 | Interface p2p0 (send: 0, received: 0) 92 | Interface awdl0 (send: 1024, received: 0) 93 | Interface en3 (send: 0, received: 0) 94 | [ether] 62:00:f1:70:6b:01 95 | Interface en1 (send: 0, received: 0) 96 | [ether] 62:00:f1:70:6b:00 97 | Interface en4 (send: 0, received: 0) 98 | [ether] 62:00:f1:70:6b:05 99 | Interface en2 (send: 0, received: 0) 100 | [ether] 62:00:f1:70:6b:04 101 | Interface bridge0 (send: 0, received: 0) 102 | Interface utun0 (send: 0, received: 0) 103 | [ipv6] fe80::70a3:5717:dc05:d917%utun0 ffff:ffff:ffff:ffff:: 104 | Interface utun1 (send: 0, received: 0) 105 | [ipv6] fe80::957:2672:7b62:49c5%utun1 ffff:ffff:ffff:ffff:: 106 | Interface en5 (send: 57344, received: 397312) 107 | [ether] ac:de:48:00:11:22 108 | [ipv6] fe80::aede:48ff:fe00:1122%en5 ffff:ffff:ffff:ffff:: 109 | Interface en8 (send: 445117440, received: 1055318016) 110 | [ether] 9c:eb:e8:48:f9:d3 111 | [ipv6] fe80::1884:5522:fed2:35d8%en8 ffff:ffff:ffff:ffff:: 112 | [ipv4] 129.12.131.172 255.255.252.0 129.12.131.255 113 | Graphics/GPU Infos: 114 | AMD Radeon Pro 450 [OFF] 115 | Intel HD Graphics 530 [ON] 116 | 18% core (@0MHz), 0MB free VRAM of 1536MB (@0MHz), 0°C 117 | Battery Infos: 118 | Serial number: D867046B0QFHDWCAA 119 | Manufacture date: 2017-01-29 00:00:00 +0000 120 | Cycle count: 251 121 | Design capacity: 6669 122 | Maximum capacity: 6359 (95.35% of design capacity) 123 | Current capacity: 6293 (98.96% of maximum capacity) 124 | Usage: 13014mV 598mA 7.78W 125 | Charging: true @ 28.67W (fully charged: true) 126 | Time remaining: 0 hours 8 minutes 127 | Sensors Infos: 128 | Fans: 129 | 2228.0 RPM (min: 2000.0, max: 5489.0) 130 | 2408.0 RPM (min: 2160.0, max: 5927.0) 131 | Temperatures: 132 | TA0V: 26.3°C 133 | TM0P: 55.6°C 134 | TTRD: 53.1°C 135 | TG0D: 35.0°C 136 | TBXT: 37.4°C 137 | TGVP: 48.8°C 138 | TPCD: 44.0°C 139 | TH0c: 46.3°C 140 | TC0E: 69.1°C 141 | TB2T: 37.4°C 142 | TaRC: 48.7°C 143 | TG0F: 35.0°C 144 | TTLD: 48.8°C 145 | Th2H: 56.5°C 146 | TC0P: 48.4°C 147 | TH0C: 46.3°C 148 | TH0a: 46.9°C 149 | TC2C: 68.0°C 150 | Ts0P: 33.9°C 151 | TC3C: 69.0°C 152 | TG0P: 55.2°C 153 | TB1T: 35.1°C 154 | TCTD: 0.3°C 155 | TaLC: 38.4°C 156 | TCGC: 64.0°C 157 | Ts1P: 30.7°C 158 | TC4C: 67.0°C 159 | TGDD: 59.0°C 160 | TCXC: 68.8°C 161 | TCSA: 66.0°C 162 | TH0B: 44.5°C 163 | Th1H: 58.9°C 164 | TC0F: 70.4°C 165 | Ts2S: 43.4°C 166 | Ts0S: 40.7°C 167 | TH0b: 44.5°C 168 | TB0T: 37.4°C 169 | TW0P: 54.3°C 170 | Ts1S: 42.9°C 171 | TH0A: 46.9°C 172 | TC1C: 67.0°C 173 | Voltage: 174 | VCSC: 0.91V 175 | VD0R: 19.84V 176 | VCAC: 0.97V 177 | VP0R: 13.0V 178 | VCTC: 0.61V 179 | Amperages: 180 | ID0R: 2.3A 181 | IPBR: 0.12A 182 | IC0R: 1.79A 183 | IG0R: 0.01A 184 | IBSC: 0.02A 185 | Wattages: 186 | PZ2E: 13.0W 187 | PZ2F: 0.12W 188 | PHPC: 23.32W 189 | PPBR: 1.62W 190 | PDTR: 45.62W 191 | PC0R: 23.24W 192 | PG0R: 0.08W 193 | PZ1E: 45.0W 194 | PZ1G: 0.08W 195 | PZ3G: 0.02W 196 | PZ0E: 70.0W 197 | PZ2G: 0.12W 198 | PZ1F: 0.08W 199 | PZ3F: 0.02W 200 | PZ3E: 135.49W 201 | PZ0G: 23.32W 202 | PZ0F: 21.99W 203 | ``` 204 | 205 | ## Tests 206 | 207 | Open the project in Xcode and run tests. 208 | 209 | You can also use the Makefile to run tests with: 210 | 211 | ```makefile 212 | make test 213 | ``` 214 | 215 | ## License 216 | 217 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details 218 | 219 | ## Acknowledgments 220 | 221 | Special thanks to https://github.com/beltex/ for his work which inspired me a lot ! 222 | 223 | Thanks to these projects: 224 | 225 | - https://github.com/beltex/SystemKit 226 | - https://github.com/beltex/SMCKit 227 | - https://github.com/hholtmann/smcFanControl/tree/master/smc-command 228 | - https://github.com/theopolis/smc-fuzzer 229 | - https://github.com/xythobuz/JSystemInfoKit 230 | 231 | Apple useful open-source code: 232 | 233 | - https://opensource.apple.com/source/PowerManagement/PowerManagement-703.30.3/ 234 | - https://opensource.apple.com/source/top/top-111.20.1/ 235 | - https://github.com/apple/darwin-xnu/tree/master/iokit/IOKit 236 | - https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/sysctl.h.auto.html 237 | - https://opensource.apple.com/source/xnu/xnu-4570.1.46/osfmk/mach/vm_statistics.h.auto.html 238 | 239 | Useful topics: 240 | 241 | - https://stackoverflow.com/questions/44744372/get-cpu-usage-ios-swift 242 | - https://apple.stackexchange.com/questions/16102/how-to-retrieve-current-wattage-info-on-os-x 243 | - https://www.exploit-db.com/exploits/40952/ 244 | - https://stackoverflow.com/questions/3887309/mapping-iokit-ioreturn-error-code-to-string 245 | - https://stackoverflow.com/questions/10110658/programmatically-get-gpu-percent-usage-in-os-x 246 | - https://stackoverflow.com/questions/18077639/getting-graphic-card-information-in-objective-c 247 | 248 | Other resources: 249 | 250 | - http://web.mit.edu/darwin/src/modules/xnu/osfmk/man/host_statistics.html 251 | - http://man7.org/linux/man-pages/man3/getifaddrs.3.html 252 | - `/usr/include/net/if.h` and `/usr/include/sys/socket.h` from macOS 10.14 beta 5 -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C4229E0C20ED284D00D8CE13 /* DiskHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4229E0B20ED284D00D8CE13 /* DiskHandler.swift */; }; 11 | C4262F2820E0091B001FDF22 /* SystemMonitor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C4262F1E20E0091B001FDF22 /* SystemMonitor.framework */; }; 12 | C4262F2D20E0091B001FDF22 /* SystemMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4262F2C20E0091B001FDF22 /* SystemMonitorTests.swift */; }; 13 | C4262F2F20E0091B001FDF22 /* SystemMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = C4262F2120E0091B001FDF22 /* SystemMonitor.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | C462FDD620E035A000C6F9C5 /* SystemMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = C462FDD520E035A000C6F9C5 /* SystemMonitor.swift */; }; 15 | C462FDD820E0385200C6F9C5 /* MemoryHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C462FDD720E0385200C6F9C5 /* MemoryHandler.swift */; }; 16 | C462FDDA20E0398300C6F9C5 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C462FDD920E0398300C6F9C5 /* Utils.swift */; }; 17 | C463355520FE729200E82CE5 /* NetworkHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C463355420FE729200E82CE5 /* NetworkHandler.swift */; }; 18 | C48231F6210656A1000619F8 /* GPUHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48231F5210656A1000619F8 /* GPUHandler.swift */; }; 19 | C48F837D210E4504008BE0E5 /* BatteryHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48F837C210E4504008BE0E5 /* BatteryHandler.swift */; }; 20 | C4963F6A210F252800524C72 /* SensorsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4963F69210F252800524C72 /* SensorsHandler.swift */; }; 21 | C4D1B0712108571E004E91FC /* SystemHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D1B0702108571E004E91FC /* SystemHandler.swift */; }; 22 | C4EE986820E64FA60077D90C /* ProcessorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4EE986720E64FA60077D90C /* ProcessorHandler.swift */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXContainerItemProxy section */ 26 | C4262F2920E0091B001FDF22 /* PBXContainerItemProxy */ = { 27 | isa = PBXContainerItemProxy; 28 | containerPortal = C4262F1520E0091B001FDF22 /* Project object */; 29 | proxyType = 1; 30 | remoteGlobalIDString = C4262F1D20E0091B001FDF22; 31 | remoteInfo = SystemMonitor; 32 | }; 33 | /* End PBXContainerItemProxy section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | C4229E0B20ED284D00D8CE13 /* DiskHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskHandler.swift; sourceTree = ""; }; 37 | C4262F1E20E0091B001FDF22 /* SystemMonitor.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SystemMonitor.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | C4262F2120E0091B001FDF22 /* SystemMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SystemMonitor.h; sourceTree = ""; }; 39 | C4262F2220E0091B001FDF22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | C4262F2720E0091B001FDF22 /* SystemMonitorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SystemMonitorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | C4262F2C20E0091B001FDF22 /* SystemMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitorTests.swift; sourceTree = ""; }; 42 | C4262F2E20E0091B001FDF22 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | C462FDD520E035A000C6F9C5 /* SystemMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemMonitor.swift; sourceTree = ""; }; 44 | C462FDD720E0385200C6F9C5 /* MemoryHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryHandler.swift; sourceTree = ""; }; 45 | C462FDD920E0398300C6F9C5 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 46 | C463355420FE729200E82CE5 /* NetworkHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkHandler.swift; sourceTree = ""; }; 47 | C48231F5210656A1000619F8 /* GPUHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GPUHandler.swift; sourceTree = ""; }; 48 | C48F837C210E4504008BE0E5 /* BatteryHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryHandler.swift; sourceTree = ""; }; 49 | C4963F69210F252800524C72 /* SensorsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensorsHandler.swift; sourceTree = ""; }; 50 | C4D1B0702108571E004E91FC /* SystemHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemHandler.swift; sourceTree = ""; }; 51 | C4EE986720E64FA60077D90C /* ProcessorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessorHandler.swift; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | C4262F1B20E0091B001FDF22 /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | ); 60 | runOnlyForDeploymentPostprocessing = 0; 61 | }; 62 | C4262F2420E0091B001FDF22 /* Frameworks */ = { 63 | isa = PBXFrameworksBuildPhase; 64 | buildActionMask = 2147483647; 65 | files = ( 66 | C4262F2820E0091B001FDF22 /* SystemMonitor.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | C4262F1420E0091B001FDF22 = { 74 | isa = PBXGroup; 75 | children = ( 76 | C4262F2020E0091B001FDF22 /* SystemMonitor */, 77 | C4262F2B20E0091B001FDF22 /* SystemMonitorTests */, 78 | C4262F1F20E0091B001FDF22 /* Products */, 79 | ); 80 | sourceTree = ""; 81 | }; 82 | C4262F1F20E0091B001FDF22 /* Products */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | C4262F1E20E0091B001FDF22 /* SystemMonitor.framework */, 86 | C4262F2720E0091B001FDF22 /* SystemMonitorTests.xctest */, 87 | ); 88 | name = Products; 89 | sourceTree = ""; 90 | }; 91 | C4262F2020E0091B001FDF22 /* SystemMonitor */ = { 92 | isa = PBXGroup; 93 | children = ( 94 | C4262F2120E0091B001FDF22 /* SystemMonitor.h */, 95 | C4262F2220E0091B001FDF22 /* Info.plist */, 96 | C462FDD520E035A000C6F9C5 /* SystemMonitor.swift */, 97 | C462FDD720E0385200C6F9C5 /* MemoryHandler.swift */, 98 | C462FDD920E0398300C6F9C5 /* Utils.swift */, 99 | C4EE986720E64FA60077D90C /* ProcessorHandler.swift */, 100 | C4229E0B20ED284D00D8CE13 /* DiskHandler.swift */, 101 | C463355420FE729200E82CE5 /* NetworkHandler.swift */, 102 | C48231F5210656A1000619F8 /* GPUHandler.swift */, 103 | C4D1B0702108571E004E91FC /* SystemHandler.swift */, 104 | C48F837C210E4504008BE0E5 /* BatteryHandler.swift */, 105 | C4963F69210F252800524C72 /* SensorsHandler.swift */, 106 | ); 107 | path = SystemMonitor; 108 | sourceTree = ""; 109 | }; 110 | C4262F2B20E0091B001FDF22 /* SystemMonitorTests */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | C4262F2C20E0091B001FDF22 /* SystemMonitorTests.swift */, 114 | C4262F2E20E0091B001FDF22 /* Info.plist */, 115 | ); 116 | path = SystemMonitorTests; 117 | sourceTree = ""; 118 | }; 119 | /* End PBXGroup section */ 120 | 121 | /* Begin PBXHeadersBuildPhase section */ 122 | C4262F1920E0091B001FDF22 /* Headers */ = { 123 | isa = PBXHeadersBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | C4262F2F20E0091B001FDF22 /* SystemMonitor.h in Headers */, 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | /* End PBXHeadersBuildPhase section */ 131 | 132 | /* Begin PBXNativeTarget section */ 133 | C4262F1D20E0091B001FDF22 /* SystemMonitor */ = { 134 | isa = PBXNativeTarget; 135 | buildConfigurationList = C4262F3220E0091B001FDF22 /* Build configuration list for PBXNativeTarget "SystemMonitor" */; 136 | buildPhases = ( 137 | C4262F1920E0091B001FDF22 /* Headers */, 138 | C4262F1A20E0091B001FDF22 /* Sources */, 139 | C4262F1B20E0091B001FDF22 /* Frameworks */, 140 | C4262F1C20E0091B001FDF22 /* Resources */, 141 | ); 142 | buildRules = ( 143 | ); 144 | dependencies = ( 145 | ); 146 | name = SystemMonitor; 147 | productName = SystemMonitor; 148 | productReference = C4262F1E20E0091B001FDF22 /* SystemMonitor.framework */; 149 | productType = "com.apple.product-type.framework"; 150 | }; 151 | C4262F2620E0091B001FDF22 /* SystemMonitorTests */ = { 152 | isa = PBXNativeTarget; 153 | buildConfigurationList = C4262F3520E0091B001FDF22 /* Build configuration list for PBXNativeTarget "SystemMonitorTests" */; 154 | buildPhases = ( 155 | C4262F2320E0091B001FDF22 /* Sources */, 156 | C4262F2420E0091B001FDF22 /* Frameworks */, 157 | C4262F2520E0091B001FDF22 /* Resources */, 158 | ); 159 | buildRules = ( 160 | ); 161 | dependencies = ( 162 | C4262F2A20E0091B001FDF22 /* PBXTargetDependency */, 163 | ); 164 | name = SystemMonitorTests; 165 | productName = SystemMonitorTests; 166 | productReference = C4262F2720E0091B001FDF22 /* SystemMonitorTests.xctest */; 167 | productType = "com.apple.product-type.bundle.unit-test"; 168 | }; 169 | /* End PBXNativeTarget section */ 170 | 171 | /* Begin PBXProject section */ 172 | C4262F1520E0091B001FDF22 /* Project object */ = { 173 | isa = PBXProject; 174 | attributes = { 175 | LastSwiftUpdateCheck = 1000; 176 | LastUpgradeCheck = 1000; 177 | ORGANIZATIONNAME = "Jacques Lorentz"; 178 | TargetAttributes = { 179 | C4262F1D20E0091B001FDF22 = { 180 | CreatedOnToolsVersion = 10.0; 181 | LastSwiftMigration = 1000; 182 | }; 183 | C4262F2620E0091B001FDF22 = { 184 | CreatedOnToolsVersion = 10.0; 185 | }; 186 | }; 187 | }; 188 | buildConfigurationList = C4262F1820E0091B001FDF22 /* Build configuration list for PBXProject "SystemMonitor" */; 189 | compatibilityVersion = "Xcode 9.3"; 190 | developmentRegion = en; 191 | hasScannedForEncodings = 0; 192 | knownRegions = ( 193 | en, 194 | ); 195 | mainGroup = C4262F1420E0091B001FDF22; 196 | productRefGroup = C4262F1F20E0091B001FDF22 /* Products */; 197 | projectDirPath = ""; 198 | projectRoot = ""; 199 | targets = ( 200 | C4262F1D20E0091B001FDF22 /* SystemMonitor */, 201 | C4262F2620E0091B001FDF22 /* SystemMonitorTests */, 202 | ); 203 | }; 204 | /* End PBXProject section */ 205 | 206 | /* Begin PBXResourcesBuildPhase section */ 207 | C4262F1C20E0091B001FDF22 /* Resources */ = { 208 | isa = PBXResourcesBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | ); 212 | runOnlyForDeploymentPostprocessing = 0; 213 | }; 214 | C4262F2520E0091B001FDF22 /* Resources */ = { 215 | isa = PBXResourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | C4262F1A20E0091B001FDF22 /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | C48231F6210656A1000619F8 /* GPUHandler.swift in Sources */, 229 | C4D1B0712108571E004E91FC /* SystemHandler.swift in Sources */, 230 | C462FDD620E035A000C6F9C5 /* SystemMonitor.swift in Sources */, 231 | C48F837D210E4504008BE0E5 /* BatteryHandler.swift in Sources */, 232 | C4229E0C20ED284D00D8CE13 /* DiskHandler.swift in Sources */, 233 | C462FDD820E0385200C6F9C5 /* MemoryHandler.swift in Sources */, 234 | C462FDDA20E0398300C6F9C5 /* Utils.swift in Sources */, 235 | C4EE986820E64FA60077D90C /* ProcessorHandler.swift in Sources */, 236 | C4963F6A210F252800524C72 /* SensorsHandler.swift in Sources */, 237 | C463355520FE729200E82CE5 /* NetworkHandler.swift in Sources */, 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | }; 241 | C4262F2320E0091B001FDF22 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | C4262F2D20E0091B001FDF22 /* SystemMonitorTests.swift in Sources */, 246 | ); 247 | runOnlyForDeploymentPostprocessing = 0; 248 | }; 249 | /* End PBXSourcesBuildPhase section */ 250 | 251 | /* Begin PBXTargetDependency section */ 252 | C4262F2A20E0091B001FDF22 /* PBXTargetDependency */ = { 253 | isa = PBXTargetDependency; 254 | target = C4262F1D20E0091B001FDF22 /* SystemMonitor */; 255 | targetProxy = C4262F2920E0091B001FDF22 /* PBXContainerItemProxy */; 256 | }; 257 | /* End PBXTargetDependency section */ 258 | 259 | /* Begin XCBuildConfiguration section */ 260 | C4262F3020E0091B001FDF22 /* Debug */ = { 261 | isa = XCBuildConfiguration; 262 | buildSettings = { 263 | ALWAYS_SEARCH_USER_PATHS = NO; 264 | CLANG_ANALYZER_NONNULL = YES; 265 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_ENABLE_OBJC_WEAK = YES; 271 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 272 | CLANG_WARN_BOOL_CONVERSION = YES; 273 | CLANG_WARN_COMMA = YES; 274 | CLANG_WARN_CONSTANT_CONVERSION = YES; 275 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 276 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 277 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 278 | CLANG_WARN_EMPTY_BODY = YES; 279 | CLANG_WARN_ENUM_CONVERSION = YES; 280 | CLANG_WARN_INFINITE_RECURSION = YES; 281 | CLANG_WARN_INT_CONVERSION = YES; 282 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 283 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 290 | CLANG_WARN_UNREACHABLE_CODE = YES; 291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 292 | CODE_SIGN_IDENTITY = "Mac Developer"; 293 | COPY_PHASE_STRIP = NO; 294 | CURRENT_PROJECT_VERSION = 1; 295 | DEBUG_INFORMATION_FORMAT = dwarf; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | ENABLE_TESTABILITY = YES; 298 | GCC_C_LANGUAGE_STANDARD = gnu11; 299 | GCC_DYNAMIC_NO_PIC = NO; 300 | GCC_NO_COMMON_BLOCKS = YES; 301 | GCC_OPTIMIZATION_LEVEL = 0; 302 | GCC_PREPROCESSOR_DEFINITIONS = ( 303 | "DEBUG=1", 304 | "$(inherited)", 305 | ); 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | MACOSX_DEPLOYMENT_TARGET = 10.14; 313 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 314 | ONLY_ACTIVE_ARCH = YES; 315 | SDKROOT = macosx; 316 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 317 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 318 | VERSIONING_SYSTEM = "apple-generic"; 319 | VERSION_INFO_PREFIX = ""; 320 | }; 321 | name = Debug; 322 | }; 323 | C4262F3120E0091B001FDF22 /* Release */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | ALWAYS_SEARCH_USER_PATHS = NO; 327 | CLANG_ANALYZER_NONNULL = YES; 328 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 330 | CLANG_CXX_LIBRARY = "libc++"; 331 | CLANG_ENABLE_MODULES = YES; 332 | CLANG_ENABLE_OBJC_ARC = YES; 333 | CLANG_ENABLE_OBJC_WEAK = YES; 334 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 335 | CLANG_WARN_BOOL_CONVERSION = YES; 336 | CLANG_WARN_COMMA = YES; 337 | CLANG_WARN_CONSTANT_CONVERSION = YES; 338 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 340 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 341 | CLANG_WARN_EMPTY_BODY = YES; 342 | CLANG_WARN_ENUM_CONVERSION = YES; 343 | CLANG_WARN_INFINITE_RECURSION = YES; 344 | CLANG_WARN_INT_CONVERSION = YES; 345 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 346 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 347 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 348 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 349 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 350 | CLANG_WARN_STRICT_PROTOTYPES = YES; 351 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 352 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 353 | CLANG_WARN_UNREACHABLE_CODE = YES; 354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 355 | CODE_SIGN_IDENTITY = "Mac Developer"; 356 | COPY_PHASE_STRIP = NO; 357 | CURRENT_PROJECT_VERSION = 1; 358 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 359 | ENABLE_NS_ASSERTIONS = NO; 360 | ENABLE_STRICT_OBJC_MSGSEND = YES; 361 | GCC_C_LANGUAGE_STANDARD = gnu11; 362 | GCC_NO_COMMON_BLOCKS = YES; 363 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 364 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 365 | GCC_WARN_UNDECLARED_SELECTOR = YES; 366 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 367 | GCC_WARN_UNUSED_FUNCTION = YES; 368 | GCC_WARN_UNUSED_VARIABLE = YES; 369 | MACOSX_DEPLOYMENT_TARGET = 10.14; 370 | MTL_ENABLE_DEBUG_INFO = NO; 371 | SDKROOT = macosx; 372 | SWIFT_COMPILATION_MODE = wholemodule; 373 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 374 | VERSIONING_SYSTEM = "apple-generic"; 375 | VERSION_INFO_PREFIX = ""; 376 | }; 377 | name = Release; 378 | }; 379 | C4262F3320E0091B001FDF22 /* Debug */ = { 380 | isa = XCBuildConfiguration; 381 | buildSettings = { 382 | CLANG_ENABLE_MODULES = YES; 383 | CODE_SIGN_IDENTITY = "Mac Developer"; 384 | CODE_SIGN_STYLE = Automatic; 385 | COMBINE_HIDPI_IMAGES = YES; 386 | DEFINES_MODULE = YES; 387 | DEVELOPMENT_TEAM = 5S63222JT6; 388 | DYLIB_COMPATIBILITY_VERSION = 1; 389 | DYLIB_CURRENT_VERSION = 1; 390 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 391 | FRAMEWORK_VERSION = A; 392 | INFOPLIST_FILE = SystemMonitor/Info.plist; 393 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 394 | LD_RUNPATH_SEARCH_PATHS = ( 395 | "$(inherited)", 396 | "@executable_path/../Frameworks", 397 | "@loader_path/Frameworks", 398 | ); 399 | PRODUCT_BUNDLE_IDENTIFIER = com.jacqueslorentz.SystemMonitor; 400 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 401 | PROVISIONING_PROFILE_SPECIFIER = ""; 402 | SKIP_INSTALL = YES; 403 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 404 | SWIFT_VERSION = 4.2; 405 | }; 406 | name = Debug; 407 | }; 408 | C4262F3420E0091B001FDF22 /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | buildSettings = { 411 | CLANG_ENABLE_MODULES = YES; 412 | CODE_SIGN_IDENTITY = "Mac Developer"; 413 | CODE_SIGN_STYLE = Automatic; 414 | COMBINE_HIDPI_IMAGES = YES; 415 | DEFINES_MODULE = YES; 416 | DEVELOPMENT_TEAM = 5S63222JT6; 417 | DYLIB_COMPATIBILITY_VERSION = 1; 418 | DYLIB_CURRENT_VERSION = 1; 419 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 420 | FRAMEWORK_VERSION = A; 421 | INFOPLIST_FILE = SystemMonitor/Info.plist; 422 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 423 | LD_RUNPATH_SEARCH_PATHS = ( 424 | "$(inherited)", 425 | "@executable_path/../Frameworks", 426 | "@loader_path/Frameworks", 427 | ); 428 | PRODUCT_BUNDLE_IDENTIFIER = com.jacqueslorentz.SystemMonitor; 429 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 430 | PROVISIONING_PROFILE_SPECIFIER = ""; 431 | SKIP_INSTALL = YES; 432 | SWIFT_VERSION = 4.2; 433 | }; 434 | name = Release; 435 | }; 436 | C4262F3620E0091B001FDF22 /* Debug */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 440 | CODE_SIGN_STYLE = Automatic; 441 | COMBINE_HIDPI_IMAGES = YES; 442 | DEVELOPMENT_TEAM = 5S63222JT6; 443 | INFOPLIST_FILE = SystemMonitorTests/Info.plist; 444 | LD_RUNPATH_SEARCH_PATHS = ( 445 | "$(inherited)", 446 | "@executable_path/../Frameworks", 447 | "@loader_path/../Frameworks", 448 | ); 449 | PRODUCT_BUNDLE_IDENTIFIER = com.jacqueslorentz.SystemMonitorTests; 450 | PRODUCT_NAME = "$(TARGET_NAME)"; 451 | SWIFT_VERSION = 4.2; 452 | }; 453 | name = Debug; 454 | }; 455 | C4262F3720E0091B001FDF22 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 459 | CODE_SIGN_STYLE = Automatic; 460 | COMBINE_HIDPI_IMAGES = YES; 461 | DEVELOPMENT_TEAM = 5S63222JT6; 462 | INFOPLIST_FILE = SystemMonitorTests/Info.plist; 463 | LD_RUNPATH_SEARCH_PATHS = ( 464 | "$(inherited)", 465 | "@executable_path/../Frameworks", 466 | "@loader_path/../Frameworks", 467 | ); 468 | PRODUCT_BUNDLE_IDENTIFIER = com.jacqueslorentz.SystemMonitorTests; 469 | PRODUCT_NAME = "$(TARGET_NAME)"; 470 | SWIFT_VERSION = 4.2; 471 | }; 472 | name = Release; 473 | }; 474 | /* End XCBuildConfiguration section */ 475 | 476 | /* Begin XCConfigurationList section */ 477 | C4262F1820E0091B001FDF22 /* Build configuration list for PBXProject "SystemMonitor" */ = { 478 | isa = XCConfigurationList; 479 | buildConfigurations = ( 480 | C4262F3020E0091B001FDF22 /* Debug */, 481 | C4262F3120E0091B001FDF22 /* Release */, 482 | ); 483 | defaultConfigurationIsVisible = 0; 484 | defaultConfigurationName = Release; 485 | }; 486 | C4262F3220E0091B001FDF22 /* Build configuration list for PBXNativeTarget "SystemMonitor" */ = { 487 | isa = XCConfigurationList; 488 | buildConfigurations = ( 489 | C4262F3320E0091B001FDF22 /* Debug */, 490 | C4262F3420E0091B001FDF22 /* Release */, 491 | ); 492 | defaultConfigurationIsVisible = 0; 493 | defaultConfigurationName = Release; 494 | }; 495 | C4262F3520E0091B001FDF22 /* Build configuration list for PBXNativeTarget "SystemMonitorTests" */ = { 496 | isa = XCConfigurationList; 497 | buildConfigurations = ( 498 | C4262F3620E0091B001FDF22 /* Debug */, 499 | C4262F3720E0091B001FDF22 /* Release */, 500 | ); 501 | defaultConfigurationIsVisible = 0; 502 | defaultConfigurationName = Release; 503 | }; 504 | /* End XCConfigurationList section */ 505 | }; 506 | rootObject = C4262F1520E0091B001FDF22 /* Project object */; 507 | } 508 | -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/project.xcworkspace/xcuserdata/jacques.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jacqueslorentz/SystemMonitor/287d7b5250bd8fab50422446d17fdff745e3f896/SystemMonitor.xcodeproj/project.xcworkspace/xcuserdata/jacques.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/xcshareddata/xcschemes/SystemMonitor.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /SystemMonitor.xcodeproj/xcuserdata/jacques.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | SystemMonitor.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SystemMonitor/BatteryHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BatteryHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 29/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct BatteryInfos { 32 | public let serialNumber: String 33 | public let manufactureDate: Date 34 | public let cycleCount: Int 35 | 36 | public let designCapacity: Int 37 | public let maxCapacity: Int 38 | public let currentCapacity: Int 39 | public let voltage: Int 40 | public let amperage: Int 41 | public let instantAmperage: Int 42 | 43 | public let timeRemaining: Int 44 | public let timeToFull: Int 45 | public let timeToEmpty: Int 46 | public let isCharging: Bool 47 | public let isFullyCharged: Bool 48 | public let chargingCurrent: Int 49 | } 50 | 51 | struct BatteryHandler { 52 | static func getBatteryInfos() throws -> BatteryInfos { 53 | let data = try getIOProperties(ioClassname: "IOPMPowerSource") 54 | if (data.count == 0) { 55 | throw SystemMonitorError.IOKitError(error: "No battery found") 56 | } 57 | let battery = data.first! 58 | guard let chargerData = battery["ChargerData"] as? [String:Int] else { 59 | throw SystemMonitorError.IOKitError(error: "No battery charger infos") 60 | } 61 | let datecode = battery["ManufactureDate"] as! Int 62 | let formatter = ISO8601DateFormatter() 63 | formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] 64 | let date = formatter.date(from: String((datecode >> 9) + 1980) + "-" + String((datecode >> 5) & 0xF) + "-" + String(datecode & 0x1F) + "T00:00:00.000Z")! 65 | return BatteryInfos( 66 | serialNumber: battery["BatterySerialNumber"] as? String ?? "", 67 | manufactureDate: date, 68 | cycleCount: battery["CycleCount"] as? Int ?? 0, 69 | designCapacity: battery["DesignCapacity"] as? Int ?? 0, 70 | maxCapacity: battery["MaxCapacity"] as? Int ?? 0, 71 | currentCapacity: battery["CurrentCapacity"] as? Int ?? 0, 72 | voltage: battery["Voltage"] as? Int ?? 0, 73 | amperage: battery["Amperage"] as? Int ?? 0, 74 | instantAmperage: battery["InstantAmperage"] as? Int ?? 0, 75 | timeRemaining: battery["TimeRemaining"] as? Int ?? 0, 76 | timeToFull: battery["AvgTimeToFull"] as? Int ?? 65535, 77 | timeToEmpty: battery["AvgTimeToEmpty"] as? Int ?? 65535, 78 | isCharging: battery["IsCharging"] as? Bool ?? false, 79 | isFullyCharged: battery["IsCharging"] as? Bool ?? false, 80 | chargingCurrent: chargerData["ChargingCurrent"] ?? 0 81 | ) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /SystemMonitor/DiskHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DiskHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 04/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import IOKit 30 | 31 | public struct VolumesDisksInfos { 32 | public let volumes: [VolumeInfos] 33 | public let disks: [DiskInfos] 34 | } 35 | 36 | public struct ConvertedVolumeUsage { 37 | public let total: Float 38 | public let free: Float 39 | public let available: Float 40 | public let unit: String 41 | } 42 | 43 | // In blocks 44 | public struct VolumeUsage { 45 | public let blocksize: UInt32 46 | public let iosize: Int32 47 | public let blocks: UInt64 48 | public let free: UInt64 49 | public let available: UInt64 50 | public let files: UInt64 51 | public let filesfree: UInt64 52 | 53 | public func convertTo(unit: String) throws -> ConvertedVolumeUsage { 54 | let pageSize: UInt64 = UInt64(blocksize) 55 | let mult = try getBytesConversionMult(unit: unit) 56 | return ConvertedVolumeUsage( 57 | total: (Float)(self.blocks * pageSize) / mult, 58 | free: (Float)(self.free * pageSize) / mult, 59 | available: (Float)(self.available * pageSize) / mult, 60 | unit: unit 61 | ) 62 | } 63 | } 64 | 65 | public struct VolumeInfos { 66 | public let filesystem: String 67 | public let mountpoint: String 68 | public let mountname: String 69 | public let usage: VolumeUsage 70 | } 71 | 72 | public struct DiskUsage { 73 | public let bytesread: UInt64 74 | public let byteswritten: UInt64 75 | public let operationsread: UInt64 76 | public let operationswritten: UInt64 77 | } 78 | 79 | public struct DiskInfos { 80 | public let name: String 81 | public let blocksize: UInt32 82 | public let size: UInt64 83 | public let usage: DiskUsage 84 | } 85 | 86 | struct DiskHandler { 87 | static func getVolumesInfos() throws -> [VolumeInfos] { 88 | let fileURLs = try FileManager.default.contentsOfDirectory( 89 | at: URL(string: "file:///Volumes/")!, 90 | includingPropertiesForKeys: nil 91 | ) 92 | let allVolumes = try fileURLs.map { (file: URL) -> VolumeInfos in 93 | let infos = try getStatfsInfo(file: file) 94 | var localfstypename = infos.f_fstypename 95 | var localmntonname = infos.f_mntonname 96 | var localmntfromname = infos.f_mntfromname 97 | return VolumeInfos( 98 | filesystem: withUnsafePointer(to: &localfstypename) { 99 | $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: infos.f_fstypename)) { 100 | String(cString: $0) 101 | } 102 | }, 103 | mountpoint: withUnsafePointer(to: &localmntonname) { 104 | $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: infos.f_mntonname)) { 105 | String(cString: $0) 106 | } 107 | }, 108 | mountname: withUnsafePointer(to: &localmntfromname) { 109 | $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: infos.f_mntfromname)) { 110 | String(cString: $0) 111 | } 112 | }, 113 | usage: VolumeUsage( 114 | blocksize: infos.f_bsize, 115 | iosize: infos.f_iosize, 116 | blocks: infos.f_blocks, 117 | free: infos.f_bfree, 118 | available: infos.f_bavail, 119 | files: infos.f_files, 120 | filesfree: infos.f_ffree 121 | ) 122 | ) 123 | } 124 | var mountnames = [String]() 125 | return allVolumes.filter({ (volume: VolumeInfos) -> Bool in 126 | if (mountnames.firstIndex(of: volume.mountname) != nil) { 127 | return false 128 | } 129 | mountnames.append(volume.mountname) 130 | return true 131 | }) 132 | } 133 | 134 | static func getDisksInfos() throws -> [DiskInfos] { 135 | var list = [DiskInfos]() 136 | let drives = try getAllDrives() 137 | for drive in drives { 138 | let properties = try getProperties(drive: drive.parent) 139 | if (properties["Statistics"] == nil) { 140 | continue 141 | } 142 | let stat = properties["Statistics"] as! [String: Any] 143 | list.append(DiskInfos( 144 | name: drive.name, 145 | blocksize: drive.blockSize, 146 | size: drive.size, 147 | usage: DiskUsage( 148 | bytesread: stat["Bytes (Read)"] as? UInt64 ?? 0, 149 | byteswritten: stat["Bytes (Write)"] as? UInt64 ?? 0, 150 | operationsread: stat["Operations (Read)"] as? UInt64 ?? 0, 151 | operationswritten: stat["Operations (Write)"] as? UInt64 ?? 0 152 | ) 153 | )) 154 | } 155 | return list 156 | } 157 | } 158 | 159 | func getStatfsInfo(file: URL) throws -> statfs { 160 | let statsptr = UnsafeMutablePointer.allocate(capacity: 1) 161 | if (statfs(file.path, statsptr) != 0) { 162 | throw SystemMonitorError.statfsError(errno: stringErrno()) 163 | } 164 | let res = Array(UnsafeBufferPointer(start: statsptr, count: 1)) 165 | statsptr.deallocate() 166 | return res[0] 167 | } 168 | 169 | struct DriveStats { 170 | let parent: io_registry_entry_t 171 | let name: String 172 | let blockSize: UInt32 173 | let size: UInt64 174 | } 175 | 176 | func getProperties(drive: io_registry_entry_t) throws -> [String:Any] { 177 | var properties: Unmanaged? = nil 178 | if (IORegistryEntryCreateCFProperties(drive, &properties, kCFAllocatorDefault, 0) != KERN_SUCCESS) { 179 | throw SystemMonitorError.IOKitError(error: "Device has no properties") 180 | } 181 | guard let prop = properties?.takeUnretainedValue() as? [String:Any] else { 182 | throw SystemMonitorError.IOKitError(error: "Device has no properties") 183 | } 184 | properties?.release() 185 | return prop 186 | } 187 | 188 | func getDriveStats(drive: io_registry_entry_t) throws -> DriveStats { 189 | var parent = io_registry_entry_t() 190 | if (IORegistryEntryGetParentEntry(drive, kIOServicePlane, &parent) != KERN_SUCCESS) { 191 | throw SystemMonitorError.IOKitError(error: "Device has no parent") 192 | } 193 | if (IOObjectConformsTo(parent, "IOBlockStorageDriver") == 0) { 194 | IOObjectRelease(parent) 195 | throw SystemMonitorError.IOKitError(error: "Device driver not conform") 196 | } 197 | let properties = try getProperties(drive: drive) 198 | return DriveStats( 199 | parent: parent, 200 | name: properties[kIOBSDNameKey] != nil ? properties[kIOBSDNameKey] as! String : "", 201 | blockSize: properties["Preferred Block Size"] as? UInt32 ?? 0, 202 | size: properties["Size"] as? UInt64 ?? 0 203 | ) 204 | } 205 | 206 | func getAllDrives() throws -> [DriveStats] { 207 | var drives = [DriveStats]() 208 | guard var iomatch = IOServiceMatching("IOMedia") as? [String: Any] else { 209 | throw SystemMonitorError.IOKitError(error: "IOMedia") 210 | } 211 | iomatch.updateValue(kCFBooleanTrue, forKey: "Whole") 212 | var drivelist = io_iterator_t() 213 | if (IOServiceGetMatchingServices(kIOMasterPortDefault, iomatch as CFDictionary, &drivelist) != KERN_SUCCESS) { 214 | throw SystemMonitorError.IOKitError(error: "IOMedia") 215 | } 216 | for _ in 0 ... 10 { 217 | let drive = IOIteratorNext(drivelist) 218 | if (drive != io_object_t(0)) { 219 | do { 220 | drives.append(try getDriveStats(drive: drive)) 221 | } catch {} 222 | } 223 | IOObjectRelease(drive) 224 | } 225 | IOObjectRelease(drivelist) 226 | return drives 227 | } 228 | -------------------------------------------------------------------------------- /SystemMonitor/GPUHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPUHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 23/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import IOKit 30 | 31 | public struct GPUSensors { 32 | public let totalPower: UInt 33 | public let temperature: UInt 34 | public let fanSpeedPercent: UInt 35 | public let fanSpeedRPM: UInt 36 | } 37 | 38 | public struct GPUInfos { 39 | public let name: String 40 | public let isOn: Bool 41 | public let utilization: UInt 42 | public let vramTotalMB: UInt 43 | public let vramFreeMB: UInt 44 | public let coreClockMHz: UInt 45 | public let memoryClockMHz: UInt 46 | public let sensors: GPUSensors 47 | } 48 | 49 | struct GPUHandler { 50 | static func getGPUUsage() throws -> [GPUInfos] { 51 | let pcidevices = try getIOProperties(ioClassname: "IOPCIDevice").filter { (dict: [String : Any]) -> Bool in 52 | return dict["model"] != nil 53 | } 54 | return try getIOProperties(ioClassname: "IOAccelerator").map({ (accelerator: [String : Any]) -> GPUInfos in 55 | guard let agcInfo = accelerator["AGCInfo"] as? [String:Int] else { 56 | throw SystemMonitorError.IOKitError(error: "IOAccelerator -> AGCInfo") 57 | } 58 | guard let performanceStatistics = accelerator["PerformanceStatistics"] as? [String:Any] else { 59 | throw SystemMonitorError.IOKitError(error: "IOAccelerator -> PerformanceStatistics") 60 | } 61 | guard let pci = try pcidevices.first(where: { (pcidevice: [String : Any]) -> Bool in 62 | guard let deviceID = pcidevice["device-id"] as? Data, let vendorID = pcidevice["vendor-id"] as? Data else { 63 | throw SystemMonitorError.IOKitError(error: "IOPCIDevice -> device-id, vendor-id") 64 | } 65 | let pciMatch = "0x" + Data([deviceID[1], deviceID[0], vendorID[1], vendorID[0]]).map { String(format: "%02hhX", $0) }.joined() 66 | let accMatch = accelerator["IOPCIMatch"] as? String ?? accelerator["IOPCIPrimaryMatch"] as? String ?? "" 67 | return accMatch.range(of: pciMatch) != nil 68 | }) else { 69 | throw SystemMonitorError.IOKitError(error: "IOAccelerator IOPCIDevice not corresponding") 70 | } 71 | return GPUInfos( 72 | name: String(data: pci["model"]! as! Data, encoding: String.Encoding.ascii)!, 73 | isOn: agcInfo["poweredOffByAGC"] == 0, 74 | utilization: performanceStatistics["Device Utilization %"] as? UInt ?? 0, 75 | vramTotalMB: accelerator["VRAM,totalMB"] as? UInt ?? pci["VRAM,totalMB"] as? UInt ?? 0, 76 | vramFreeMB: (performanceStatistics["vramFreeBytes"] as? UInt ?? 0) / (1024 * 1024), 77 | coreClockMHz: performanceStatistics["Core Clock(MHz)"] as? UInt ?? 0, 78 | memoryClockMHz: performanceStatistics["Memory Clock(MHz)"] as? UInt ?? 0, 79 | sensors: GPUSensors( 80 | totalPower: performanceStatistics["Total Power(W)"] as? UInt ?? 0, 81 | temperature: performanceStatistics["Temperature(C)"] as? UInt ?? 0, 82 | fanSpeedPercent: performanceStatistics["Fan Speed(%)"] as? UInt ?? 0, 83 | fanSpeedRPM: performanceStatistics["Fan Speed(RPM)"] as? UInt ?? 0 84 | ) 85 | ) 86 | }) 87 | } 88 | } 89 | 90 | func getIOProperties(ioClassname: String) throws -> [[String:Any]] { 91 | var results = [[String:Any]]() 92 | let matchDict = IOServiceMatching(ioClassname) 93 | var iterator = io_iterator_t() 94 | if (IOServiceGetMatchingServices(kIOMasterPortDefault, matchDict, &iterator) == kIOReturnSuccess) { 95 | var regEntry: io_registry_entry_t = IOIteratorNext(iterator) 96 | while (regEntry != io_object_t(0)) { 97 | var properties: Unmanaged? = nil 98 | if (IORegistryEntryCreateCFProperties(regEntry, &properties, kCFAllocatorDefault, 0) == kIOReturnSuccess) { 99 | guard let prop = properties?.takeUnretainedValue() as? [String:Any] else { 100 | throw SystemMonitorError.IOKitError(error: ioClassname) 101 | } 102 | properties?.release() 103 | results.append(prop) 104 | } 105 | IOObjectRelease(regEntry) 106 | regEntry = IOIteratorNext(iterator) 107 | } 108 | IOObjectRelease(iterator) 109 | } 110 | return results 111 | } 112 | -------------------------------------------------------------------------------- /SystemMonitor/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2018 Jacques Lorentz. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /SystemMonitor/MemoryHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemoryHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 24/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct MemoryUsage { 32 | public let swapUsage: SwapUsage 33 | public let ramUsage: RAMUsage 34 | } 35 | 36 | public struct ConvertedSwapUsage { 37 | public let total: Float 38 | public let used: Float 39 | public let free: Float 40 | public let unit: String 41 | } 42 | 43 | // In bytes 44 | public struct SwapUsage { 45 | public let total: UInt64 46 | public let used: UInt64 47 | public let free: UInt64 48 | 49 | public func convertTo(unit: String) throws -> ConvertedSwapUsage { 50 | let mult = try getBytesConversionMult(unit: unit) 51 | return ConvertedSwapUsage( 52 | total: (Float)(self.total) / mult, 53 | used: (Float)(self.used) / mult, 54 | free: (Float)(self.free) / mult, 55 | unit: unit 56 | ) 57 | } 58 | } 59 | 60 | public struct ConvertedRAMUsage { 61 | public let wired: Float 62 | public let active: Float 63 | public let appMemory: Float 64 | public let compressed: Float 65 | public let available: Float 66 | public let unit: String 67 | } 68 | 69 | // In Memory pages (4096 bytes) 70 | public struct RAMUsage { 71 | public let wired: UInt 72 | public let active: UInt 73 | public let appMemory: UInt 74 | public let compressed: UInt 75 | public let available: UInt 76 | 77 | public func convertTo(unit: String) throws -> ConvertedRAMUsage { 78 | let pageSize: UInt = 4096 79 | let mult = try getBytesConversionMult(unit: unit) 80 | return ConvertedRAMUsage( 81 | wired: (Float)(self.wired * pageSize) / mult, 82 | active: (Float)(self.active * pageSize) / mult, 83 | appMemory: (Float)(self.appMemory * pageSize) / mult, 84 | compressed: (Float)(self.compressed * pageSize) / mult, 85 | available: (Float)(self.available * pageSize) / mult, 86 | unit: unit 87 | ) 88 | } 89 | } 90 | 91 | struct MemoryHandler { 92 | static func getRAMInfos() throws -> RAMUsage { 93 | let array = try hostMemoryCall(request: HOST_VM_INFO64, layoutSize: MemoryLayout.size); 94 | 95 | var stat: [String: UInt] = [:] 96 | let attr: [(String, Int)] = [ 97 | ("free", 1), ("active", 1), ("inactive", 1), ("wired", 1), 98 | ("zeroFilled", 2), ("reactivations", 2), ("pageins", 2), ("pageouts", 2), 99 | ("faults", 2), ("cowfaults", 2), ("lookups", 2), ("hits", 2), 100 | ("purges", 2), ("purgeable", 1), ("speculative", 1), 101 | ("decompressions", 2), ("compressions", 2), ("swapins", 2), ("swapouts", 2), 102 | ("compressorPage", 1), ("throttled", 1), 103 | ("externalPage", 1), ("internalPage", 1), ("totalUncompressedInCompressor", 2) 104 | ] 105 | 106 | var inc = 0; 107 | for tag in attr { 108 | if (tag.1 == 1) { 109 | stat[tag.0] = UInt(array[inc]) 110 | } else { 111 | stat[tag.0] = 0//UInt(UInt32(array[inc])) + UInt(UInt32(array[inc + 1])) * UInt(UINT32_MAX) 112 | } 113 | inc += tag.1 114 | } 115 | return RAMUsage( 116 | wired: stat["wired"]!, 117 | active: stat["active"]!, 118 | appMemory: stat["active"]! + stat["purgeable"]!, 119 | compressed: stat["compressorPage"]!, 120 | available: stat["inactive"]! + stat["free"]! 121 | ); 122 | } 123 | 124 | static func getSwapInfos() throws -> SwapUsage { 125 | let request = "vm.swapusage" 126 | var count = MemoryLayout.size 127 | var usage = xsw_usage() 128 | if (sysctlbyname(request, &usage, &count, nil, 0) != 0) { 129 | throw SystemMonitorError.sysctlError(arg: request, errno: stringErrno()) 130 | } 131 | return SwapUsage(total: usage.xsu_total, used: usage.xsu_used, free: usage.xsu_avail) 132 | } 133 | } 134 | 135 | func hostMemoryCall(request: Int32, layoutSize: Int) throws -> [Int32] { 136 | let size = layoutSize / MemoryLayout.size 137 | let ptr = UnsafeMutablePointer.allocate(capacity: size) 138 | var count = UInt32(size) 139 | if (host_statistics64(mach_host_self(), request, ptr, &count) != 0) { 140 | throw SystemMonitorError.hostCallError(arg: request, errno: stringErrno()) 141 | } 142 | let res = Array(UnsafeBufferPointer(start: ptr, count: size)) 143 | ptr.deallocate() 144 | return res 145 | } 146 | -------------------------------------------------------------------------------- /SystemMonitor/NetworkHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 17/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Darwin 30 | 31 | public struct InterfaceAddressFlags { 32 | public let iff_up: Bool 33 | public let iff_broadcast: Bool 34 | public let iff_debug: Bool 35 | public let iff_loopback: Bool 36 | public let iff_pointopoint: Bool 37 | public let iff_notrailers: Bool 38 | public let iff_running: Bool 39 | public let iff_noarp: Bool 40 | public let iff_promisc: Bool 41 | public let iff_allmulti: Bool 42 | public let iff_oactive: Bool 43 | public let iff_simplex: Bool 44 | public let iff_link0: Bool 45 | public let iff_link1: Bool 46 | public let iff_link2: Bool 47 | public let iff_altphys: Bool 48 | public let iff_multicast: Bool 49 | } 50 | 51 | public struct InterfaceAddress { 52 | public let address: String 53 | public let netmask: String 54 | public let destaddress: String 55 | public let type: String 56 | public let flags: InterfaceAddressFlags 57 | } 58 | 59 | public struct NetworkInterfaceInfos { 60 | public let name: String 61 | public let bytessend: UInt 62 | public let bytesreceived: UInt 63 | public let addresses: [InterfaceAddress] 64 | } 65 | 66 | struct NetworkHandler { 67 | static func getNetworkInfos() throws -> [NetworkInterfaceInfos] { 68 | let interfaces = try getInterfaceIO(interfaces: try getInterfaceNames()) 69 | let ifaddresses = try getIFAddresses() 70 | return interfaces.map { (interface: NetworkInterfaceInfos) -> NetworkInterfaceInfos in 71 | return NetworkInterfaceInfos( 72 | name: interface.name, 73 | bytessend: interface.bytessend, 74 | bytesreceived: interface.bytesreceived, 75 | addresses: ifaddresses[interface.name] ?? [InterfaceAddress]() 76 | ) 77 | } 78 | } 79 | } 80 | 81 | func getInterfaceNames() throws -> [String] { 82 | let request = [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0] 83 | var count = 0 84 | if (sysctl(UnsafeMutablePointer(mutating: request), UInt32(request.count), nil, &count, nil, 0) != 0) { 85 | throw SystemMonitorError.sysctlError(arg: "net...", errno: stringErrno()) 86 | } 87 | let ptr = UnsafeMutablePointer.allocate(capacity: count) 88 | if (sysctl(UnsafeMutablePointer(mutating: request), UInt32(request.count), ptr, &count, nil, 0) != 0) { 89 | throw SystemMonitorError.sysctlError(arg: "net...", errno: stringErrno()) 90 | } 91 | var interfaces = [String]() 92 | var cursor = 0 93 | while (cursor < count) { 94 | cursor = ptr.advanced(by: cursor).withMemoryRebound(to: if_msghdr.self, capacity: MemoryLayout.size) { ifm_ptr -> Int in 95 | if (integer_t(ifm_ptr.pointee.ifm_type) == RTM_IFINFO) { 96 | let interface = ifm_ptr.advanced(by: 1).withMemoryRebound(to: Int8.self, capacity: 20) { name_ptr -> String in 97 | let size = Int(name_ptr.advanced(by: 5).pointee) 98 | let buf = name_ptr.advanced(by: 8) 99 | buf.advanced(by: size).pointee = 0 100 | return String(cString: buf) 101 | } 102 | if (!interface.isEmpty) { 103 | interfaces.append(interface) 104 | } 105 | } 106 | return cursor + Int(ifm_ptr.pointee.ifm_msglen) 107 | } 108 | } 109 | ptr.deallocate() 110 | return interfaces 111 | } 112 | 113 | 114 | func getInterfaceIO(interfaces: [String]) throws -> [NetworkInterfaceInfos] { 115 | let request = [CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0] 116 | var count = 0 117 | if (sysctl(UnsafeMutablePointer(mutating: request), UInt32(request.count), nil, &count, nil, 0) != 0) { 118 | throw SystemMonitorError.sysctlError(arg: "net...", errno: stringErrno()) 119 | } 120 | let ptr = UnsafeMutablePointer.allocate(capacity: count) 121 | if (sysctl(UnsafeMutablePointer(mutating: request), UInt32(request.count), ptr, &count, nil, 0) != 0) { 122 | throw SystemMonitorError.sysctlError(arg: "net...", errno: stringErrno()) 123 | } 124 | var io = [NetworkInterfaceInfos]() 125 | var cursor = 0 126 | var index = 0 127 | while (cursor < count) { 128 | cursor = ptr.advanced(by: cursor).withMemoryRebound(to: if_msghdr.self, capacity: MemoryLayout.size) { ifm_ptr -> Int in 129 | if integer_t(ifm_ptr.pointee.ifm_type) == RTM_IFINFO2 { 130 | ifm_ptr.withMemoryRebound(to: if_msghdr2.self, capacity: MemoryLayout.size) { ifm_ptr in 131 | let pd = ifm_ptr.pointee 132 | 133 | // SEE WHAT ELSE WE CAN GET 134 | // print(pd.ifm_flags) 135 | 136 | if index < interfaces.count { 137 | io.append(NetworkInterfaceInfos(name: interfaces[index], bytessend: UInt(pd.ifm_data.ifi_obytes), bytesreceived: UInt(pd.ifm_data.ifi_ibytes), addresses: [InterfaceAddress]())) 138 | } 139 | index += 1 140 | } 141 | } 142 | return cursor + Int(ifm_ptr.pointee.ifm_msglen) 143 | } 144 | } 145 | ptr.deallocate() 146 | return io 147 | } 148 | 149 | func getNameFromIfAddress(ptr: UnsafeMutablePointer?) -> String { 150 | var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) 151 | if (ptr != nil && getnameinfo(ptr, socklen_t(ptr!.pointee.sa_len), &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) { 152 | return String(cString: hostname) 153 | } 154 | return "" 155 | } 156 | 157 | func getAddressType(ptr: UnsafeMutablePointer?) -> String { 158 | if (ptr == nil) { 159 | return "" 160 | } 161 | switch ptr!.pointee.sa_family { 162 | case UInt8(AF_INET): 163 | return "ipv4" 164 | case UInt8(AF_INET6): 165 | return "ipv6" 166 | case UInt8(AF_LINK): 167 | return "ether" 168 | default: 169 | return String(ptr!.pointee.sa_family) 170 | } 171 | } 172 | 173 | func checkIfFlag(value: Int32, flag: Int32) -> Bool { 174 | return value & flag == flag 175 | } 176 | 177 | func getIFAddresses() throws -> [String:[InterfaceAddress]] { 178 | var addresses = [String:[InterfaceAddress]]() 179 | var ifaddr : UnsafeMutablePointer? 180 | if (getifaddrs(&ifaddr) != 0 || ifaddr == nil) { 181 | throw SystemMonitorError.getifaddrsError() 182 | } 183 | for ptr in sequence(first: ifaddr!, next: { $0.pointee.ifa_next }) { 184 | let flags = Int32(ptr.pointee.ifa_flags) 185 | let name = String(cString: ptr.pointee.ifa_name) 186 | if (!addresses.contains(where: { (args) -> Bool in 187 | let (key, _) = args 188 | return key == name 189 | })) { 190 | addresses.updateValue([InterfaceAddress](), forKey: name) 191 | } 192 | addresses[name]?.append(InterfaceAddress( 193 | address: getNameFromIfAddress(ptr: ptr.pointee.ifa_addr), 194 | netmask: getNameFromIfAddress(ptr: ptr.pointee.ifa_netmask), 195 | destaddress: getNameFromIfAddress(ptr: ptr.pointee.ifa_dstaddr), 196 | type: getAddressType(ptr: ptr.pointee.ifa_addr), 197 | flags: InterfaceAddressFlags( 198 | iff_up: checkIfFlag(value: flags, flag: IFF_UP), 199 | iff_broadcast: checkIfFlag(value: flags, flag: IFF_BROADCAST), 200 | iff_debug: checkIfFlag(value: flags, flag: IFF_DEBUG), 201 | iff_loopback: checkIfFlag(value: flags, flag: IFF_LOOPBACK), 202 | iff_pointopoint: checkIfFlag(value: flags, flag: IFF_POINTOPOINT), 203 | iff_notrailers: checkIfFlag(value: flags, flag: IFF_NOTRAILERS), 204 | iff_running: checkIfFlag(value: flags, flag: IFF_RUNNING), 205 | iff_noarp: checkIfFlag(value: flags, flag: IFF_NOARP), 206 | iff_promisc: checkIfFlag(value: flags, flag: IFF_PROMISC), 207 | iff_allmulti: checkIfFlag(value: flags, flag: IFF_ALLMULTI), 208 | iff_oactive: checkIfFlag(value: flags, flag: IFF_OACTIVE), 209 | iff_simplex: checkIfFlag(value: flags, flag: IFF_SIMPLEX), 210 | iff_link0: checkIfFlag(value: flags, flag: IFF_LINK0), 211 | iff_link1: checkIfFlag(value: flags, flag: IFF_LINK1), 212 | iff_link2: checkIfFlag(value: flags, flag: IFF_LINK2), 213 | iff_altphys: checkIfFlag(value: flags, flag: IFF_ALTPHYS), 214 | iff_multicast: checkIfFlag(value: flags, flag: IFF_MULTICAST)) 215 | ) 216 | ) 217 | } 218 | freeifaddrs(ifaddr) 219 | return addresses 220 | } 221 | -------------------------------------------------------------------------------- /SystemMonitor/ProcessorHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProcessorHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 29/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct CPUCoreUsagePercent { 32 | public let user: Float 33 | public let system: Float 34 | public let idle: Float 35 | public let nice: Float 36 | } 37 | 38 | public struct CPUCoreUsage { 39 | public let user: UInt32 40 | public let system: UInt32 41 | public let idle: UInt32 42 | public let nice: UInt32 43 | 44 | public func toPercent() -> CPUCoreUsagePercent { 45 | let total = Float(self.user + self.system + self.idle + self.nice) / 100 46 | return CPUCoreUsagePercent( 47 | user: Float(self.user) / total, 48 | system: Float(self.system) / total, 49 | idle: Float(self.idle) / total, 50 | nice: Float(self.nice) / total 51 | ) 52 | } 53 | } 54 | 55 | public struct CPUUsagePercent { 56 | public let cores: [CPUCoreUsagePercent] 57 | public let total: CPUCoreUsagePercent 58 | } 59 | 60 | public struct CPUUsage { 61 | public let cores: [CPUCoreUsage] 62 | public let total: CPUCoreUsage 63 | 64 | public func toPercent(unixLike: Bool) -> CPUUsagePercent { 65 | let totalNotUnix = self.total.toPercent() 66 | let coresNb = Float(self.cores.count) 67 | let total = (unixLike ? CPUCoreUsagePercent( 68 | user: totalNotUnix.user * coresNb, 69 | system: totalNotUnix.system * coresNb, 70 | idle: totalNotUnix.idle * coresNb, 71 | nice: totalNotUnix.nice * coresNb 72 | ) : totalNotUnix) 73 | return CPUUsagePercent( 74 | cores: self.cores.map { core in core.toPercent() }, 75 | total: total 76 | ) 77 | } 78 | } 79 | 80 | public struct CPUInfos { 81 | public let coreNumber: Int32 82 | public let model: String 83 | public let cpuFeatures: [String] 84 | public let usage: CPUUsage 85 | } 86 | 87 | struct ProcessorHandler { 88 | var cpuLastLoad: [[UInt32]] 89 | let coreNumber: Int32 90 | let model: String 91 | let features: [String] 92 | 93 | init() throws { 94 | self.cpuLastLoad = try hostProcessorCall(request: PROCESSOR_CPU_LOAD_INFO) 95 | 96 | var count = 4 97 | var nb: Int32 = 0 98 | if (sysctlbyname("hw.ncpu", &nb, &count, nil, 0) != 0) { 99 | throw SystemMonitorError.sysctlError(arg: "hw.ncpu", errno: stringErrno()) 100 | } 101 | self.coreNumber = (count == 4 ? nb : 0) 102 | self.model = try getSysctlString(request: "machdep.cpu.brand_string") 103 | self.features = try getSysctlString(request: "machdep.cpu.features").split(separator: " ").map({ String($0) }) 104 | 105 | usleep(100000) // For calculating CPU ticks difference 106 | } 107 | 108 | mutating func getCPUUsage() throws -> CPUUsage { 109 | let cpuLoad = try hostProcessorCall(request: PROCESSOR_CPU_LOAD_INFO) 110 | let cores = cpuLoad.enumerated().map { (arg: (offset: Int, element: [UInt32])) -> CPUCoreUsage in 111 | let (offset, element) = arg 112 | return CPUCoreUsage( 113 | user: element[0] - cpuLastLoad[offset][0], 114 | system: element[1] - cpuLastLoad[offset][1], 115 | idle: element[2] - cpuLastLoad[offset][2], 116 | nice: element[3] - cpuLastLoad[offset][3] 117 | ) 118 | } 119 | self.cpuLastLoad = cpuLoad 120 | let total = cores.reduce(CPUCoreUsage(user: 0, system: 0, idle: 0, nice: 0)) { 121 | (res: CPUCoreUsage, elem: CPUCoreUsage) -> CPUCoreUsage in CPUCoreUsage( 122 | user: res.user + elem.user, 123 | system: res.system + elem.system, 124 | idle: res.idle + elem.idle, 125 | nice: res.nice + elem.nice 126 | ) 127 | } 128 | return CPUUsage(cores: cores, total: total) 129 | } 130 | 131 | mutating func getCPUInfos() throws -> CPUInfos { 132 | return CPUInfos( 133 | coreNumber: self.coreNumber, 134 | model: self.model, 135 | cpuFeatures: self.features, 136 | usage: try getCPUUsage() 137 | ); 138 | } 139 | } 140 | 141 | func hostProcessorCall(request: Int32) throws -> [[UInt32]] { 142 | var array = processor_info_array_t(bitPattern: 0) 143 | var msgCount = mach_msg_type_name_t() 144 | var count = UInt32() 145 | if (host_processor_info(mach_host_self(), request, &count, &array, &msgCount) != 0) { 146 | throw SystemMonitorError.hostCallError(arg: request, errno: stringErrno()) 147 | } 148 | let cpuLoad = array!.withMemoryRebound( 149 | to: processor_cpu_load_info.self, 150 | capacity: Int(count) * MemoryLayout.size 151 | ) { ptr -> processor_cpu_load_info_t in ptr } 152 | var res = [[UInt32]]() 153 | for i in 0 ... Int(count) - 1 { 154 | res.append([ 155 | cpuLoad[i].cpu_ticks.0, 156 | cpuLoad[i].cpu_ticks.1, 157 | cpuLoad[i].cpu_ticks.2, 158 | cpuLoad[i].cpu_ticks.3, 159 | ]) 160 | } 161 | munmap(array, Int(vm_page_size)) 162 | return res 163 | } 164 | -------------------------------------------------------------------------------- /SystemMonitor/SensorsHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SensorsHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 30/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | public typealias SMCBytes = (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, 32 | UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, 33 | UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, 34 | UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, 35 | UInt8, UInt8, UInt8, UInt8) 36 | 37 | extension UInt32 { 38 | init(fromBytes bytes: (UInt8, UInt8, UInt8, UInt8)) { 39 | let byte0 = UInt32(bytes.0) << 24 40 | let byte1 = UInt32(bytes.1) << 16 41 | let byte2 = UInt32(bytes.2) << 8 42 | let byte3 = UInt32(bytes.3) 43 | self = byte0 | byte1 | byte2 | byte3 44 | } 45 | } 46 | 47 | public struct SMCParamStruct { 48 | 49 | /// I/O Kit function selector 50 | public enum Selector: UInt8 { 51 | case kSMCHandleYPCEvent = 2 52 | case kSMCReadKey = 5 53 | case kSMCWriteKey = 6 54 | case kSMCGetKeyFromIndex = 8 55 | case kSMCGetKeyInfo = 9 56 | } 57 | 58 | /// Return codes for SMCParamStruct.result property 59 | public enum Result: UInt8 { 60 | case kSMCSuccess = 0 61 | case kSMCError = 1 62 | case kSMCKeyNotFound = 132 63 | } 64 | 65 | public struct SMCVersion { 66 | var major: CUnsignedChar = 0 67 | var minor: CUnsignedChar = 0 68 | var build: CUnsignedChar = 0 69 | var reserved: CUnsignedChar = 0 70 | var release: CUnsignedShort = 0 71 | } 72 | 73 | public struct SMCPLimitData { 74 | var version: UInt16 = 0 75 | var length: UInt16 = 0 76 | var cpuPLimit: UInt32 = 0 77 | var gpuPLimit: UInt32 = 0 78 | var memPLimit: UInt32 = 0 79 | } 80 | 81 | public struct SMCKeyInfoData { 82 | /// How many bytes written to SMCParamStruct.bytes 83 | var dataSize: IOByteCount = 0 84 | 85 | /// Type of data written to SMCParamStruct.bytes. This lets us know how 86 | /// to interpret it (translate it to human readable) 87 | var dataType: UInt32 = 0 88 | 89 | var dataAttributes: UInt8 = 0 90 | } 91 | 92 | /// FourCharCode telling the SMC what we want 93 | var key: UInt32 = 0 94 | var vers = SMCVersion() 95 | var pLimitData = SMCPLimitData() 96 | var keyInfo = SMCKeyInfoData() 97 | /// Padding for struct alignment when passed over to C side 98 | var padding: UInt16 = 0 99 | /// Result of an operation 100 | var result: UInt8 = 0 101 | var status: UInt8 = 0 102 | /// Method selector 103 | var data8: UInt8 = 0 104 | var data32: UInt32 = 0 105 | /// Data returned from the SMC 106 | var bytes: SMCBytes = (UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), 107 | UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), 108 | UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), 109 | UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), 110 | UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), UInt8(0), 111 | UInt8(0), UInt8(0)) 112 | } 113 | 114 | public struct FanSensor { 115 | public let min: Float 116 | public let max: Float 117 | public let actual: Float 118 | public let target: Float 119 | } 120 | 121 | public struct SensorsInfos { 122 | public let fans: [FanSensor] 123 | public let temperatures: [String:Float] 124 | public let amperages: [String:Float] 125 | public let voltages: [String:Float] 126 | public let powers: [String:Float] 127 | } 128 | 129 | struct SensorsHandler { 130 | var sensors: [(UInt32, UInt32)] 131 | 132 | init() throws { 133 | self.sensors = [(UInt32, UInt32)]() 134 | var ioc = io_connect_t() 135 | let service: io_object_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC")) 136 | if (service != IO_OBJECT_NULL) { 137 | IOServiceOpen(service, mach_task_self_, 0, &ioc) 138 | var inputStruct = SMCParamStruct() 139 | var outputStruct = SMCParamStruct() 140 | let inputStructSize = MemoryLayout.stride 141 | var outputStructSize = MemoryLayout.stride 142 | 143 | let key = stringToKeyCode(key: "#KEY") 144 | inputStruct.key = key 145 | inputStruct.keyInfo.dataSize = 4 146 | inputStruct.data8 = 5 147 | let returnValue = IOConnectCallStructMethod(ioc, 2, &inputStruct, inputStructSize, &outputStruct, &outputStructSize) 148 | if (returnValue != kIOReturnSuccess) { 149 | throw SystemMonitorError.SMCError(error: errorCodeToString(errorCode: returnValue)) 150 | } 151 | let total = UInt32(outputStruct.bytes.0) << 24 + UInt32(outputStruct.bytes.1) << 16 152 | + UInt32(outputStruct.bytes.2) << 8 + UInt32(outputStruct.bytes.3) 153 | var i = UInt32(0) 154 | 155 | while (i < total) { 156 | inputStruct.data8 = 8 157 | inputStruct.data32 = i 158 | 159 | let returnVal0 = IOConnectCallStructMethod(ioc, 2, &inputStruct, inputStructSize, &outputStruct, &outputStructSize) 160 | if (returnVal0 != kIOReturnSuccess) { 161 | throw SystemMonitorError.SMCError(error: errorCodeToString(errorCode: returnVal0)) 162 | } 163 | let key = outputStruct.key 164 | inputStruct.key = outputStruct.key 165 | inputStruct.keyInfo.dataSize = 4 166 | inputStruct.data8 = 9 167 | 168 | let returnVal1 = IOConnectCallStructMethod(ioc, 2, &inputStruct, inputStructSize, &outputStruct, &outputStructSize) 169 | if (returnVal1 != kIOReturnSuccess) { 170 | throw SystemMonitorError.SMCError(error: errorCodeToString(errorCode: returnVal1)) 171 | } 172 | let dataType = outputStruct.keyInfo.dataType 173 | if (acceptKey(key: key, type: dataType)) { 174 | self.sensors.append((key, dataType)) 175 | } 176 | i += 1 177 | } 178 | IOServiceClose(ioc) 179 | } 180 | } 181 | 182 | func getSensorsInfos() throws -> SensorsInfos { 183 | var ioc = io_connect_t() 184 | let service: io_object_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleSMC")) 185 | var sensors = [String:Float]() 186 | var fans = [UInt32:[UInt32:Float]]() 187 | 188 | if (service != IO_OBJECT_NULL) { 189 | IOServiceOpen(service, mach_task_self_, 0, &ioc) 190 | var inputStruct = SMCParamStruct() 191 | var outputStruct = SMCParamStruct() 192 | let inputStructSize = MemoryLayout.stride 193 | var outputStructSize = MemoryLayout.stride 194 | for s in self.sensors { 195 | let letter = s.0 >> 24 196 | inputStruct.key = s.0 197 | inputStruct.keyInfo.dataSize = 4 198 | inputStruct.data8 = 5 199 | 200 | let returnVal0 = IOConnectCallStructMethod(ioc, 2, &inputStruct, inputStructSize, &outputStruct, &outputStructSize) 201 | if (returnVal0 != kIOReturnSuccess) { 202 | throw SystemMonitorError.SMCError(error: errorCodeToString(errorCode: returnVal0)) 203 | } 204 | let data = outputStruct.bytes 205 | if (letter != 0x46) { 206 | sensors[uint32ToString(value: s.0)] = (s.1 == 1718383648 ? fltToFloat(data.0, data.1, data.2, data.3) : sp78ToFloat(data.0, data.1)) 207 | } else { 208 | let nb = (s.0 >> 16 & 0xFF) - 0x30 // 0 209 | if (fans[nb] == nil) { 210 | fans[nb] = [UInt32:Float]() 211 | } 212 | fans[nb]![s.0 & 0xFFFF] = fpe2ToFloat(data.0, data.1) 213 | } 214 | } 215 | IOServiceClose(ioc) 216 | } 217 | 218 | let fanSensors = fans.map({ (arg0) -> FanSensor in 219 | let (_, value) = arg0 220 | return FanSensor( 221 | min: value[0x4D6e]!, 222 | max: value[0x4D78]!, 223 | actual: value[0x4163]!, 224 | target: value[0x5467]! 225 | ) 226 | }) 227 | 228 | return SensorsInfos( 229 | fans: fanSensors, 230 | temperatures: sensors.filter({ $0.key.first == "T" }), 231 | amperages: sensors.filter({ $0.key.first == "I" }), 232 | voltages: sensors.filter({ $0.key.first == "V" }), 233 | powers: sensors.filter({ $0.key.first == "P" }) 234 | ) 235 | } 236 | } 237 | 238 | func stringToKeyCode(key: String) -> UInt32 { 239 | return (key.count != 4 ? 0 : key.utf8.reduce(0) { sum, character in 240 | return sum << 8 | UInt32(character) 241 | }) 242 | } 243 | 244 | func uint32ToString(value: UInt32) -> String { 245 | return String(describing: UnicodeScalar(value >> 24 & 0xff)!) + 246 | String(describing: UnicodeScalar(value >> 16 & 0xff)!) + 247 | String(describing: UnicodeScalar(value >> 8 & 0xff)!) + 248 | String(describing: UnicodeScalar(value & 0xff)!) 249 | } 250 | 251 | func sp78ToFloat(_ byte0: UInt8, _ byte1: UInt8) -> Float { 252 | return Float(Int16(byte0) << 8 + Int16(byte1)) / 256.0 // (2 ^ 8) 253 | } 254 | 255 | func fpe2ToFloat(_ byte0: UInt8, _ byte1: UInt8) -> Float { 256 | return Float(UInt16(byte0) << 8 + UInt16(byte1)) / 4.0 // (2 ^ 2) 257 | } 258 | 259 | func fltToFloat(_ byte0: UInt8, _ byte1: UInt8, _ byte2: UInt8, _ byte3: UInt8) -> Float { 260 | return Float(bitPattern: UInt32(fromBytes: (byte3, byte2, byte1, byte0))) 261 | } 262 | 263 | func acceptKey(key: UInt32, type: UInt32) -> Bool { 264 | let flt = 1718383648 265 | let sp78 = 1936734008 266 | let fpe2 = 1718641970 267 | let letter = key >> 24 268 | return ( 269 | letter == 0x56 && type == flt // V - flt 270 | || letter == 0x54 && type == sp78 // T - sp78 271 | || letter == 0x49 && type == flt // I - flt 272 | || letter == 0x50 && (type == flt || type == sp78) // P - flt & sp78 273 | || letter == 0x46 && type == fpe2 // F - fpe2 274 | ) 275 | } 276 | 277 | func errorCodeToString(errorCode: Int32) -> String { 278 | let map = [ 279 | kIOReturnSuccess: "KERN_SUCCESS", 280 | kIOReturnError: "general error", 281 | kIOReturnNoMemory: "can't allocate memory", 282 | kIOReturnNoResources: "resource shortage", 283 | kIOReturnIPCError: "error during IPC", 284 | kIOReturnNoDevice: "no such device", 285 | kIOReturnNotPrivileged: "privilege violation", 286 | kIOReturnBadArgument: "invalid argument", 287 | kIOReturnLockedRead: "device read locked", 288 | kIOReturnLockedWrite: "device write locked", 289 | kIOReturnExclusiveAccess: "exclusive access and device already open", 290 | kIOReturnBadMessageID: "sent/received messages had different msg_id", 291 | kIOReturnUnsupported: "unsupported function", 292 | kIOReturnVMError: "misc. VM failure", 293 | kIOReturnInternalError: "internal error", 294 | kIOReturnIOError: "General I/O error", 295 | kIOReturnCannotLock: "can't acquire lock", 296 | kIOReturnNotOpen: "device not open", 297 | kIOReturnNotReadable: "read not supported", 298 | kIOReturnNotWritable: "write not supported", 299 | kIOReturnNotAligned: "alignment error", 300 | kIOReturnBadMedia: "Media Error", 301 | kIOReturnStillOpen: "device(s) still open", 302 | kIOReturnRLDError: "rld failure", 303 | kIOReturnDMAError: "DMA failure", 304 | kIOReturnBusy: "Device Busy", 305 | kIOReturnTimeout: "I/O Timeout", 306 | kIOReturnOffline: "device offline", 307 | kIOReturnNotReady: "not ready", 308 | kIOReturnNotAttached: "device not attached", 309 | kIOReturnNoChannels: "no DMA channels left", 310 | kIOReturnNoSpace: "no space for data", 311 | kIOReturnPortExists: "port already exists", 312 | kIOReturnCannotWire: "can't wire down physical memory", 313 | kIOReturnNoInterrupt: "no interrupt attached", 314 | kIOReturnNoFrames: "no DMA frames enqueued", 315 | kIOReturnMessageTooLarge: "oversized msg received on interrupt port", 316 | kIOReturnNotPermitted: "not permitted", 317 | kIOReturnNoPower: "no power to device", 318 | kIOReturnNoMedia: "media not present", 319 | kIOReturnUnformattedMedia: "media not formatted", 320 | kIOReturnUnsupportedMode: "no such mode", 321 | kIOReturnUnderrun: "data underrun", 322 | kIOReturnOverrun: "data overrun", 323 | kIOReturnDeviceError: "the device is not working properly!", 324 | kIOReturnNoCompletion: "a completion routine is required", 325 | kIOReturnAborted: "operation aborted", 326 | kIOReturnNoBandwidth: "bus bandwidth would be exceeded", 327 | kIOReturnNotResponding: "device not responding", 328 | kIOReturnIsoTooOld: "isochronous I/O request for distant past!", 329 | kIOReturnIsoTooNew: "isochronous I/O request for distant future", 330 | kIOReturnNotFound: "data was not found", 331 | kIOReturnInvalid: "should never be seen" 332 | ] 333 | return map[errorCode] ?? "error code not defined (" + String(errorCode) + ")" 334 | } 335 | -------------------------------------------------------------------------------- /SystemMonitor/SystemHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemHandler.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 25/07/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct SystemSpecificInfos { 32 | public let boottime: Int 33 | public let hostname: String 34 | public let kernel: String 35 | public let kernelVersion: String 36 | } 37 | 38 | struct SystemHandler { 39 | static func getSystemInfos() throws -> SystemSpecificInfos { 40 | return SystemSpecificInfos( 41 | boottime: try getSysctlAsTimeval(request: "kern.boottime").tv_sec, 42 | hostname: try getSysctlString(request: "kern.hostname"), 43 | kernel: try getSysctlString(request: "kern.ostype"), 44 | kernelVersion: try getSysctlString(request: "kern.osrelease") 45 | ) 46 | } 47 | } 48 | 49 | func getSysctlAsTimeval(request: String) throws -> timeval { 50 | var count = MemoryLayout.size 51 | var time = timeval() 52 | if (sysctlbyname(request, &time, &count, nil, 0) != 0) { 53 | throw SystemMonitorError.sysctlError(arg: request, errno: stringErrno()) 54 | } 55 | return time 56 | } 57 | -------------------------------------------------------------------------------- /SystemMonitor/SystemMonitor.h: -------------------------------------------------------------------------------- 1 | // 2 | // SystemMonitor.h 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 24/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | #import 30 | 31 | //! Project version number for SystemMonitor. 32 | FOUNDATION_EXPORT double SystemMonitorVersionNumber; 33 | 34 | //! Project version string for SystemMonitor. 35 | FOUNDATION_EXPORT const unsigned char SystemMonitorVersionString[]; 36 | 37 | // In this header, you should import all the public headers of your framework using statements like #import 38 | 39 | 40 | -------------------------------------------------------------------------------- /SystemMonitor/SystemMonitor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemMonitor.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 24/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | enum SystemMonitorError : Error { 32 | case sysctlError(arg: String, errno: String) 33 | case hostCallError(arg: Int32, errno: String) 34 | case conversionFailed(invalidUnit: String) 35 | case statfsError(errno: String) 36 | case IOKitError(error: String) 37 | case getifaddrsError() 38 | case SMCError(error: String) 39 | } 40 | 41 | public struct SystemInfos { 42 | public let memory: MemoryUsage 43 | public let processor: CPUInfos 44 | public let disk: VolumesDisksInfos 45 | public let network: [NetworkInterfaceInfos] 46 | public let graphics: [GPUInfos] 47 | public let system: SystemSpecificInfos 48 | public let battery: BatteryInfos 49 | public let sensors: SensorsInfos 50 | } 51 | 52 | public class SystemMonitor { 53 | let sensorsHandler: SensorsHandler 54 | var processorHandler: ProcessorHandler 55 | 56 | public init() throws { 57 | self.sensorsHandler = try SensorsHandler() 58 | self.processorHandler = try ProcessorHandler() 59 | } 60 | 61 | public func getInfos() throws -> SystemInfos { 62 | return SystemInfos( 63 | memory: try self.getMemoryInfos(), 64 | processor: try self.getProcessorInfos(), 65 | disk: try self.getDiskInfos(), 66 | network: try self.getNetworkInfos(), 67 | graphics: try self.getGPUInfos(), 68 | system: try self.getSystemInfos(), 69 | battery: try self.getBatteryInfos(), 70 | sensors: try self.getSensorsInfos() 71 | ) 72 | } 73 | 74 | public func getMemoryInfos() throws -> MemoryUsage { 75 | return MemoryUsage( 76 | swapUsage: try MemoryHandler.getSwapInfos(), 77 | ramUsage: try MemoryHandler.getRAMInfos() 78 | ) 79 | } 80 | 81 | public func getProcessorInfos() throws -> CPUInfos { 82 | return try self.processorHandler.getCPUInfos() 83 | } 84 | 85 | public func getDiskInfos() throws -> VolumesDisksInfos { 86 | return VolumesDisksInfos( 87 | volumes: try DiskHandler.getVolumesInfos(), 88 | disks: try DiskHandler.getDisksInfos() 89 | ) 90 | } 91 | 92 | public func getNetworkInfos() throws -> [NetworkInterfaceInfos] { 93 | return try NetworkHandler.getNetworkInfos() 94 | } 95 | 96 | public func getGPUInfos() throws -> [GPUInfos] { 97 | return try GPUHandler.getGPUUsage() 98 | } 99 | 100 | public func getSystemInfos() throws -> SystemSpecificInfos { 101 | return try SystemHandler.getSystemInfos() 102 | } 103 | 104 | public func getBatteryInfos() throws -> BatteryInfos { 105 | return try BatteryHandler.getBatteryInfos() 106 | } 107 | 108 | public func getSensorsInfos() throws -> SensorsInfos { 109 | return try sensorsHandler.getSensorsInfos() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /SystemMonitor/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // SystemMonitor 4 | // 5 | // Created by Jacques Lorentz on 24/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import Foundation 30 | 31 | func stringErrno() -> String { 32 | return String(cString: strerror(errno)); 33 | } 34 | 35 | func getBytesConversionMult(unit: String) throws -> Float { 36 | let units = ["B", "KB", "MB", "GB"] 37 | if (!units.contains(unit)) { 38 | throw SystemMonitorError.conversionFailed(invalidUnit: unit) 39 | } 40 | let index = units.firstIndex(of: unit) 41 | return pow(Float(1024), Float(index!)) 42 | } 43 | 44 | func getSysctlString(request: String) throws -> String { 45 | var count = 0 46 | if (sysctlbyname(request, nil, &count, nil, 0) != 0) { 47 | throw SystemMonitorError.sysctlError(arg: request, errno: stringErrno()) 48 | } 49 | let ptr = UnsafeMutablePointer.allocate(capacity: count) 50 | if (sysctlbyname(request, ptr, &count, nil, 0) != 0) { 51 | throw SystemMonitorError.sysctlError(arg: request, errno: stringErrno()) 52 | } 53 | let res = Array(UnsafeBufferPointer(start: ptr, count: count)).reduce("", { (str: String, code: Int8) -> String in 54 | return (code == 0 ? str : str + String(Character(UnicodeScalar(Int(code))!))) 55 | }) 56 | ptr.deallocate() 57 | return res 58 | } 59 | -------------------------------------------------------------------------------- /SystemMonitorTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SystemMonitorTests/SystemMonitorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SystemMonitorTests.swift 3 | // SystemMonitorTests 4 | // 5 | // Created by Jacques Lorentz on 24/06/2018. 6 | // 7 | // MIT License 8 | // Copyright (c) 2018 Jacques Lorentz 9 | // 10 | // Permission is hereby granted, free of charge, to any person obtaining a copy 11 | // of this software and associated documentation files (the "Software"), to deal 12 | // in the Software without restriction, including without limitation the rights 13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | // copies of the Software, and to permit persons to whom the Software is 15 | // furnished to do so, subject to the following conditions: 16 | // 17 | // The above copyright notice and this permission notice shall be included in all 18 | // copies or substantial portions of the Software. 19 | // 20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | // SOFTWARE. 27 | // 28 | 29 | import XCTest 30 | @testable import SystemMonitor 31 | 32 | class SystemMonitorTests: XCTestCase { 33 | let printDebug = false 34 | 35 | override func setUp() { 36 | super.setUp() 37 | // Put setup code here. This method is called before the invocation of each test method in the class. 38 | } 39 | 40 | override func tearDown() { 41 | // Put teardown code here. This method is called after the invocation of each test method in the class. 42 | super.tearDown() 43 | } 44 | 45 | func testSystemMonitor() throws { 46 | let sm = try SystemMonitor() 47 | let infos = try sm.getInfos() 48 | if (printDebug) { 49 | print(infos) 50 | } 51 | } 52 | 53 | func testMemoryHandler() throws { 54 | let sm = try SystemMonitor() 55 | let infos = try sm.getMemoryInfos() 56 | if (printDebug) { 57 | print(infos) 58 | } 59 | } 60 | 61 | func testProcessorHandler() throws { 62 | let sm = try SystemMonitor() 63 | let infos = try sm.getProcessorInfos() 64 | if (printDebug) { 65 | print(infos) 66 | } 67 | } 68 | 69 | func testDiskHandler() throws { 70 | let sm = try SystemMonitor() 71 | let infos = try sm.getDiskInfos() 72 | if (printDebug) { 73 | print(infos) 74 | } 75 | } 76 | 77 | func testNetworkHandler() throws { 78 | let sm = try SystemMonitor() 79 | let infos = try sm.getNetworkInfos() 80 | if (printDebug) { 81 | print(infos) 82 | } 83 | } 84 | 85 | func testGPUHandler() throws { 86 | let sm = try SystemMonitor() 87 | let infos = try sm.getGPUInfos() 88 | if (printDebug) { 89 | print(infos) 90 | } 91 | } 92 | 93 | func testSystemHandler() throws { 94 | let sm = try SystemMonitor() 95 | let infos = try sm.getSystemInfos() 96 | if (printDebug) { 97 | print(infos) 98 | } 99 | } 100 | 101 | func testBatteryHandler() throws { 102 | let sm = try SystemMonitor() 103 | let infos = try sm.getBatteryInfos() 104 | if (printDebug) { 105 | print(infos) 106 | } 107 | } 108 | 109 | func testSensorsHandler() throws { 110 | let sm = try SystemMonitor() 111 | let infos = try sm.getSensorsInfos() 112 | if (printDebug) { 113 | print(infos) 114 | } 115 | } 116 | 117 | func testNiceDisplay() throws { 118 | let sm = try SystemMonitor() 119 | 120 | print("System Infos:") 121 | let systemInfos = try sm.getSystemInfos() 122 | print("\tHostname:", systemInfos.hostname) 123 | print("\tKernel:", systemInfos.kernel) 124 | print("\tKernel Version:", systemInfos.kernelVersion) 125 | let gmt = Date(timeIntervalSince1970: TimeInterval(systemInfos.boottime)) 126 | let gmtDiff = TimeInterval(TimeZone.current.secondsFromGMT()) 127 | print("\tBoottime:", gmt.addingTimeInterval(gmtDiff)) 128 | 129 | print("Processor/CPU Infos:") 130 | let cpuInfos = try sm.getProcessorInfos() 131 | print("\tCore number: \(cpuInfos.coreNumber)") 132 | print("\tModel: \(cpuInfos.model)") 133 | print("\tCPU Features: \(cpuInfos.cpuFeatures)") 134 | print("\tCPU Usage:\t User\t System\t Idle\t Nice") 135 | let cpuUsage = cpuInfos.usage 136 | for (index, core) in cpuUsage.cores.enumerated() { 137 | let corePercent = core.toPercent() 138 | print( 139 | "\t\tCPU" + String(index) + ":\t", 140 | String(floatTrunc(flt: corePercent.user, decimalNb: 1)) + "%\t", 141 | String(floatTrunc(flt: corePercent.system, decimalNb: 1)) + "%\t", 142 | String(floatTrunc(flt: corePercent.idle, decimalNb: 1)) + "%\t", 143 | String(floatTrunc(flt: corePercent.nice, decimalNb: 1)) + "%\t", 144 | "(" + String(core.user) + ",", 145 | String(core.system) + ",", 146 | String(core.idle) + ",", 147 | String(core.nice) + ")" 148 | ) 149 | } 150 | let total = cpuUsage.total 151 | let totalPercent = cpuUsage.total.toPercent() 152 | print( 153 | "\t\tTotal:\t", 154 | String(floatTrunc(flt: totalPercent.user, decimalNb: 1)) + "%\t", 155 | String(floatTrunc(flt: totalPercent.system, decimalNb: 1)) + "%\t", 156 | String(floatTrunc(flt: totalPercent.idle, decimalNb: 1)) + "%\t", 157 | String(floatTrunc(flt: totalPercent.nice, decimalNb: 1)) + "%\t", 158 | "(" + String(total.user) + ",", 159 | String(total.system) + ",", 160 | String(total.idle) + ",", 161 | String(total.nice) + ")" 162 | ) 163 | 164 | print("Memory Infos:") 165 | let memoryInfos = try sm.getMemoryInfos() 166 | let unitToConvert = "GB" 167 | let ramUsage = memoryInfos.ramUsage 168 | let ramUsageUnit = try memoryInfos.ramUsage.convertTo(unit: unitToConvert) 169 | print("\tRAM Usage:") 170 | print("\t\tActive: ", floatTrunc(flt: ramUsageUnit.active, decimalNb: 2), ramUsageUnit.unit, "(", ramUsage.active, "pages )") 171 | print("\t\tWired: ", floatTrunc(flt: ramUsageUnit.wired, decimalNb: 2), ramUsageUnit.unit, "(", ramUsage.wired, "pages )") 172 | print("\t\tApplication:", floatTrunc(flt: ramUsageUnit.appMemory, decimalNb: 2), ramUsageUnit.unit, "(", ramUsage.appMemory, "pages )") 173 | print("\t\tCompressed: ", floatTrunc(flt: ramUsageUnit.compressed, decimalNb: 2), ramUsageUnit.unit, "(", ramUsage.compressed, "pages )") 174 | print("\t\tAvailablbe: ", floatTrunc(flt: ramUsageUnit.available, decimalNb: 2), ramUsageUnit.unit, "(", ramUsage.available, "pages )") 175 | print("\tSwap Usage:") 176 | let swapUsage = memoryInfos.swapUsage 177 | let swapUsageUnit = try memoryInfos.swapUsage.convertTo(unit: unitToConvert) 178 | print("\t\tTotal:", floatTrunc(flt: swapUsageUnit.total, decimalNb: 2), swapUsageUnit.unit, "(", swapUsage.total, "bytes )") 179 | print("\t\tUsed: ", floatTrunc(flt: swapUsageUnit.used, decimalNb: 2), swapUsageUnit.unit, "(", swapUsage.used, "bytes )") 180 | print("\t\tFree: ", floatTrunc(flt: swapUsageUnit.free, decimalNb: 2), swapUsageUnit.unit, "(", swapUsage.free, "bytes )") 181 | 182 | print("Disks/Volumes Infos:") 183 | let disksVolumesInfos = try sm.getDiskInfos() 184 | print("\tVolumes Infos:") 185 | for volume in disksVolumesInfos.volumes { 186 | let usage = try volume.usage.convertTo(unit: "GB") 187 | print( 188 | "\t\t" + volume.mountname, 189 | "[" + volume.filesystem + "]", 190 | volume.mountpoint, 191 | "\t", 192 | String(floatTrunc(flt: usage.available, decimalNb: 2)) + usage.unit, 193 | "available on", 194 | String(floatTrunc(flt: usage.total, decimalNb: 2)) + usage.unit, 195 | "(" + String(floatTrunc(flt: usage.free, decimalNb: 2)) + usage.unit + " free)" 196 | ) 197 | } 198 | print("\tDisks Infos:") 199 | for disk in disksVolumesInfos.disks { 200 | let usage = disk.usage 201 | print( 202 | "\t\t" + disk.name, 203 | "\t", 204 | String(floatTrunc(flt: bytesToGB(bytes: disk.size), decimalNb: 2)) + "GB", 205 | "(read:", 206 | String(floatTrunc(flt: bytesToGB(bytes: usage.bytesread), decimalNb: 2)) + "GB,", 207 | "writen:", 208 | String(floatTrunc(flt: bytesToGB(bytes: usage.byteswritten), decimalNb: 2)) + "GB)" 209 | ) 210 | } 211 | 212 | print("Network Infos:") 213 | let networkInfos = try sm.getNetworkInfos() 214 | for interface in networkInfos { 215 | print("\tInterface " + interface.name + " (send: " + String(interface.bytessend) + ", received: " + String(interface.bytesreceived) + ")") 216 | for address in interface.addresses.filter({ $0.flags.iff_up && $0.address.count > 0 }) { 217 | print("\t\t[" + address.type + "]\t" + address.address + "\t" + address.netmask + "\t" + address.destaddress) 218 | } 219 | } 220 | 221 | print("Graphics/GPU Infos:") 222 | let gpuInfos = try sm.getGPUInfos() 223 | for gpu in gpuInfos { 224 | print("\t" + gpu.name + " [" + (gpu.isOn ? "ON" : "OFF") + "]") 225 | if (gpu.isOn) { 226 | print( 227 | "\t\t" + String(gpu.utilization) + "% core", 228 | "(@" + String(gpu.coreClockMHz) + "MHz),", 229 | String(gpu.vramFreeMB) + "MB free VRAM of", 230 | String(gpu.vramTotalMB) + "MB", 231 | "(@" + String(gpu.memoryClockMHz) + "MHz),", 232 | String(gpu.sensors.temperature) + "°C" 233 | ) 234 | } 235 | } 236 | 237 | print("Battery Infos:") 238 | let batteryInfos = try sm.getBatteryInfos() 239 | print("\tSerial number:", batteryInfos.serialNumber) 240 | print("\tManufacture date:", batteryInfos.manufactureDate) 241 | print("\tCycle count:", batteryInfos.cycleCount) 242 | print("\tDesign capacity:", batteryInfos.designCapacity) 243 | print( 244 | "\tMaximum capacity:", 245 | batteryInfos.maxCapacity, 246 | "(" + String(floatTrunc(flt: Float(batteryInfos.maxCapacity) / Float(batteryInfos.designCapacity) * 100, decimalNb: 2)) + "%", 247 | "of design capacity)" 248 | ) 249 | print( 250 | "\tCurrent capacity:", 251 | batteryInfos.currentCapacity, 252 | "(" + String(floatTrunc(flt: Float(batteryInfos.currentCapacity) / Float(batteryInfos.maxCapacity) * 100, decimalNb: 2)) + "%", 253 | "of maximum capacity)" 254 | ) 255 | let watt = (Float(batteryInfos.voltage) / 1000) * (Float(batteryInfos.instantAmperage) / 1000) 256 | print( 257 | "\tUsage:", 258 | String(batteryInfos.voltage) + "mV", 259 | String(batteryInfos.instantAmperage) + "mA", 260 | String(floatTrunc(flt: (watt < 0 ? -watt : watt), decimalNb: 2)) + "W" 261 | 262 | ) 263 | print( 264 | "\tCharging:", 265 | batteryInfos.isCharging, 266 | (batteryInfos.isCharging ? "@ " + String(floatTrunc(flt: Float(batteryInfos.chargingCurrent) / 1000, decimalNb: 2)) + "W" : ""), 267 | "(fully charged:", 268 | String(batteryInfos.isFullyCharged) + ")" 269 | ) 270 | print("\tTime remaining:", batteryInfos.timeRemaining / 60, "hours", batteryInfos.timeRemaining % 60, "minutes") 271 | 272 | print("Sensors Infos:") 273 | let sensorsInfos = try sm.getSensorsInfos() 274 | print("\tFans:") 275 | for fan in sensorsInfos.fans { 276 | print("\t\t" + String(fan.actual), "RPM", "(min:", String(fan.min) + ", max:", String(fan.max) + ")") 277 | } 278 | print("\tTemperatures:") 279 | for temp in sensorsInfos.temperatures.filter({ (_: String, value: Float) -> Bool in value > 0 }) { 280 | print("\t\t" + temp.key + ": " + String(floatTrunc(flt: temp.value, decimalNb: 1)) + "°C") 281 | } 282 | print("\tVoltage:") 283 | for temp in sensorsInfos.voltages.filter({ (_: String, value: Float) -> Bool in value > 0 }) { 284 | print("\t\t" + temp.key + ": " + String(floatTrunc(flt: temp.value, decimalNb: 2)) + "V") 285 | } 286 | print("\tAmperages:") 287 | for temp in sensorsInfos.amperages.filter({ (_: String, value: Float) -> Bool in value > 0 }) { 288 | print("\t\t" + temp.key + ": " + String(floatTrunc(flt: temp.value, decimalNb: 2)) + "A") 289 | } 290 | print("\tWattages:") 291 | for temp in sensorsInfos.powers.filter({ (_: String, value: Float) -> Bool in value > 0 }) { 292 | print("\t\t" + temp.key + ": " + String(floatTrunc(flt: temp.value, decimalNb: 2)) + "W") 293 | } 294 | } 295 | } 296 | 297 | func floatTrunc(flt: Float, decimalNb: Int) -> Float { 298 | let power = pow(Float(10), Float(decimalNb)) 299 | return round(flt * power) / power 300 | } 301 | 302 | func bytesToGB(bytes: UInt64) -> Float { 303 | return Float(bytes) / pow(Float(1024), Float(3)) 304 | } 305 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ## Bug To Fix 2 | 3 | - RAM infos on two bytes error, replaced by 0 4 | 5 | 6 | ## Features 7 | 8 | Add iOS compatibility?? 9 | 10 | #### Memory 11 | 12 | - Total RAM 13 | - RAM pressure 14 | - RAM cache 15 | 16 | #### Processor 17 | 18 | - CPU frequency / temp (Intel Power Gadget) 19 | 20 | #### Disks 21 | 22 | - S.M.A.R.T. 23 | - Disk name 24 | - Volume name 25 | - Which disk for a volume 26 | 27 | #### Network 28 | 29 | - Public IP 30 | - Network nice name 31 | 32 | #### GPU 33 | 34 | - Intel GPU frequency / temp (Intel Power Gadget) 35 | - Display frame per second 36 | 37 | #### Sensors 38 | 39 | - Add names to sensors (temp / volt / amp / watt) 40 | - Fans names 41 | 42 | #### Power 43 | 44 | - Charger power capability 45 | 46 | #### System 47 | 48 | - Device model 49 | - Machine arch 50 | - Processes / threads / load avg --------------------------------------------------------------------------------