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
10 changes: 5 additions & 5 deletions lib/data/model/app/scripts/script_builders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -256,21 +256,21 @@ if [ "\$macSign" = "" ] && [ "\$bsdSign" = "" ]; then
\tif [ "\$isBusybox" != "" ]; then
\t\tps w
\telse
\t\tprintf 'PID USER %%CPU %%MEM VSZ RSS TTY STAT START TIME READ_BYTES WRITE_BYTES COMMAND\\n'
\t\tps -axo pid=,user=,%cpu=,%mem=,vsz=,rss=,tty=,stat=,start=,time=,args= | while IFS= read -r line; do
\t\tprintf 'PID USER %%CPU %%MEM VSZ RSS TTY STAT TIME READ_BYTES WRITE_BYTES COMMAND\\n'
\t\tps -axo pid=,user=,%cpu=,%mem=,vsz=,rss=,tty=,stat=,time=,args= | while IFS= read -r line; do
\t\t\tset -f
\t\t\tset -- \$line
\t\t\tset +f
\t\t\tpid=\$1; user=\$2; cpu=\$3; mem=\$4; vsz=\$5; rss=\$6; tty=\$7; stat=\$8; start=\$9; time=\${10}
\t\t\tshift 10
\t\t\tpid=\$1; user=\$2; cpu=\$3; mem=\$4; vsz=\$5; rss=\$6; tty=\$7; stat=\$8; time=\$9
\t\t\tshift 9
\t\t\tcmd=\$*
\t\t\tread_bytes='-'
\t\t\twrite_bytes='-'
\t\t\tif [ -r "/proc/\$pid/io" ]; then
\t\t\t\tread_bytes=\$(awk '/^read_bytes:/ {print \$2}' "/proc/\$pid/io")
\t\t\t\twrite_bytes=\$(awk '/^write_bytes:/ {print \$2}' "/proc/\$pid/io")
\t\t\tfi
\t\t\tprintf '%s %s %s %s %s %s %s %s %s %s %s %s %s\\n' "\$pid" "\$user" "\$cpu" "\$mem" "\$vsz" "\$rss" "\$tty" "\$stat" "\$start" "\$time" "\$read_bytes" "\$write_bytes" "\$cmd"
\t\t\tprintf '%s %s %s %s %s %s %s %s %s %s %s %s\\n' "\$pid" "\$user" "\$cpu" "\$mem" "\$vsz" "\$rss" "\$tty" "\$stat" "\$time" "\$read_bytes" "\$write_bytes" "\$cmd"
\t\tdone
\tfi
else
Expand Down
105 changes: 76 additions & 29 deletions lib/data/model/server/proc.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import 'dart:convert';

import 'package:server_box/data/res/misc.dart';

final _whitespaceRegExp = RegExp(r'\s+');

class _ProcValIdxMap {
Expand Down Expand Up @@ -55,6 +53,7 @@ class Proc {
final String command;

late final binary = _parseBinary();
late final args = _parseArgs();
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Proc({
this.user,
Expand All @@ -74,6 +73,51 @@ class Proc {
required this.command,
});

// Value equality based on all parsed fields lets ListView skip rebuilding
// rows whose underlying process data is unchanged between refreshes, which
// is the common case for idle processes. `binary` is derived from `command`
// so it is intentionally excluded to avoid forcing its lazy initialization
// during comparisons.
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Proc &&
runtimeType == other.runtimeType &&
user == other.user &&
pid == other.pid &&
cpu == other.cpu &&
mem == other.mem &&
vsz == other.vsz &&
rss == other.rss &&
tty == other.tty &&
stat == other.stat &&
start == other.start &&
time == other.time &&
readBytes == other.readBytes &&
writeBytes == other.writeBytes &&
readSpeed == other.readSpeed &&
writeSpeed == other.writeSpeed &&
command == other.command;

@override
int get hashCode => Object.hash(
user,
pid,
cpu,
mem,
vsz,
rss,
tty,
stat,
start,
time,
readBytes,
writeBytes,
readSpeed,
writeSpeed,
command,
);

factory Proc._parse(
String raw,
_ProcValIdxMap map, {
Expand Down Expand Up @@ -141,35 +185,18 @@ class Proc {
);
}

Map toJson() {
return {
'user': user,
'pid': pid,
'cpu': cpu,
'mem': mem,
'vsz': vsz,
'rss': rss,
'tty': tty,
'stat': stat,
'start': start,
'time': time,
'readBytes': readBytes,
'writeBytes': writeBytes,
'readSpeed': readSpeed,
'writeSpeed': writeSpeed,
'command': command,
};
}

@override
String toString() {
return Miscs.jsonEncoder.convert(toJson());
}

String _parseBinary() {
final parts = command.trim().split(' ').where((e) => e.isNotEmpty).toList();
return parts.isNotEmpty ? parts[0] : '';
}

String _parseArgs() {
final trimmed = command.trim();
if (trimmed.isEmpty) return '';
final binary = this.binary;
if (binary.isEmpty || trimmed.length <= binary.length) return '';
return trimmed.substring(binary.length).trimLeft();
}
}

// `ps -aux` result
Expand Down Expand Up @@ -215,8 +242,18 @@ class PsResult {
final header = lines[0];
final parts = header.split(_whitespaceRegExp);
parts.removeWhere((element) => element.isEmpty);
final pidIdx = parts.indexOfOrNull('PID');
final commandIdx =
parts.indexOfOrNull('COMMAND') ?? parts.indexOfOrNull('CMD');
if (pidIdx == null || commandIdx == null) {
return PsResult(
procs: const [],
error: 'Unsupported process output header: $header',
sampledAtMillis: currentSampledAtMillis,
);
}
final map = _ProcValIdxMap(
pid: parts.indexOfOrNull('PID')!,
pid: pidIdx,
user: parts.indexOfOrNull('USER'),
cpu: parts.indexOfOrNull('%CPU'),
mem: parts.indexOfOrNull('%MEM'),
Expand All @@ -228,7 +265,7 @@ class PsResult {
time: parts.indexOfOrNull('TIME'),
readBytes: parts.indexOfOrNull('READ_BYTES'),
writeBytes: parts.indexOfOrNull('WRITE_BYTES'),
command: parts.indexOfOrNull('COMMAND') ?? parts.indexOfOrNull('CMD')!,
command: commandIdx,
);

final procs = <Proc>[];
Expand Down Expand Up @@ -301,6 +338,16 @@ class PsResult {
}
}

PsResult sortedBy(ProcSortMode sort) {
final sorted = List<Proc>.of(procs);
_sort(sorted, sort);
return PsResult(
procs: sorted,
error: error,
sampledAtMillis: sampledAtMillis,
);
}

static void _sort(List<Proc> procs, ProcSortMode sort) {
switch (sort) {
case ProcSortMode.cpu:
Expand Down
2 changes: 2 additions & 0 deletions lib/data/res/github_id.dart
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ abstract final class GithubIds {
'shen-lan',
'Sahib911',
'SiLong96',
'YCyingchen',
'hiSandog',
};
}

Expand Down
Loading