|
| 1 | +/* |
| 2 | + * SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) |
| 4 | + */ |
| 5 | + |
| 6 | +public class Monitor.ProcessDRM { |
| 7 | + /** |
| 8 | + * Time spent busy in nanoseconds by the render engine executing |
| 9 | + * workloads from the last time it was read |
| 10 | + */ |
| 11 | + private uint64 last_engine_render; |
| 12 | + private uint64 last_engine_gfx; |
| 13 | + |
| 14 | + |
| 15 | + public double gpu_percentage { get; private set; } |
| 16 | + |
| 17 | + private int pid; |
| 18 | + private int update_interval; |
| 19 | + |
| 20 | + public ProcessDRM (int pid, int update_interval) { |
| 21 | + this.pid = pid; |
| 22 | + this.update_interval = update_interval; |
| 23 | + |
| 24 | + last_engine_render = 0; |
| 25 | + last_engine_gfx = 0; |
| 26 | + } |
| 27 | + |
| 28 | + public void update () { |
| 29 | + string path_fdinfo = "/proc/%d/fdinfo".printf (pid); |
| 30 | + string path_fd = "/proc/%d/fd".printf (pid); |
| 31 | + |
| 32 | + |
| 33 | + var drm_files = new Gee.ArrayList<GLib.File ?> (); |
| 34 | + |
| 35 | + try { |
| 36 | + Dir dir = Dir.open (path_fdinfo, 0); |
| 37 | + string ? name = null; |
| 38 | + |
| 39 | + while ((name = dir.read_name ()) != null) { |
| 40 | + |
| 41 | + // skip standard fds |
| 42 | + if (name == "0" || name == "1" || name == "2") { |
| 43 | + continue; |
| 44 | + } |
| 45 | + string path = Path.build_filename (path_fdinfo, name); |
| 46 | + |
| 47 | + int fd_dir_fd = Posix.open (path_fd, Posix.O_RDONLY | Posix.O_DIRECTORY); |
| 48 | + if (fd_dir_fd == -1) { |
| 49 | + warning ("Cannot open file descriptor: %s", path_fd); |
| 50 | + continue; |
| 51 | + } |
| 52 | + |
| 53 | + bool is_drm = is_drm_fd (fd_dir_fd, name); |
| 54 | + Posix.close (fd_dir_fd); |
| 55 | + |
| 56 | + if (is_drm) { |
| 57 | + var drm_file = File.new_for_path (path); |
| 58 | + drm_files.add (drm_file); |
| 59 | + } |
| 60 | + } |
| 61 | + } catch (FileError err) { |
| 62 | + // prevent flooding logs with permission errors |
| 63 | + if (!(err is FileError.ACCES)) { |
| 64 | + warning (err.message); |
| 65 | + } |
| 66 | + } |
| 67 | + |
| 68 | + foreach (var drm_file in drm_files) { |
| 69 | + try { |
| 70 | + var dis = new DataInputStream (drm_file.read ()); |
| 71 | + string ? line; |
| 72 | + |
| 73 | + while ((line = dis.read_line ()) != null) { |
| 74 | + var splitted_line = line.split (":"); |
| 75 | + switch (splitted_line[0]) { |
| 76 | + case "drm-engine-gfx": |
| 77 | + update_engine (splitted_line[1], ref last_engine_gfx); |
| 78 | + break; |
| 79 | + // for i915 there is only drm-engine-render to check |
| 80 | + case "drm-engine-render": |
| 81 | + update_engine (splitted_line[1], ref last_engine_render); |
| 82 | + break; |
| 83 | + default: |
| 84 | + // Ignore other entries |
| 85 | + break; |
| 86 | + } |
| 87 | + } |
| 88 | + } catch (Error err) { |
| 89 | + if (!(err is FileError.ACCES)) { |
| 90 | + warning ("Can't read fdinfo: '%s' %d", err.message, err.code); |
| 91 | + } |
| 92 | + } |
| 93 | + break; |
| 94 | + } |
| 95 | + } |
| 96 | + |
| 97 | + private void update_engine (string line, ref uint64 last_engine) { |
| 98 | + var engine = uint64.parse (line.strip ().split (" ")[0]); |
| 99 | + if (last_engine != 0) { |
| 100 | + gpu_percentage = calculate_percentage (engine, last_engine, update_interval); |
| 101 | + } |
| 102 | + last_engine = engine; |
| 103 | + } |
| 104 | + |
| 105 | + private static double calculate_percentage (uint64 engine, uint64 last_engine, int interval) { |
| 106 | + // Since values in the files are in nanoseconds, it is also needed to convert interval to nanoseconds (10^9) |
| 107 | + return 100 * ((double) (engine - last_engine)) / (interval * 1e9); |
| 108 | + } |
| 109 | + |
| 110 | + // Based on nvtop |
| 111 | + // https://github.com/Syllo/nvtop/blob/4bf5db248d7aa7528f3a1ab7c94f504dff6834e4/src/extract_processinfo_fdinfo.c#L88 |
| 112 | + private static bool is_drm_fd (int fd_dir_fd, string name) { |
| 113 | + Posix.Stat stat; |
| 114 | + int ret = Posix.fstatat (fd_dir_fd, name, out stat, 0); |
| 115 | + return ret == 0 && (stat.st_mode & Posix.S_IFMT) == Posix.S_IFCHR && Posix.major (stat.st_rdev) == 226; |
| 116 | + } |
| 117 | + |
| 118 | +} |
0 commit comments