Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 1 addition & 45 deletions CLI/CMUXCLI+Memory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ extension CMUXCLI {
let processCount = padLeft(String(topInt(group["process_count"]) ?? 0), width: 5)
let name = topLabelText(group["name"] as? String)
let command = name.padding(toLength: 26, withPad: " ", startingAt: 0)
let attribution = memoryAttributionText(group["top_attribution"], idFormat: idFormat)
let attribution = memoryGroupAttributionText(group, idFormat: idFormat)
lines.append("\(rss) \(processCount) \(command) \(attribution)")
}

Expand All @@ -182,48 +182,4 @@ extension CMUXCLI {
)
}

private func memoryAttributionText(_ raw: Any?, idFormat: CLIIDFormat) -> String {
guard let attribution = raw as? [String: Any] else {
return String(localized: "cli.memory.output.unattributed", defaultValue: "unattributed")
}

var parts: [String] = []
if let workspace = memoryAttributionHandle(attribution, prefix: "workspace", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.workspaceAttribution", defaultValue: "workspace %@"),
workspace
))
}
if let pane = memoryAttributionHandle(attribution, prefix: "pane", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.paneAttribution", defaultValue: "pane %@"),
pane
))
}
if let surface = memoryAttributionHandle(attribution, prefix: "surface", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.surfaceAttribution", defaultValue: "surface %@"),
surface
))
}
return parts.isEmpty ? String(localized: "cli.memory.output.unattributed", defaultValue: "unattributed") : parts.joined(separator: " / ")
}

private func memoryAttributionHandle(
_ attribution: [String: Any],
prefix: String,
idFormat: CLIIDFormat
) -> String? {
let ref = topLabelText(attribution["\(prefix)_ref"] as? String)
let id = topLabelText(attribution["\(prefix)_id"] as? String)
switch idFormat {
case .refs:
return ref.isEmpty ? (id.isEmpty ? nil : id) : ref
case .uuids:
return id.isEmpty ? (ref.isEmpty ? nil : ref) : id
case .both:
let values = [ref, id].filter { !$0.isEmpty }
return values.isEmpty ? nil : values.joined(separator: " ")
}
}
}
79 changes: 79 additions & 0 deletions CLI/CMUXCLI+MemoryAttribution.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import Foundation

extension CMUXCLI {
func memoryGroupAttributionText(
_ group: [String: Any],
idFormat: CLIIDFormat
) -> String {
guard let groupAttribution = group["group_attribution"] as? [String: Any],
let kind = groupAttribution["kind"] as? String else {
return memoryAttributionText(group["top_attribution"], idFormat: idFormat)
}
switch kind {
case "common":
return memoryAttributionText(groupAttribution["owner"], idFormat: idFormat)
case "multiple":
let workspaceCount = topInt(groupAttribution["workspace_count"]) ?? 0
if workspaceCount > 1 {
return String.localizedStringWithFormat(
String(localized: "memory.attribution.multipleWorkspaces", defaultValue: "%lld workspaces"),
workspaceCount
)
}
return String(localized: "memory.attribution.multipleOwners", defaultValue: "multiple owners")
case "partial":
return String(localized: "memory.attribution.partial", defaultValue: "partially attributed")
case "unattributed":
return String(localized: "cli.memory.output.unattributed", defaultValue: "unattributed")
default:
return memoryAttributionText(group["top_attribution"], idFormat: idFormat)
}
}

private func memoryAttributionText(_ raw: Any?, idFormat: CLIIDFormat) -> String {
guard let attribution = raw as? [String: Any] else {
return String(localized: "cli.memory.output.unattributed", defaultValue: "unattributed")
}

var parts: [String] = []
if let workspace = memoryAttributionHandle(attribution, prefix: "workspace", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.workspaceAttribution", defaultValue: "workspace %@"),
workspace
))
}
if let pane = memoryAttributionHandle(attribution, prefix: "pane", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.paneAttribution", defaultValue: "pane %@"),
pane
))
}
if let surface = memoryAttributionHandle(attribution, prefix: "surface", idFormat: idFormat) {
parts.append(String.localizedStringWithFormat(
String(localized: "cli.memory.output.surfaceAttribution", defaultValue: "surface %@"),
surface
))
}
return parts.isEmpty
? String(localized: "cli.memory.output.unattributed", defaultValue: "unattributed")
: parts.joined(separator: " / ")
}

