Skip to content

Commit 44b2bf5

Browse files
authored
Update CPU, Architecture and OSInfo system attributes (#154)
* Add CPU, Architecture and OSInfo system attributes Add OSInfo name, version and buildNumber Add CPU/Device architecture Fix uname machine and model attributes Add isSimulator attribute
1 parent 1b64036 commit 44b2bf5

File tree

3 files changed

+168
-18
lines changed

3 files changed

+168
-18
lines changed

Sources/Features/Attributes/DefaultAttributes.swift

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,30 @@ struct Device: AttributesSource {
101101
#endif
102102
}
103103

104-
var immutable: [String: Any?] {
105-
return [
106-
"device.machine": try? System.machine(),
107-
"device.model": try? System.machine(),
108-
"uname.sysname": getSysname()
109-
]
104+
var immutable: [String : Any?] {
105+
var result: [String : Any?] = [:]
106+
107+
let architecture = CPU.architecture()
108+
result["device.arch"] = architecture
109+
result["cpu.arch"] = architecture
110+
111+
let sysname = OSInfo.name
112+
result["uname.sysname"] = sysname
113+
114+
let machine = try? System.machine()
115+
result["uname.machine"] = machine
116+
result["device.machine"] = machine
117+
118+
let model = try? System.model()
119+
result["device.model"] = model
120+
121+
result["device.isSimulator"] = isSimulator()
122+
123+
result["device.osName"] = OSInfo.name
124+
result["device.osVersion"] = OSInfo.version
125+
result["device.osBuildNumber"] = OSInfo.buildNumber
126+
127+
return result
110128
}
111129

112130
private func getSysname() -> String {
@@ -120,6 +138,14 @@ struct Device: AttributesSource {
120138
return "Unsupported device"
121139
#endif
122140
}
141+
142+
func isSimulator() -> Bool {
143+
#if targetEnvironment(simulator)
144+
return true
145+
#else
146+
return false
147+
#endif
148+
}
123149
}
124150

125151
struct ScreenInfo: AttributesSource {

Sources/Features/Attributes/System.swift

Lines changed: 131 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Foundation
2+
import MachO
3+
import Darwin
24

35
struct Statistics {
46

@@ -110,21 +112,39 @@ struct SystemControl {
110112
return data
111113
}
112114

113-
static func string(mib: [Int32]) throws -> String {
114-
guard let string = try bytes(mib: mib).withUnsafeBufferPointer({ dataPointer -> String? in
115-
dataPointer.baseAddress.flatMap { String(validatingUTF8: $0) }
116-
}) else {
117-
throw CodingError.encodingFailed
118-
}
119-
return string
120-
}
121-
122115
static func value<T>(mib: [Int32]) throws -> T {
123116
return try bytes(mib: mib).withUnsafeBufferPointer({ (buffer) throws -> T in
124117
guard let baseAddress = buffer.baseAddress else { throw KernError.unexpected }
125118
return baseAddress.withMemoryRebound(to: T.self, capacity: 1, { $0.pointee })
126119
})
127120
}
121+
122+
static func string(forKeys keys: [Int32]) throws -> String {
123+
var keys = keys
124+
var size = 0
125+
if sysctl(&keys, u_int(keys.count), nil, &size, nil, 0) != 0 {
126+
throw SysctlError.sysctlFailed("Failed to get size of sysctl data.")
127+
}
128+
129+
var data = [CChar](repeating: 0, count: size)
130+
if sysctl(&keys, u_int(keys.count), &data, &size, nil, 0) != 0 {
131+
throw SysctlError.sysctlFailed("Failed to get sysctl data.")
132+
}
133+
134+
guard let result = String(cString: data, encoding: .utf8) else {
135+
throw SysctlError.invalidUTF8("Failed to convert sysctl data to string.")
136+
}
137+
return result.trimmingCharacters(in: .whitespacesAndNewlines)
138+
}
139+
140+
static func integer<T: FixedWidthInteger>(forName name: String) throws -> T {
141+
var size = MemoryLayout<T>.stride
142+
var value = T(0)
143+
if sysctlbyname(name, &value, &size, nil, 0) != 0 {
144+
throw SysctlError.sysctlFailed("Failed to read \(name).")
145+
}
146+
return value
147+
}
128148
}
129149

130150
struct MemoryInfo {
@@ -238,13 +258,112 @@ struct System {
238258
}
239259
return currentTime.tv_sec - bootTime
240260
}
241-
261+
242262
static func machine() throws -> String {
243-
return try SystemControl.string(mib: [CTL_HW, HW_MACHINE])
263+
return (try? SystemControl.string(forKeys: [CTL_HW, HW_MACHINE])) ?? "unknown"
244264
}
245265

246266
static func model() throws -> String {
247-
return try SystemControl.string(mib: [CTL_HW, HW_MODEL])
267+
#if os(iOS) || os(tvOS) || os(watchOS)
268+
return (try? SystemControl.string(forKeys: [CTL_HW, HW_MACHINE])) ?? "unknown"
269+
#else
270+
return (try? SystemControl.string(forKeys: [CTL_HW, HW_MODEL])) ?? "unknown"
271+
#endif
272+
}
273+
}
274+
275+
struct CPU {
276+
static func architecture() -> String {
277+
do {
278+
let cpuType: cpu_type_t = try SystemControl.integer(forName: "hw.cputype")
279+
let cpuSubType: cpu_subtype_t = try SystemControl.integer(forName: "hw.cpusubtype")
280+
281+
return architectureString(cpuType: cpuType, cpuSubType: cpuSubType)
282+
} catch {
283+
return "unknown"
284+
}
285+
}
286+
287+
private static func architectureString(cpuType: cpu_type_t, cpuSubType: cpu_subtype_t) -> String {
288+
switch cpuType {
289+
case CPU_TYPE_X86:
290+
switch cpuSubType {
291+
case CPU_SUBTYPE_X86_64_H:
292+
return "x86_64h"
293+
case CPU_SUBTYPE_X86_64_ALL:
294+
return "x86_64"
295+
default:
296+
return "x86"
297+
}
298+
case CPU_TYPE_X86_64:
299+
return "x86_64"
300+
301+
case CPU_TYPE_ARM:
302+
switch cpuSubType {
303+
case CPU_SUBTYPE_ARM_V6:
304+
return "armv6"
305+
case CPU_SUBTYPE_ARM_V7:
306+
return "armv7"
307+
case CPU_SUBTYPE_ARM_V7S:
308+
return "armv7s"
309+
case CPU_SUBTYPE_ARM_V7K:
310+
return "armv7k"
311+
default:
312+
return "arm"
313+
}
314+
315+
case CPU_TYPE_ARM64, CPU_TYPE_ARM64_32:
316+
switch cpuSubType {
317+
case CPU_SUBTYPE_ARM64_V8:
318+
return "armv8"
319+
case CPU_SUBTYPE_ARM64E:
320+
return "arm64e"
321+
default:
322+
return "arm64"
323+
}
324+
325+
default:
326+
return "unknown (type=\(cpuType), subType=\(cpuSubType))"
327+
}
328+
}
329+
}
330+
331+
struct OSInfo {
332+
static var name: String {
333+
#if os(macOS)
334+
return "macOS"
335+
#elseif os(tvOS)
336+
return "tvOS"
337+
#elseif os(watchOS)
338+
return "watchOS"
339+
#elseif os(iOS) && !targetEnvironment(macCatalyst)
340+
return UIDevice.current.systemName
341+
#elseif os(iOS) && targetEnvironment(macCatalyst)
342+
return "Catalyst"
343+
#else
344+
return "unknownOS"
345+
#endif
346+
}
347+
348+
static var version: String {
349+
#if os(iOS) && !targetEnvironment(macCatalyst)
350+
return UIDevice.current.systemVersion
351+
#elseif os(watchOS)
352+
let version = ProcessInfo.processInfo.operatingSystemVersion
353+
return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
354+
#else
355+
let version = ProcessInfo.processInfo.operatingSystemVersion
356+
return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
357+
#endif
358+
}
359+
360+
static var buildNumber: String {
361+
let mib = [CTL_KERN, KERN_OSVERSION]
362+
do {
363+
return try SystemControl.string(forKeys: mib)
364+
} catch {
365+
return "unknown"
366+
}
248367
}
249368
}
250369

Sources/Features/Error/BacktraceError.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ enum CodingError: BacktraceError {
3939
case encodingFailed
4040
}
4141

42+
enum SysctlError: BacktraceError {
43+
case sysctlFailed(String?)
44+
case invalidUTF8(String?)
45+
}
46+
4247
extension HttpError {
4348
var backtraceStatus: BacktraceReportStatus {
4449
switch self {

0 commit comments

Comments
 (0)