Skip to content
Open
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
199 changes: 24 additions & 175 deletions Resource_Monitor@Ory0n/extension.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,12 @@ const IndicatorName = Me.metadata.name;

const Domain = Gettext.domain(Me.metadata.uuid);

const GpuBackend = Me.imports.gpu.backend.GpuBackend
const AmdBackend = Me.imports.gpu.amd.AmdBackend
const NvidiaBackend = Me.imports.gpu.nvidia.NvidiaBackend

const _executeCommand = Me.imports.utils.executeCommand

const _ = Domain.gettext;
const ngettext = Domain.ngettext;

Expand Down Expand Up @@ -1783,7 +1789,7 @@ const ResourceMonitor = GObject.registerClass(
}

_refreshDiskSpaceValue() {
this._executeCommand(['df', '-BKB', '-x', 'squashfs', '-x', 'tmpfs']).then(output => {
_executeCommand(['df', '-BKB', '-x', 'squashfs', '-x', 'tmpfs']).then(output => {
const lines = output.split('\n');

// Excludes the first line of output
Expand Down Expand Up @@ -2247,155 +2253,26 @@ const ResourceMonitor = GObject.registerClass(
}

_refreshGpuValue() {
this._executeCommand(['nvidia-smi', '--query-gpu=uuid,memory.total,memory.used,memory.free,utilization.gpu,temperature.gpu', '--format=csv,noheader']).then(output => {
const lines = output.split('\n');

for (let i = 0; i < lines.length - 1; i++) {
const line = lines[i];
const entry = line.trim().split(/\,\s/);

const uuid = entry[0];
let memoryTotal = entry[1].slice(0, -4);
let memoryUsed = entry[2].slice(0, -4);
let memoryFree = entry[3].slice(0, -4);
const usage = entry[4].slice(0, -1);
const temperature = entry[5];

// mebibyte
memoryTotal = parseInt(memoryTotal);
memoryUsed = parseInt(memoryUsed);
memoryFree = parseInt(memoryFree);

// kibibyte
memoryTotal *= 1024;
memoryUsed *= 1024;
memoryFree *= 1024;

// kilobyte
memoryTotal *= 1.024;
memoryUsed *= 1.024;
memoryFree *= 1.024;

this._gpuBox.update_element_value(uuid, usage, '%');

let value = 0;
let unit = 'KB';
switch (this._gpuMemoryUnitType) {
case 'perc':
const used = (100 * memoryUsed) / memoryTotal;
unit = '%';

switch (this._gpuMemoryMonitor) {
case 'free':
value = 100 - used;

break;

case 'used':

default:
value = used;

break;
}

break;

case 'numeric':

default:
switch (this._gpuMemoryMonitor) {
case 'free':
value = memoryFree;

break;

case 'used':

default:
value = memoryUsed;

break;
}

switch (this._gpuMemoryUnitMeasure) {
case 'k':
unit = 'KB';
break;

case 'm':
unit = 'MB';
value /= 1000;

break;

case 'g':
unit = 'GB';
value /= 1000;
value /= 1000;

break;

case 't':
unit = 'TB';
value /= 1000;
value /= 1000;
value /= 1000;

break;

case 'auto':

default:
if (value > 1000) {
unit = 'MB';
value /= 1000;
if (value > 1000) {
unit = 'GB';
value /= 1000;
if (value > 1000) {
unit = 'TB';
value /= 1000;
}
}
} else {
unit = 'KB';
}

break;
}

break;
}

let valueT = parseInt(temperature);
let unitT = '°C';
switch (this._thermalTemperatureUnit) {
case 'f':
valueT = (valueT * 1.8) + 32;
unitT = '°F';

break;

case 'c':

default:
unitT = '°C';

break;
}

if (this._decimalsStatus) {
this._gpuBox.update_element_memory_value(uuid, `${value.toFixed(1)}`, unit);
this._gpuBox.update_element_thermal_value(uuid, `${valueT.toFixed(1)}`, unitT);
} else {
this._gpuBox.update_element_memory_value(uuid, `${value.toFixed(0)}`, unit);
this._gpuBox.update_element_thermal_value(uuid, `${valueT.toFixed(0)}`, unitT);
}
}
const context = {
gpuBox: this._gpuBox,
gpuMemoryMonitor: this._gpuMemoryMonitor,
gpuMemoryUnitType: this._gpuMemoryUnitType,
gpuMemoryUnitMeasure: this._gpuMemoryUnitMeasure,
thermalTemperatureUnit: this._thermalTemperatureUnit,
decimalsStatus: this._decimalsStatus,
};
let gpu = new GpuBackend(context);
if(NvidiaBackend.detect()){
gpu = new NvidiaBackend(context);
}
else if(AmdBackend.detect()){
gpu = new AmdBackend(context);
}
gpu.query().catch((error)=>{
log('[Resource_Monitor] Load GPU error (' + error + ')');
});
}

// Common Function
_basicItemStatus(status, iconCondition, icon, ...elements) {
if (status) {
Expand Down Expand Up @@ -2451,34 +2328,6 @@ const ResourceMonitor = GObject.registerClass(
}
}

_readOutput(proc, cancellable = null) {
return new Promise((resolve, reject) => {
proc.communicate_utf8_async(null, cancellable, (source_object, res) => {
try {
const [ok, stdout, stderr] = source_object.communicate_utf8_finish(res);

if (source_object.get_successful()) {
resolve(stdout);
} else {
throw new Error(stderr);
}
} catch (e) {
reject(e);
}
});
});
}

async _executeCommand(command, cancellable = null) {
try {
const proc = Gio.Subprocess.new(command, Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_PIPE);
const output = await this._readOutput(proc, cancellable);

return output;
} catch (error) {
log('[Resource_Monitor] Execute Command Error (' + error + ')');
}
}
});

const DiskContainer = GObject.registerClass(
Expand Down
167 changes: 167 additions & 0 deletions Resource_Monitor@Ory0n/gpu/amd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
const GpuBackend = imports.misc.extensionUtils.getCurrentExtension().imports.gpu.backend.GpuBackend;
const { Gio, GLib } = imports.gi;

var AmdBackend = class AmdBackend extends GpuBackend {
static detect() {
return GLib.file_test('/sys/class/drm/card0/device/hwmon', GLib.FileTest.EXISTS);
}

async query() {
const cards = this._findAmdCards();
for (const card of cards) {
const uuid = card;
const base = `/sys/class/drm/${card}/device`;

const usage = this._readInt(`${base}/gpu_busy_percent`);
const vramTotal = this._readInt(`${base}/mem_info_vram_total`);
const vramUsed = this._readInt(`${base}/mem_info_vram_used`);
const temperature = this._readTemperature(base);

// ---- USAGE ----
if (usage !== null) {
this._gpuBox.update_element_value(uuid, `${usage}`, '%');
}

// ---- MEMORY ----
if (vramTotal && vramUsed) {
let value;
let unit = 'KB';

if (this._gpuMemoryUnitType === 'perc') {
const usedPerc = (100 * vramUsed) / vramTotal;
value = this._gpuMemoryMonitor === 'free'
? 100 - usedPerc
: usedPerc;
unit = '%';
} else {
value = this._gpuMemoryMonitor === 'free'
? (vramTotal - vramUsed)
: vramUsed;

value /= 1024; // bytes → KB

switch (this._gpuMemoryUnitMeasure) {
case 'm':
value /= 1000;
unit = 'MB';
break;
case 'g':
value /= 1_000_000;
unit = 'GB';
break;
case 't':
value /= 1_000_000_000;
unit = 'TB';
break;
case 'auto':
default:
if (value > 1000) {
value /= 1000;
unit = 'MB';
if (value > 1000) {
value /= 1000;
unit = 'GB';
}
}
break;
}
}

this._gpuBox.update_element_memory_value(
uuid,
this._decimalsStatus ? `${value.toFixed(1)}` : `${value.toFixed(0)}`,
unit
);
}

// ---- TEMPERATURE ----
if (temperature !== null) {

let valueT = temperature;
let unitT = '°C';

if (this._thermalTemperatureUnit === 'f') {
valueT = (valueT * 1.8) + 32;
unitT = '°F';
}

const picsa = this._decimalsStatus ? `${valueT.toFixed(1)}` : `${valueT.toFixed(0)}`;

this._gpuBox.update_element_thermal_value(
uuid,
this._decimalsStatus ? `${valueT.toFixed(1)}` : `${valueT.toFixed(0)}`,
unitT
);
}
}
}

// ---------------- helpers ----------------

_findAmdCards() {
const dir = Gio.File.new_for_path('/sys/class/drm');
const enumerator = dir.enumerate_children(
'standard::name',
Gio.FileQueryInfoFlags.NONE,
null
);

const cards = [];
let info;
while ((info = enumerator.next_file(null))) {
const name = info.get_name();
if (name.startsWith('card')) {
const vendor = this._readString(`/sys/class/drm/${name}/device/vendor`);
if (vendor === '0x1002') { // AMD
cards.push(name);
}
}
}
return cards;
}

_readInt(path) {
try {
const file = Gio.File.new_for_path(path);
const [, contents] = file.load_contents(null);
return parseInt(new TextDecoder().decode(contents).trim());
} catch {
return null;
}
}

_readString(path) {
try {
const file = Gio.File.new_for_path(path);
const [, contents] = file.load_contents(null);
return new TextDecoder().decode(contents).trim();
} catch {
return null;
}
}

_readTemperature(basePath) {
try {
const hwmonDirPath = `${basePath}/hwmon`;
const file = Gio.File.new_for_path(hwmonDirPath);
const enumerator = file.enumerate_children(
'standard::name',
Gio.FileQueryInfoFlags.NONE,
null
);
let info;
while ((info = enumerator.next_file(null)) !== null) {
const hwmonPath = `${hwmonDirPath}/${info.get_name()}`;
const tempPath = `${hwmonPath}/temp1_input`;
if (GLib.file_test(tempPath, GLib.FileTest.EXISTS)) {
const temp = this._readInt(tempPath);
return temp !== null ? temp / 1000 : null;
}
}
return null;
} catch (e) {
log(`[Resource_Monitor] GPU temp read error: ${e}`);
return null;
}
}
}
Loading