private func memoryAttributionHandle(
_ attribution: [String: Any],
prefix: String,
idFormat: CLIIDFormat
) -> String? {
let ref = topLabelText(attribution["\(prefix)_ref"] as? String)
let id = topLabelText(attribution["\(prefix)_id"] as? String)
switch idFormat {
case .refs:
return ref.isEmpty ? (id.isEmpty ? nil : id) : ref
case .uuids:
return id.isEmpty ? (ref.isEmpty ? nil : ref) : id
case .both:
let values = [ref, id].filter { !$0.isEmpty }
return values.isEmpty ? nil : values.joined(separator: " ")
}
}
}
51 changes: 51 additions & 0 deletions Resources/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -115711,6 +115711,57 @@
}
}
},
"memory.attribution.multipleOwners": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "multiple owners"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "複数の帰属先"
}
}
}
},
"memory.attribution.multipleWorkspaces": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "%lld workspaces"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "%lld ワークスペース"
}
}
}
},
"memory.attribution.partial": {
"extractionState": "manual",
"localizations": {
"en": {
"stringUnit": {
"state": "translated",
"value": "partially attributed"
}
},
"ja": {
"stringUnit": {
"state": "translated",
"value": "一部のみ帰属"
}
}
}
},
"memoryDiagnostic.summary.base": {
"extractionState": "manual",
"localizations": {
Expand Down
67 changes: 67 additions & 0 deletions Sources/CmuxTaskManagerMemoryAttribution.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import Foundation

/// Identifies a Task Manager process owner decoded from a diagnostic payload.
struct CmuxTaskManagerMemoryAttribution: Sendable {
let workspaceId: UUID?
let workspaceRef: String?
let paneId: UUID?
let paneRef: String?
let surfaceId: UUID?
let surfaceRef: String?
let surfaceType: String?

init?(_ payload: [String: Any]?) {
guard let payload else { return nil }
self.workspaceId = Self.uuid(payload["workspace_id"])
self.workspaceRef = CmuxTaskManagerMemoryDiagnostic.string(payload["workspace_ref"])
self.paneId = Self.uuid(payload["pane_id"])
self.paneRef = CmuxTaskManagerMemoryDiagnostic.string(payload["pane_ref"])
self.surfaceId = Self.uuid(payload["surface_id"])
self.surfaceRef = CmuxTaskManagerMemoryDiagnostic.string(payload["surface_ref"])
self.surfaceType = CmuxTaskManagerMemoryDiagnostic.string(payload["surface_type"])
if workspaceId == nil,
workspaceRef == nil,
paneId == nil,
paneRef == nil,
surfaceId == nil,
surfaceRef == nil,
surfaceType == nil {
return nil
}
}

var localizedDescription: String {
var parts: [String] = []
if let workspace = workspaceRef ?? workspaceId?.uuidString {
parts.append(String.localizedStringWithFormat(
String(localized: "taskManager.memory.workspace", defaultValue: "Workspace %@"),
workspace
))
}
if let pane = paneRef ?? paneId?.uuidString {
parts.append(String.localizedStringWithFormat(
String(localized: "taskManager.memory.pane", defaultValue: "Pane %@"),
pane
))
}
if let surface = surfaceRef ?? surfaceId?.uuidString {
parts.append(String.localizedStringWithFormat(
String(localized: "taskManager.memory.surface", defaultValue: "Surface %@"),
surface
))
}
return parts.isEmpty
? String(localized: "taskManager.memory.unattributed", defaultValue: "Unattributed")
: parts.joined(separator: " / ")
}

private static func uuid(_ raw: Any?) -> UUID? {
if let value = raw as? UUID {
return value
}
guard let value = CmuxTaskManagerMemoryDiagnostic.string(raw) else {
return nil
}
return UUID(uuidString: value)
}
}
30 changes: 30 additions & 0 deletions Sources/CmuxTaskManagerMemoryGroup.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

/// Decodes one command-name aggregate from the memory diagnostic payload.
struct CmuxTaskManagerMemoryGroup: Sendable {
let id: String
let name: String
let rssBytes: Int64
let processCount: Int
let processIds: [Int]
let attribution: CmuxTaskManagerMemoryGroupAttribution

init?(_ payload: [String: Any]) {
guard let name = CmuxTaskManagerMemoryDiagnostic.string(payload["name"]) else {
return nil
}
let processCount = CmuxTaskManagerMemoryDiagnostic.int(payload["process_count"]) ?? 0
guard processCount > 0 else { return nil }
self.id = CmuxTaskManagerMemoryDiagnostic.string(payload["id"]) ?? name.lowercased()
self.name = name
self.rssBytes = CmuxTaskManagerMemoryDiagnostic.int64(payload["rss_bytes"])
self.processCount = processCount
self.processIds = CmuxTaskManagerMemoryDiagnostic.intArray(payload["pids"])
let topAttribution = CmuxTaskManagerMemoryAttribution(payload["top_attribution"] as? [String: Any])
self.attribution = CmuxTaskManagerMemoryGroupAttribution(
payload["group_attribution"] as? [String: Any] ?? [:]
)
?? topAttribution.map(CmuxTaskManagerMemoryGroupAttribution.common)
?? .unattributed
}
}
65 changes: 65 additions & 0 deletions Sources/CmuxTaskManagerMemoryGroupAttribution.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Foundation

/// Describes whether a memory group has one common owner, several owners, or incomplete attribution.
enum CmuxTaskManagerMemoryGroupAttribution: Sendable {
case common(CmuxTaskManagerMemoryAttribution)
case multiple(workspaceCount: Int, ownerCount: Int)
case partial(attributedProcessCount: Int, unattributedProcessCount: Int)
case unattributed

init?(_ payload: [String: Any]) {
guard let kind = CmuxTaskManagerMemoryDiagnostic.string(payload["kind"]) else {
return nil
}
switch kind {
case "common":
guard let owner = CmuxTaskManagerMemoryAttribution(payload["owner"] as? [String: Any]) else {
return nil
}
self = .common(owner)
case "multiple":
self = .multiple(
workspaceCount: CmuxTaskManagerMemoryDiagnostic.int(payload["workspace_count"]) ?? 0,
ownerCount: CmuxTaskManagerMemoryDiagnostic.int(payload["owner_count"]) ?? 0
)
case "partial":
self = .partial(
attributedProcessCount: CmuxTaskManagerMemoryDiagnostic.int(
payload["attributed_process_count"]
) ?? 0,
unattributedProcessCount: CmuxTaskManagerMemoryDiagnostic.int(
payload["unattributed_process_count"]
) ?? 0
)
case "unattributed":
self = .unattributed
default:
return nil
}
}

var commonOwner: CmuxTaskManagerMemoryAttribution? {
guard case .common(let owner) = self else { return nil }
return owner
}

var localizedDescription: String {
switch self {
case .common(let owner):
owner.localizedDescription
case .multiple(let workspaceCount, _):
if workspaceCount > 1 {
String.localizedStringWithFormat(
String(localized: "memory.attribution.multipleWorkspaces", defaultValue: "%lld workspaces"),
workspaceCount
)
} else {
String(localized: "memory.attribution.multipleOwners", defaultValue: "multiple owners")
}
case .partial:
String(localized: "memory.attribution.partial", defaultValue: "partially attributed")
case .unattributed:
String(localized: "taskManager.memory.unattributed", defaultValue: "Unattributed")
}
}
}
Loading
Loading