system-monitor-graph@rcassani: Fix Stuttering (#938)

* read files directly without using 'cat'/'head' to improve performance and use communicate_utf8_async for async command execution

* bump version
This commit is contained in:
Georg Sieber 2023-10-26 19:59:04 +02:00 committed by GitHub
parent 16defd7701
commit ffa89bedca
2 changed files with 117 additions and 118 deletions

233
files/system-monitor-graph@rcassani/desklet.js Normal file → Executable file
View File

@ -366,81 +366,80 @@ SystemMonitorGraph.prototype = {
Mainloop.source_remove(this.timeout); Mainloop.source_remove(this.timeout);
}, },
cpu_file: Gio.file_new_for_path('/proc/stat'),
get_cpu_use: function() { get_cpu_use: function() {
// https://rosettacode.org/wiki/Linux_CPU_utilization // https://rosettacode.org/wiki/Linux_CPU_utilization
let subprocess = new Gio.Subprocess({ this.cpu_file.load_contents_async(null, (file, response) => {
argv: ['head', '-n', '1', '/proc/stat'], let [success, contents, tag] = file.load_contents_finish(response);
flags: Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE, if(success) {
}); let cpu_values = ByteArray.toString(contents).split("\n")[0].split(/\s+/);
subprocess.init(null); let cpu_idl = parseFloat(cpu_values[4]);
subprocess.wait_async(null, (sourceObject, res) => { let cpu_tot = 0;
let [, stdout, stderr] = sourceObject.communicate_utf8(null, null); for (let i = 1; i<10; i++){
let cpu_values = stdout.split(/\s+/); cpu_tot += parseFloat(cpu_values[i])
let cpu_idl = parseFloat(cpu_values[4]); }
let cpu_tot = 0; this.cpu_use = 100 * (1 - (cpu_idl - this.cpu_cpu_idl) / (cpu_tot - this.cpu_cpu_tot));
for (let i = 1; i<10; i++){ this.cpu_cpu_tot = cpu_tot;
cpu_tot += parseFloat(cpu_values[i]) this.cpu_cpu_idl = cpu_idl;
} }
this.cpu_use = 100 * (1 - (cpu_idl - this.cpu_cpu_idl) / (cpu_tot - this.cpu_cpu_tot)); GLib.free(contents);
this.cpu_cpu_tot = cpu_tot;
this.cpu_cpu_idl = cpu_idl;
}); });
}, },
ram_swap_file: Gio.file_new_for_path('/proc/meminfo'),
get_ram_values: function() { get_ram_values: function() {
// used = total - available // used = total - available
// while meminfo says "kb" the unit is kibibytes // while meminfo says "kb" the unit is kibibytes
let subprocess = new Gio.Subprocess({ this.ram_swap_file.load_contents_async(null, (file, response) => {
argv: ['cat', '/proc/meminfo'], let [success, contents, tag] = file.load_contents_finish(response);
flags: Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE, if(success) {
}); let mem = ByteArray.toString(contents);
subprocess.init(null); let mem_tot = parseInt(mem.match(/(MemTotal):\D+(\d+)/)[2]);
subprocess.wait_async(null, (sourceObject, res) => { let mem_usd = mem_tot - parseInt(mem.match(/(MemAvailable):\D+(\d+)/)[2]);
let [, stdout, stderr] = sourceObject.communicate_utf8(null, null); let ram_tot
let mem_tot = parseInt(stdout.match(/(MemTotal):\D+(\d+)/)[2]); let ram_usd
let mem_usd = mem_tot - parseInt(stdout.match(/(MemAvailable):\D+(\d+)/)[2]); if (this.data_prefix_ram == 1) {
let ram_tot // decimal prefix
let ram_usd ram_tot = mem_tot * 1024 / GB_TO_B;
if (this.data_prefix_ram == 1) { ram_usd = mem_usd * 1024 / GB_TO_B;
   // decimal prefix } else {
ram_tot = mem_tot * 1024 / GB_TO_B; // binary prefix
ram_usd = mem_usd * 1024 / GB_TO_B; ram_tot = mem_tot / GIB_TO_KIB;
} else { ram_usd = mem_usd / GIB_TO_KIB;
  // binary prefix }
ram_tot = mem_tot / GIB_TO_KIB; this.ram_values = [ram_tot, ram_usd];
ram_usd = mem_usd / GIB_TO_KIB;
} }
this.ram_values = [ram_tot, ram_usd]; GLib.free(contents);
}); });
}, },
get_swap_values: function() { get_swap_values: function() {
// used = total - available // used = total - available
// while meminfo says "kb" the unit is kibibytes // while meminfo says "kb" the unit is kibibytes
let subprocess = new Gio.Subprocess({ this.ram_swap_file.load_contents_async(null, (file, response) => {
argv: ['cat', '/proc/meminfo'], let [success, contents, tag] = file.load_contents_finish(response);
flags: Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE, if(success) {
}); let mem = ByteArray.toString(contents);
subprocess.init(null); let mem_tot = parseInt(mem.match(/(SwapTotal):\D+(\d+)/)[2]);
subprocess.wait_async(null, (sourceObject, res) => { let mem_usd = mem_tot - parseInt(mem.match(/(SwapFree):\D+(\d+)/)[2]);
let [, stdout, stderr] = sourceObject.communicate_utf8(null, null); let swap_tot
let mem_tot = parseInt(stdout.match(/(SwapTotal):\D+(\d+)/)[2]); let swap_usd
let mem_usd = mem_tot - parseInt(stdout.match(/(SwapFree):\D+(\d+)/)[2]); if (this.data_prefix_swap == 1) {
let swap_tot // decimal prefix
let swap_usd swap_tot = mem_tot * 1024 / GB_TO_B;
if (this.data_prefix_swap == 1) { swap_usd = mem_usd * 1024 / GB_TO_B;
   // decimal prefix } else {
swap_tot = mem_tot * 1024 / GB_TO_B; // binary prefix
swap_usd = mem_usd * 1024 / GB_TO_B; swap_tot = mem_tot / GIB_TO_KIB;
} else { swap_usd = mem_usd / GIB_TO_KIB;
  // binary prefix }
swap_tot = mem_tot / GIB_TO_KIB; this.swap_values = [swap_tot, swap_usd];
swap_usd = mem_usd / GIB_TO_KIB;
} }
this.swap_values = [swap_tot, swap_usd]; GLib.free(contents);
}); });
}, },
hdd_file: Gio.file_new_for_path('/proc/diskstats'),
get_hdd_values: function(dir_path) { get_hdd_values: function(dir_path) {
// while df says "1K" it means 1 kibibyte // while df says "1K" it means 1 kibibyte
let subprocess = new Gio.Subprocess({ let subprocess = new Gio.Subprocess({
@ -453,49 +452,51 @@ SystemMonitorGraph.prototype = {
let df_line = stdout.match(/.+/g)[1]; let df_line = stdout.match(/.+/g)[1];
let df_values = df_line.split(/\s+/); // split by space let df_values = df_line.split(/\s+/); // split by space
// values for partition space // values for partition space
let hdd_tot var hdd_tot;
let hdd_fre var hdd_fre;
if (this.data_prefix_hdd == 1) { if (this.data_prefix_hdd == 1) {
   // decimal prefix // decimal prefix
hdd_tot = parseFloat(df_values[1]) * 1024 / GB_TO_B; hdd_tot = parseFloat(df_values[1]) * 1024 / GB_TO_B;
hdd_fre = parseFloat(df_values[3]) * 1024 / GB_TO_B; hdd_fre = parseFloat(df_values[3]) * 1024 / GB_TO_B;
} else { } else {
  // binary prefix // binary prefix
hdd_tot = parseFloat(df_values[1]) / GIB_TO_KIB; hdd_tot = parseFloat(df_values[1]) / GIB_TO_KIB;
hdd_fre = parseFloat(df_values[3]) / GIB_TO_KIB; hdd_fre = parseFloat(df_values[3]) / GIB_TO_KIB;
} }
// utilization of partition // get IO utilization of partition
let dev_fs = df_values[0]; let dev_fs = df_values[0];
let fs = dev_fs.split(/\/+/)[2]; let fs = dev_fs.split(/\/+/)[2];
let hdd_use = this.get_hdd_use(fs); let re = new RegExp(fs + '.+');
this.hdd_values = [fs, hdd_use, hdd_tot, hdd_fre]; // https://stackoverflow.com/questions/4458183/how-the-util-of-iostat-is-computed
this.cpu_file.load_contents_async(null, (file, response) => {
let [cpu_success, cpu_contents, cpu_tag] = file.load_contents_finish(response);
if(!cpu_success) return;
let cpu_line = ByteArray.toString(cpu_contents).match(/cpu\s.+/)[0];
// get total CPU time
let cpu_values = cpu_line.split(/\s+/);
let hdd_cpu_tot = 0;
for (let i = 1; i<10; i++){
hdd_cpu_tot += parseFloat(cpu_values[i])
}
this.hdd_file.load_contents_async(null, (file, response) => {
let [hdd_success, hdd_contents, hdd_tag] = file.load_contents_finish(response);
if(!hdd_success) return;
let hdd_line = ByteArray.toString(hdd_contents).match(re)[0];
// get total of IO times
let hdd_hdd_tot = hdd_line.split(/\s+/)[10];
// update HDD use
let hdd_use = 100 * (hdd_hdd_tot - this.hdd_hdd_tot) / (hdd_cpu_tot - this.hdd_cpu_tot);
// update global values
this.hdd_cpu_tot = hdd_cpu_tot;
this.hdd_hdd_tot = hdd_hdd_tot;
this.hdd_values = [fs, hdd_use, hdd_tot, hdd_fre];
GLib.free(hdd_contents);
});
GLib.free(cpu_contents);
});
}); });
}, },
get_hdd_use: function(fs) {
// https://stackoverflow.com/questions/4458183/how-the-util-of-iostat-is-computed
let cpu_obj = GLib.file_get_contents("/proc/stat")[1];
let cpu_line = ByteArray.toString(cpu_obj).match(/cpu\s.+/)[0];
let re = new RegExp(fs + '.+');
let diskstats = GLib.file_get_contents("/proc/diskstats")[1];
let hdd_line = ByteArray.toString(diskstats).match(re)[0];
// get total CPU time
let cpu_values = cpu_line.split(/\s+/);
let hdd_cpu_tot = 0;
for (let i = 1; i<10; i++){
hdd_cpu_tot += parseFloat(cpu_values[i])
}
// get total of IO times
let hdd_hdd_tot = hdd_line.split(/\s+/)[10];
// update HHD use
let hdd_use = 100 * (hdd_hdd_tot - this.hdd_hdd_tot) / (hdd_cpu_tot - this.hdd_cpu_tot);
// update global values
this.hdd_cpu_tot = hdd_cpu_tot;
this.hdd_hdd_tot = hdd_hdd_tot;
return hdd_use;
},
parse_rgba_settings: function(color_str) { parse_rgba_settings: function(color_str) {
let colors = color_str.match(/\((.*?)\)/)[1].split(","); // get contents inside brackets: "rgb(...)" let colors = color_str.match(/\((.*?)\)/)[1].split(","); // get contents inside brackets: "rgb(...)"
let r = parseInt(colors[0])/255; let r = parseInt(colors[0])/255;
@ -507,41 +508,39 @@ SystemMonitorGraph.prototype = {
}, },
get_nvidia_gpu_use: function() { get_nvidia_gpu_use: function() {
let subprocess = new Gio.Subprocess({ let subprocess = Gio.Subprocess.new(
argv: ['nvidia-smi', '--query-gpu=utilization.gpu', '--format=csv', '--id='+ this.gpu_id], ['/usr/bin/nvidia-smi', '--query-gpu=utilization.gpu', '--format=csv', '--id='+ this.gpu_id],
flags: Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE, Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE
}); );
subprocess.init(null); subprocess.communicate_utf8_async(null, null, (subprocess, result) => {
subprocess.wait_async(null, (sourceObject, res) => { let [, stdout, stderr] = subprocess.communicate_utf8_finish(result);
let [, stdout, stderr] = sourceObject.communicate_utf8(null, null); this.gpu_use = parseInt(stdout.match(/[^\r\n]+/g)[1]); // parse integer in second line
this.gpu_use = parseInt(stdout.toString().match(/[^\r\n]+/g)[1]); // parse integer in second line
}); });
}, },
get_nvidia_gpu_mem: function() { get_nvidia_gpu_mem: function() {
let subprocess = new Gio.Subprocess({ let subprocess = Gio.Subprocess.new(
argv: ['nvidia-smi', '--query-gpu=memory.total,memory.used', '--format=csv', '--id='+ this.gpu_id], ['/usr/bin/nvidia-smi', '--query-gpu=memory.total,memory.used', '--format=csv', '--id='+ this.gpu_id],
flags: Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE, Gio.SubprocessFlags.STDOUT_PIPE|Gio.SubprocessFlags.STDERR_PIPE
}); );
subprocess.init(null); subprocess.communicate_utf8_async(null, null, (subprocess, result) => {
subprocess.wait_async(null, (sourceObject, res) => { let [, stdout, stderr] = subprocess.communicate_utf8_finish(result);
let [, stdout, stderr] = sourceObject.communicate_utf8(null, null); let fslines = stdout.split(/\r?\n/); // Line0:Headers Line1:Values
let fslines = stdout.split(/\r?\n/); // Line0:Headers Line1:Values let items = fslines[1].split(','); // Values are comma-separated
let items = fslines[1].split(','); // Values are comma-separated let mem_tot
let mem_tot let mem_usd
let mem_usd if (this.data_prefix_gpumem == 1) {
if (this.data_prefix_gpumem == 1) { // decimal prefix
   // decimal prefix mem_tot = parseInt(items[0]) * 1024 * 1024 / GB_TO_B;
mem_tot = parseInt(items[0]) * 1024 * 1024 / GB_TO_B; mem_usd = parseInt(items[1]) * 1024 * 1024 / GB_TO_B;
mem_usd = parseInt(items[1]) * 1024 * 1024 / GB_TO_B; } else {
} else { // binary prefix
  // binary prefix mem_tot = parseInt(items[0]) / GIB_TO_MIB;
mem_tot = parseInt(items[0]) / GIB_TO_MIB; mem_usd = parseInt(items[1]) / GIB_TO_MIB;
mem_usd = parseInt(items[1]) / GIB_TO_MIB; }
} this.gpu_mem[0] = mem_tot;
this.gpu_mem[0] = mem_tot; this.gpu_mem[1] = mem_usd;
this.gpu_mem[1] = mem_usd; });
});
} }
}; };

View File

@ -3,6 +3,6 @@
"uuid": "system-monitor-graph@rcassani", "uuid": "system-monitor-graph@rcassani",
"name": "System monitor graph", "name": "System monitor graph",
"description": "Creates graphs for system variables.", "description": "Creates graphs for system variables.",
"version": "1.5", "version": "1.6",
"prevent-decorations": true "prevent-decorations": true
} }