mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
- add top processes to RAM module
This commit is contained in:
@@ -57,7 +57,6 @@ public class CPU: Module {
|
||||
self.loadReader?.store = store
|
||||
|
||||
self.processReader = ProcessReader()
|
||||
self.processReader?.store = store
|
||||
|
||||
self.settingsView.callback = { [unowned self] in
|
||||
self.loadReader?.read()
|
||||
|
||||
@@ -32,28 +32,16 @@ internal class Popup: NSView {
|
||||
private var ready: Bool = false
|
||||
|
||||
private var processes: [ProcessView] = []
|
||||
private var processesView: NSView? = nil
|
||||
|
||||
private var topProcessState: Bool {
|
||||
get {
|
||||
return self.store.pointee.bool(key: "\(self.title)_topProcesses", defaultValue: false)
|
||||
}
|
||||
}
|
||||
|
||||
public init(_ title: String, store: UnsafePointer<Store>) {
|
||||
self.store = store
|
||||
self.title = title
|
||||
|
||||
let topProcessState = store.pointee.bool(key: "\(title)_topProcesses", defaultValue: false)
|
||||
let height = topProcessState ? dashboardHeight + (Constants.Popup.separatorHeight*2) + detailsHeight + processesHeight : dashboardHeight + Constants.Popup.separatorHeight + detailsHeight
|
||||
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: height))
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: dashboardHeight + (Constants.Popup.separatorHeight*2) + detailsHeight + processesHeight))
|
||||
|
||||
initDashboard()
|
||||
initDetails()
|
||||
if topProcessState {
|
||||
self.initProcesses()
|
||||
}
|
||||
initProcesses()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -102,16 +90,15 @@ internal class Popup: NSView {
|
||||
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.processesHeight))
|
||||
|
||||
processes.append(ProcessView(0))
|
||||
processes.append(ProcessView(1))
|
||||
processes.append(ProcessView(2))
|
||||
processes.append(ProcessView(3))
|
||||
processes.append(ProcessView(4))
|
||||
self.processes.append(ProcessView(0))
|
||||
self.processes.append(ProcessView(1))
|
||||
self.processes.append(ProcessView(2))
|
||||
self.processes.append(ProcessView(3))
|
||||
self.processes.append(ProcessView(4))
|
||||
|
||||
processes.forEach{ view.addSubview($0) }
|
||||
self.processes.forEach{ view.addSubview($0) }
|
||||
|
||||
self.addSubview(view)
|
||||
self.processesView = view
|
||||
}
|
||||
|
||||
private func addFirstRow(mView: NSView, y: CGFloat, title: String, value: String) -> NSTextField {
|
||||
@@ -174,49 +161,3 @@ internal class Popup: NSView {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private class ProcessView: NSView {
|
||||
public var width: CGFloat {
|
||||
get { return 0 }
|
||||
set {
|
||||
self.setFrameSize(NSSize(width: newValue, height: self.frame.height))
|
||||
}
|
||||
}
|
||||
|
||||
public var label: String {
|
||||
get { return "" }
|
||||
set {
|
||||
self.labelView?.stringValue = newValue
|
||||
}
|
||||
}
|
||||
public var value: String {
|
||||
get { return "" }
|
||||
set {
|
||||
self.valueView?.stringValue = newValue
|
||||
}
|
||||
}
|
||||
|
||||
private var labelView: LabelField? = nil
|
||||
private var valueView: ValueField? = nil
|
||||
|
||||
init(_ n: CGFloat) {
|
||||
super.init(frame: NSRect(x: 0, y: n*22, width: Constants.Popup.width, height: 16))
|
||||
|
||||
let rowView: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: 16))
|
||||
|
||||
let labelView: LabelField = LabelField(frame: NSRect(x: 0, y: 0.5, width: rowView.frame.width - 50, height: 15), "")
|
||||
let valueView: ValueField = ValueField(frame: NSRect(x: rowView.frame.width - 50, y: 0, width: 50, height: 16), "")
|
||||
|
||||
rowView.addSubview(labelView)
|
||||
rowView.addSubview(valueView)
|
||||
|
||||
self.labelView = labelView
|
||||
self.valueView = valueView
|
||||
|
||||
self.addSubview(rowView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,10 +150,6 @@ internal class LoadReader: Reader<CPU_Load> {
|
||||
}
|
||||
|
||||
public class ProcessReader: Reader<[TopProcess]> {
|
||||
public var store: UnsafePointer<Store>? = nil
|
||||
|
||||
private var loadPrevious = host_cpu_load_info()
|
||||
|
||||
public override func setup() {
|
||||
self.popup = true
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public struct RAM_Usage: value_t {
|
||||
public class Memory: Module {
|
||||
private let popupView: Popup = Popup()
|
||||
private var usageReader: UsageReader? = nil
|
||||
private var processReader: ProcessReader? = nil
|
||||
private var settingsView: Settings
|
||||
|
||||
public init(_ store: UnsafePointer<Store>) {
|
||||
@@ -53,6 +54,7 @@ public class Memory: Module {
|
||||
}
|
||||
|
||||
self.usageReader = UsageReader()
|
||||
self.processReader = ProcessReader()
|
||||
|
||||
self.usageReader?.readyCallback = { [unowned self] in
|
||||
self.readyHandler()
|
||||
@@ -61,9 +63,18 @@ public class Memory: Module {
|
||||
self.loadCallback(value: value)
|
||||
}
|
||||
|
||||
self.processReader?.callbackHandler = { [unowned self] value in
|
||||
if let list = value {
|
||||
self.popupView.processCallback(list)
|
||||
}
|
||||
}
|
||||
|
||||
if let reader = self.usageReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
if let reader = self.processReader {
|
||||
self.addReader(reader)
|
||||
}
|
||||
}
|
||||
|
||||
private func loadCallback(value: RAM_Usage?) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import StatsKit
|
||||
internal class Popup: NSView {
|
||||
private let dashboardHeight: CGFloat = 90
|
||||
private let detailsHeight: CGFloat = 66
|
||||
private let processesHeight: CGFloat = 22*5
|
||||
|
||||
private var totalField: NSTextField? = nil
|
||||
private var usedField: NSTextField? = nil
|
||||
@@ -29,11 +30,14 @@ internal class Popup: NSView {
|
||||
private var chart: LineChartView? = nil
|
||||
private var initialized: Bool = false
|
||||
|
||||
private var processes: [ProcessView] = []
|
||||
|
||||
public init() {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: dashboardHeight + Constants.Popup.separatorHeight + detailsHeight))
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: Constants.Popup.width, height: dashboardHeight + (Constants.Popup.separatorHeight*2) + detailsHeight + processesHeight))
|
||||
|
||||
initFirstView()
|
||||
initDetails()
|
||||
initProcesses()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
@@ -78,6 +82,23 @@ internal class Popup: NSView {
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
private func initProcesses() {
|
||||
let separator = SeparatorView("Top processes", origin: NSPoint(x: 0, y: self.processesHeight), width: self.frame.width)
|
||||
self.addSubview(separator)
|
||||
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 0, width: self.frame.width, height: self.processesHeight))
|
||||
|
||||
self.processes.append(ProcessView(0))
|
||||
self.processes.append(ProcessView(1))
|
||||
self.processes.append(ProcessView(2))
|
||||
self.processes.append(ProcessView(3))
|
||||
self.processes.append(ProcessView(4))
|
||||
|
||||
self.processes.forEach{ view.addSubview($0) }
|
||||
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
private func addFirstRow(mView: NSView, y: CGFloat, title: String, value: String) -> NSTextField {
|
||||
let rowView: NSView = NSView(frame: NSRect(x: 0, y: y, width: mView.frame.width, height: 16))
|
||||
|
||||
@@ -116,4 +137,17 @@ internal class Popup: NSView {
|
||||
self.chart?.addValue(value.usage!)
|
||||
})
|
||||
}
|
||||
|
||||
public func processCallback(_ list: [TopProcess]) {
|
||||
DispatchQueue.main.async(execute: {
|
||||
for i in 0..<list.count {
|
||||
let process = list[i]
|
||||
let index = list.count-i-1
|
||||
if self.processes.indices.contains(index) {
|
||||
self.processes[index].label = process.command
|
||||
self.processes[index].value = Units(bytes: Int64(process.usage)).getReadableMemory()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import StatsKit
|
||||
import ModuleKit
|
||||
|
||||
internal class UsageReader: Reader<RAM_Usage> {
|
||||
@@ -76,3 +77,60 @@ internal class UsageReader: Reader<RAM_Usage> {
|
||||
print("Error with host_statistics64(): " + (String(cString: mach_error_string(result), encoding: String.Encoding.ascii) ?? "unknown error"))
|
||||
}
|
||||
}
|
||||
|
||||
public class ProcessReader: Reader<[TopProcess]> {
|
||||
public override func setup() {
|
||||
self.popup = true
|
||||
}
|
||||
|
||||
public override func read() {
|
||||
let task = Process()
|
||||
task.launchPath = "/usr/bin/top"
|
||||
task.arguments = ["-l", "1", "-o", "mem", "-n", "5", "-stats", "pid,command,mem"]
|
||||
|
||||
let outputPipe = Pipe()
|
||||
let errorPipe = Pipe()
|
||||
|
||||
task.standardOutput = outputPipe
|
||||
task.standardError = errorPipe
|
||||
|
||||
do {
|
||||
try task.run()
|
||||
} catch let error {
|
||||
print(error)
|
||||
return
|
||||
}
|
||||
|
||||
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let errorData = errorPipe.fileHandleForReading.readDataToEndOfFile()
|
||||
let output = String(decoding: outputData, as: UTF8.self)
|
||||
_ = String(decoding: errorData, as: UTF8.self)
|
||||
|
||||
if output.isEmpty {
|
||||
return
|
||||
}
|
||||
|
||||
var processes: [TopProcess] = []
|
||||
output.enumerateLines { (line, stop) -> () in
|
||||
if line.matches("^\\d+ + .+ +\\d+.\\d[M\\+\\-]+ *$") {
|
||||
var str = line.trimmingCharacters(in: .whitespaces)
|
||||
let pidString = str.findAndCrop(pattern: "^\\d+")
|
||||
let usageString = str.findAndCrop(pattern: " [0-9]+M(\\+|\\-)*$")
|
||||
var command = str.trimmingCharacters(in: .whitespaces)
|
||||
|
||||
if let regex = try? NSRegularExpression(pattern: " (\\+|\\-)*$", options: .caseInsensitive) {
|
||||
command = regex.stringByReplacingMatches(in: command, options: [], range: NSRange(location: 0, length: command.count), withTemplate: "")
|
||||
}
|
||||
|
||||
let pid = Int(pidString) ?? 0
|
||||
guard let usage = Double(usageString.filter("01234567890.".contains)) else {
|
||||
return
|
||||
}
|
||||
let process = TopProcess(pid: pid, command: command, usage: usage * Double(1024 * 1024))
|
||||
processes.append(process)
|
||||
}
|
||||
}
|
||||
|
||||
self.callback(processes)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user