Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package nvtop for openSUSE:Factory checked 
in at 2025-07-14 10:51:30
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/nvtop (Old)
 and      /work/SRC/openSUSE:Factory/.nvtop.new.7373 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "nvtop"

Mon Jul 14 10:51:30 2025 rev:8 rq:1292365 version:3.2.0+14

Changes:
--------
--- /work/SRC/openSUSE:Factory/nvtop/nvtop.changes      2025-02-20 
17:40:16.168481048 +0100
+++ /work/SRC/openSUSE:Factory/.nvtop.new.7373/nvtop.changes    2025-07-14 
10:56:42.101066600 +0200
@@ -1,0 +2,26 @@
+Tue Jul 08 18:01:07 UTC 2025 - malcolmle...@opensuse.org
+
+- Update to version 3.2.0+14:
+  * Fix power draw metrics on Intel Battlemage.
+  * Use fdinfo's engine count when refreshing utilisation rate.
+  * Add support for Rockchip NPU.
+  * Fix icon path.
+  * Fix the installed filename of the appstream metainfo.
+  * Fix division by zero on intel xe.
+  * Reorder percentage associativity and max to 100.
+  * Fix cpu fan selection.
+  * Adding TPU metrics via libtpuinfo.
+  * Convert output to json.
+  * Add Intel Xe driver temp support.
+  * Add snapshot command to nvtop for scripting purposes.
+  * Fix some compiler warnings.
+  * Control reaches end of non-void function error,
+    (gh#Syllo/nvtop#350).
+  * Improve PCI bridge checks.
+  * Add Intel to libdrm detection in CMakeLists.txt.
+  * Intel: Fix some very minor stuff.
+  * Workaround broken Intel PCIe speeds.
+- Drop nvtop-fix-non-void-function.patch as this has been
+  upstreamed.
+
+-------------------------------------------------------------------

Old:
----
  nvtop-3.1.0+100.obscpio
  nvtop-fix-non-void-function.patch

New:
----
  nvtop-3.2.0+14.obscpio

----------(Old B)----------
  Old:  * Workaround broken Intel PCIe speeds.
- Drop nvtop-fix-non-void-function.patch as this has been
  upstreamed.
----------(Old E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ nvtop.spec ++++++
--- /var/tmp/diff_new_pack.y9H9hZ/_old  2025-07-14 10:56:42.881098936 +0200
+++ /var/tmp/diff_new_pack.y9H9hZ/_new  2025-07-14 10:56:42.885099102 +0200
@@ -18,19 +18,16 @@
 
 
 Name:           nvtop
-Version:        3.1.0+100
+Version:        3.2.0+14
 Release:        0
 Summary:        A (h)top like task monitor for NVIDIA and AMD GPUs
 License:        GPL-3.0-or-later
 URL:            https://github.com/Syllo/nvtop
 Source0:        %{name}-%{version}.tar.xz
-#PATCH-FIX-UPSTREAM nvtop-fix-non-void-function.patch (gh#Syllo/nvtop#350) 
malcolmle...@opensuse.org -- Fix control reaches end of non-void function error
-Patch0:         nvtop-fix-non-void-function.patch
 BuildRequires:  cmake
 BuildRequires:  gcc-c++
 BuildRequires:  hicolor-icon-theme
 BuildRequires:  pkgconfig
-BuildRequires:  update-desktop-files
 BuildRequires:  pkgconfig(libdrm)
 BuildRequires:  pkgconfig(libudev)
 BuildRequires:  pkgconfig(ncurses)
@@ -59,7 +56,7 @@
 %doc README.markdown
 %{_bindir}/nvtop
 %{_datadir}/applications/nvtop.desktop
-%{_datadir}/icons/nvtop.svg
+%{_datadir}/icons/hicolor/scalable/apps/nvtop.svg
 %{_mandir}/man1/nvtop.1%{?ext_man}
-%{_datadir}/metainfo/nvtop.metainfo.xml
+%{_datadir}/metainfo/io.github.syllo.nvtop.metainfo.xml
 

++++++ _servicedata ++++++
--- /var/tmp/diff_new_pack.y9H9hZ/_old  2025-07-14 10:56:42.929100926 +0200
+++ /var/tmp/diff_new_pack.y9H9hZ/_new  2025-07-14 10:56:42.933101092 +0200
@@ -1,6 +1,6 @@
 <servicedata>
 <service name="tar_scm">
                 <param name="url">https://github.com/Syllo/nvtop.git</param>
-              <param 
name="changesrevision">f901275e5b922835e9cf0301f873600bbdb9753b</param></service></servicedata>
+              <param 
name="changesrevision">339ee0b10a64ec51f43d27357b0068a40f16e9e4</param></service></servicedata>
 (No newline at EOF)
 

++++++ nvtop-3.1.0+100.obscpio -> nvtop-3.2.0+14.obscpio ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/.github/workflows/compile.yml 
new/nvtop-3.2.0+14/.github/workflows/compile.yml
--- old/nvtop-3.1.0+100/.github/workflows/compile.yml   2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/.github/workflows/compile.yml    2025-06-28 
15:30:10.000000000 +0200
@@ -12,7 +12,7 @@
       fail-fast: false
 
       matrix:
-        os: [ubuntu-24.04, ubuntu-22.04, ubuntu-20.04]
+        os: [ubuntu-latest, ubuntu-24.04, ubuntu-22.04]
 
     env:
       BUILD_DIR: build
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/CMakeLists.txt 
new/nvtop-3.2.0+14/CMakeLists.txt
--- old/nvtop-3.1.0+100/CMakeLists.txt  2025-01-19 09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/CMakeLists.txt   2025-06-28 15:30:10.000000000 +0200
@@ -4,7 +4,7 @@
 #                              PROJECT                              #
 #///////////////////////////////////////////////////////////////////#
 
-project(nvtop VERSION 3.1.0
+project(nvtop VERSION 3.2.0
   LANGUAGES C CXX)
 
 set(default_build_type "Release")
@@ -91,6 +91,32 @@
   set(ASCEND_SUPPORT_DEFAULT OFF)
 endif()
 
+# TPU support is only available on Linux
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
+  # Check for libtpuinfo.so to set the default for TPU support
+  find_library(LIBTPUINFO
+    NAMES libtpuinfo.so
+    PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64
+    HINTS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 lib lib64
+  )
+  if (NOT LIBTPUINFO)
+    set(TPU_SUPPORT_DEFAULT OFF)
+  else()
+    set(TPU_SUPPORT_DEFAULT ON)
+  endif()
+else()
+  set(TPU_SUPPORT_DEFAULT OFF)
+endif()
+
+# Rockchip support is only available on Linux on arm
+# Enable Rockchip support only on ARM Linux
+if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND
+    (CMAKE_SYSTEM_PROCESSOR MATCHES "armv7" OR CMAKE_SYSTEM_PROCESSOR MATCHES 
"aarch64"))
+  set(ROCKCHIP_SUPPORT_DEFAULT ON)
+else()
+  set(ROCKCHIP_SUPPORT_DEFAULT OFF)
+endif()
+
 option(NVIDIA_SUPPORT "Build support for NVIDIA GPUs through libnvml" 
${NVIDIA_SUPPORT_DEFAULT})
 option(AMDGPU_SUPPORT "Build support for AMD GPUs through amdgpu driver" 
${AMDGPU_SUPPORT_DEFAULT})
 option(INTEL_SUPPORT "Build support for Intel GPUs through i915 or xe driver" 
${INTEL_SUPPORT_DEFAULT})
@@ -100,6 +126,8 @@
 option(PANTHOR_SUPPORT "Build support for Mali GPUs through panthor driver" 
${PANTHOR_SUPPORT_DEFAULT})
 option(ASCEND_SUPPORT "Build support for Ascend NPUs through Ascend DCMI" 
${ASCEND_SUPPORT_DEFAULT})
 option(V3D_SUPPORT "Build support for Raspberrypi through v3d" 
${V3D_SUPPORT_DEFAULT})
+option(TPU_SUPPORT "Build support for Google TPUs through GRPC" 
${TPU_SUPPORT_DEFAULT})
+option(ROCKCHIP_SUPPORT "Enable support for Rockchip NPU" 
${ROCKCHIP_SUPPORT_DEFAULT})
 
 add_subdirectory(src)
 
@@ -115,7 +143,7 @@
   IMMEDIATE @ONLY)
 configure_file(
   "${CMAKE_CURRENT_SOURCE_DIR}/desktop/nvtop.metainfo.xml.in"
-  "${CMAKE_CURRENT_BINARY_DIR}/desktop/nvtop.metainfo.xml"
+  "${CMAKE_CURRENT_BINARY_DIR}/desktop/io.github.syllo.nvtop.metainfo.xml"
   IMMEDIATE @ONLY)
 install(FILES
   "${CMAKE_CURRENT_BINARY_DIR}/manpage/nvtop"
@@ -124,14 +152,14 @@
   RENAME nvtop.1)
 install(FILES
   "${CMAKE_CURRENT_SOURCE_DIR}/desktop/nvtop.svg"
-  DESTINATION share/icons
+  DESTINATION share/icons/hicolor/scalable/apps
   PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
 install(FILES
   "${CMAKE_CURRENT_SOURCE_DIR}/desktop/nvtop.desktop"
   DESTINATION share/applications
   PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
 install(FILES
-  "${CMAKE_CURRENT_BINARY_DIR}/desktop/nvtop.metainfo.xml"
+  "${CMAKE_CURRENT_BINARY_DIR}/desktop/io.github.syllo.nvtop.metainfo.xml"
   DESTINATION share/metainfo
   PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/README.markdown 
new/nvtop-3.2.0+14/README.markdown
--- old/nvtop-3.1.0+100/README.markdown 2025-01-19 09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/README.markdown  2025-06-28 15:30:10.000000000 +0200
@@ -138,6 +138,12 @@
 
 On non-raspberry pi os, you need to use the `linux-rpi 6.12.y` kernel and 
above, and ensure the presence of the `/dev/vcio` device.
 
+### Rockchip
+
+NVTOP supports Rockchip (testing on orangepi 5 plus).
+
+Supports NPU frequency, temperature, utilization.
+
 Build
 -----
 
@@ -157,20 +163,19 @@
 
 A standalone application is available as [AppImage](#appimage).
 
-#### Ubuntu Impish (21.10), Debian buster (stable) and more recent
+#### Ubuntu Focal (20.04), Debian buster (stable) and more recent
 
-- ```bash
-  sudo apt install nvtop
-  ```
+```bash
+sudo apt install nvtop
+```
 
 #### Ubuntu PPA
 
-A [PPA supporting Ubuntu 20.04, 22.04 and 
newer](https://launchpad.net/~flexiondotorg/+archive/ubuntu/nvtop) is provided 
by
-[Martin Wimpress](https://github.com/flexiondotorg) that offers an up-to-date
-version of `nvtop`, enabled for NVIDIA, AMD and Intel.
+A [PPA supporting Ubuntu 20.04 and 
newer](https://launchpad.net/~quentiumyt/+archive/ubuntu/nvtop) is provided by
+[Quentin Lienhardt](https://github.com/QuentiumYT) that offers an up-to-date 
version of `nvtop`, enabled for NVIDIA, AMD and Intel.
 
 ```bash
-sudo add-apt-repository ppa:flexiondotorg/nvtop
+sudo add-apt-repository ppa:quentiumyt/nvtop
 sudo apt install nvtop
 ```
 
@@ -347,7 +352,9 @@
 sudo make install
 
 # Alternatively, install without privileges at a location of your choosing
-# make DESTDIR="/your/install/path" install
+# cmake .. -DNVIDIA_SUPPORT=ON -DAMDGPU_SUPPORT=ON -DINTEL_SUPPORT=ON 
-DCMAKE_INSTALL_PREFIX=/path/to/your/dir
+# make
+# make install
 ```
 
 If you use **conda** as environment manager and encounter an error while 
building NVTOP, try `conda deactivate` before invoking `cmake`.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/nvtop-3.1.0+100/include/nvtop/extract_gpuinfo_common.h 
new/nvtop-3.2.0+14/include/nvtop/extract_gpuinfo_common.h
--- old/nvtop-3.1.0+100/include/nvtop/extract_gpuinfo_common.h  2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/include/nvtop/extract_gpuinfo_common.h   2025-06-28 
15:30:10.000000000 +0200
@@ -60,6 +60,7 @@
   gpuinfo_n_shared_cores_valid,
   gpuinfo_l2cache_size_valid,
   gpuinfo_n_exec_engines_valid,
+  gpuinfo_engine_count_valid,
   gpuinfo_static_info_count,
 };
 
@@ -74,6 +75,7 @@
   unsigned n_shared_cores;
   unsigned l2cache_size;
   unsigned n_exec_engines;
+  unsigned engine_count;
   bool integrated_graphics;
   bool encode_decode_shared;
   unsigned char valid[(gpuinfo_static_info_count + CHAR_BIT - 1) / CHAR_BIT];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/include/nvtop/interface.h 
new/nvtop-3.2.0+14/include/nvtop/interface.h
--- old/nvtop-3.1.0+100/include/nvtop/interface.h       2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/include/nvtop/interface.h        2025-06-28 
15:30:10.000000000 +0200
@@ -55,4 +55,6 @@
 
 bool show_information_messages(unsigned num_messages, const char **messages);
 
+void print_snapshot(struct list_head *devices, bool use_fahrenheit_option);
+
 #endif // INTERFACE_H_
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/CMakeLists.txt 
new/nvtop-3.2.0+14/src/CMakeLists.txt
--- old/nvtop-3.1.0+100/src/CMakeLists.txt      2025-01-19 09:13:14.000000000 
+0100
+++ new/nvtop-3.2.0+14/src/CMakeLists.txt       2025-06-28 15:30:10.000000000 
+0200
@@ -74,28 +74,29 @@
   target_sources(nvtop PRIVATE device_discovery_linux.c)
 endif()
 
-if(AMDGPU_SUPPORT OR MSM_SUPPORT OR PANFROST_SUPPORT OR PANTHOR_SUPPORT)
+if(AMDGPU_SUPPORT OR INTEL_SUPPORT OR MSM_SUPPORT OR PANFROST_SUPPORT OR 
PANTHOR_SUPPORT)
   # Search for libdrm for AMDGPU support
   find_package(Libdrm)
 
   if(Libdrm_FOUND)
     message(STATUS "Found libdrm; Enabling support")
     target_include_directories(nvtop PRIVATE ${Libdrm_INCLUDE_DIRS})
-    if (AMDGPU_SUPPORT)
-      target_sources(nvtop PRIVATE extract_gpuinfo_amdgpu.c)
-      target_sources(nvtop PRIVATE extract_gpuinfo_amdgpu_utils.c)
-    endif()
-
-    if (MSM_SUPPORT)
-      target_sources(nvtop PRIVATE extract_gpuinfo_msm.c)
-      target_sources(nvtop PRIVATE extract_gpuinfo_msm_utils.c)
-    endif()
-
   else()
-    message(FATAL_ERROR "libdrm not found; This library is required for AMDGPU 
and MSM support")
+    message(FATAL_ERROR "libdrm not found; This library is required for 
AMDGPU, INTEL, MSM, PANFROST and PANTHOR support")
+    # CMake will exit if libdrm is not found
   endif()
 endif()
 
+if (AMDGPU_SUPPORT)
+  target_sources(nvtop PRIVATE extract_gpuinfo_amdgpu.c)
+  target_sources(nvtop PRIVATE extract_gpuinfo_amdgpu_utils.c)
+endif()
+
+if (MSM_SUPPORT)
+  target_sources(nvtop PRIVATE extract_gpuinfo_msm.c)
+  target_sources(nvtop PRIVATE extract_gpuinfo_msm_utils.c)
+endif()
+
 if(INTEL_SUPPORT)
   target_sources(nvtop PRIVATE extract_gpuinfo_intel.c)
   target_sources(nvtop PRIVATE extract_gpuinfo_intel_i915.c)
@@ -131,6 +132,25 @@
   target_sources(nvtop PRIVATE extract_gpuinfo_mali_common.c)
 endif()
 
+if(TPU_SUPPORT)
+  find_library(LIBTPUINFO
+    NAMES libtpuinfo.so
+    PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64
+    HINTS ${CMAKE_INSTALL_PREFIX}/lib ${CMAKE_INSTALL_PREFIX}/lib64 lib lib64
+  )
+  if (NOT LIBTPUINFO)
+    message(WARNING "TPU Support enabled, but libtpuinfo.so not found in 
ldconfig path, we will not be able to read TPU usage")
+    set(TPU_SUPPORT_DEFAULT OFF)
+  endif()
+  target_sources(nvtop PRIVATE extract_gpuinfo_tpu.c)
+endif()
+
+if(ROCKCHIP_SUPPORT)
+  message(STATUS "Building with Rockchip NPU support")
+
+  target_sources(nvtop PRIVATE extract_npuinfo_rockchip.c)
+endif()
+
 target_include_directories(nvtop PRIVATE
   ${PROJECT_SOURCE_DIR}/include
   ${PROJECT_BINARY_DIR}/include)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo.c   2025-01-19 09:13:14.000000000 
+0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo.c    2025-06-28 15:30:10.000000000 
+0200
@@ -19,7 +19,6 @@
  *
  */
 
-#include <assert.h>
 #include <ctype.h>
 #include <math.h>
 #include <stdio.h>
@@ -216,10 +215,10 @@
     // Process memory usage percent of total device memory
     if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, total_memory) &&
         GPUINFO_PROCESS_FIELD_VALID(&device->processes[j], gpu_memory_usage)) {
-      float percentage =
-          roundf(100.f * (float)device->processes[j].gpu_memory_usage / 
(float)device->dynamic_info.total_memory);
+      double percentage = fmin(
+          round(100. * ((double)device->processes[j].gpu_memory_usage / 
(double)device->dynamic_info.total_memory)),
+          100.);
       SET_GPUINFO_PROCESS(&device->processes[j], gpu_memory_percentage, 
(unsigned)percentage);
-      assert(device->processes[j].gpu_memory_percentage <= 100);
     }
   }
 }
@@ -341,6 +340,7 @@
   unsigned int utilisation_rate;
   uint64_t max_freq_hz;
   double avg_delta_secs;
+  unsigned int ec;
 
   for (unsigned processIdx = 0; processIdx < gpu_info->processes_count; 
++processIdx) {
     struct gpu_process *process_info = &gpu_info->processes[processIdx];
@@ -352,9 +352,14 @@
   if (!gfx_total_process_cycles)
     return;
 
+  if (IS_VALID(gpuinfo_engine_count_valid, gpu_info->static_info.valid))
+          ec = gpu_info->static_info.engine_count;
+  else
+          ec = 1;
+
   avg_delta_secs = ((double)total_delta / gpu_info->processes_count) / 
1000000000.0;
   max_freq_hz = gpu_info->dynamic_info.gpu_clock_speed_max * 1000000;
-  utilisation_rate = (unsigned int)((((double)gfx_total_process_cycles) / 
(((double)max_freq_hz) * avg_delta_secs * 2)) * 100);
+  utilisation_rate = (unsigned int)((((double)gfx_total_process_cycles) / 
(((double)max_freq_hz) * avg_delta_secs * ec)) * 100);
   utilisation_rate = utilisation_rate > 100 ? 100 : utilisation_rate;
 
   SET_GPUINFO_DYNAMIC(&gpu_info->dynamic_info, gpu_util_rate, 
utilisation_rate);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_ascend.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_ascend.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_ascend.c    2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_ascend.c     2025-06-28 
15:30:10.000000000 +0200
@@ -19,6 +19,7 @@
  */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <errno.h>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_intel.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_intel.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_intel.c     2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_intel.c      2025-06-28 
15:30:10.000000000 +0200
@@ -71,7 +71,7 @@
   for (unsigned i = 0; i < intel_gpu_count; ++i) {
     struct gpu_info_intel *current = &gpu_infos[i];
     if (current->card_fd)
-        close(current->card_fd);
+      close(current->card_fd);
     nvtop_device_unref(current->card_device);
     nvtop_device_unref(current->driver_device);
   }
@@ -87,6 +87,7 @@
   case DRIVER_XE:
     return parse_drm_fdinfo_intel_xe(info, fdinfo_file, process_info);
   }
+  return false;
 }
 
 static void add_intel_cards(struct nvtop_device *dev, struct list_head 
*devices, unsigned *count) {
@@ -180,24 +181,46 @@
     }
   }
 
+  // Mark integrated GPUs
+  if (strcmp(gpu_info->base.pdev, INTEGRATED_I915_GPU_PCI_ID) == 0) {
+    static_info->integrated_graphics = true;
+  }
+
   nvtop_pcie_link max_link_characteristics;
   int ret = nvtop_device_maximum_pcie_link(gpu_info->driver_device, 
&max_link_characteristics);
   if (ret >= 0) {
+    // Some cards report PCIe GEN 1@ 1x, attempt to detect this and get the 
card's bridge link speeds
+    gpu_info->bridge_device = gpu_info->driver_device;
+    struct nvtop_device *parent;
+    const char *vendor, *class;
+    unsigned attempts = 0;
+    while (ret >= 0 && static_info->integrated_graphics == false &&
+           // check likely incorrect speed
+           max_link_characteristics.width == 1 && 
max_link_characteristics.speed == 2 &&
+           // check vendor
+           nvtop_device_get_sysattr_value(gpu_info->bridge_device, "vendor", 
&vendor) == 0 &&
+           strcmp(vendor, VENDOR_INTEL_STR) == 0 &&
+           // check class is either VGA or (non-host) PCI Bridge
+           nvtop_device_get_sysattr_value(gpu_info->bridge_device, "class", 
&class) == 0 &&
+           (strcmp(class, "0x030000") == 0 || strcmp(class, "0x060400") == 0) 
&&
+           // don't go more than 2 levels up
+           attempts++ < 2) {
+      ret = nvtop_device_get_parent(gpu_info->bridge_device, &parent);
+      if (ret >= 0 && nvtop_device_maximum_pcie_link(parent, 
&max_link_characteristics) >= 0) {
+        gpu_info->bridge_device = parent;
+      }
+    }
     SET_GPUINFO_STATIC(static_info, max_pcie_link_width, 
max_link_characteristics.width);
     unsigned pcieGen = 
nvtop_pcie_gen_from_link_speed(max_link_characteristics.speed);
     SET_GPUINFO_STATIC(static_info, max_pcie_gen, pcieGen);
   }
-
-  // Mark integrated GPUs
-  if (strcmp(gpu_info->base.pdev, INTEGRATED_I915_GPU_PCI_ID) == 0) {
-    static_info->integrated_graphics = true;
-  }
 }
 
 void gpuinfo_intel_refresh_dynamic_info(struct gpu_info *_gpu_info) {
   struct gpu_info_intel *gpu_info = container_of(_gpu_info, struct 
gpu_info_intel, base);
   struct gpuinfo_static_info *static_info = &gpu_info->base.static_info;
   struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info;
+  bool is_xe = gpu_info->driver == DRIVER_XE;
 
   RESET_ALL(dynamic_info->valid);
 
@@ -214,28 +237,32 @@
     if (nvtop_device_get_syspath(gpu_info->hwmon_device, &syspath) >= 0)
       nvtop_device_new_from_syspath(&hwmon_dev_noncached, syspath);
   }
+  nvtop_device *bridge_dev_noncached = NULL;
+  if (gpu_info->bridge_device) {
+    if (nvtop_device_get_syspath(gpu_info->bridge_device, &syspath) >= 0)
+      nvtop_device_new_from_syspath(&bridge_dev_noncached, syspath);
+  } else {
+    bridge_dev_noncached = driver_dev_noncached;
+  }
 
-  nvtop_device *clock_device = gpu_info->driver == DRIVER_XE ? 
driver_dev_noncached : card_dev_noncached;
+  nvtop_device *clock_device = is_xe ? driver_dev_noncached : 
card_dev_noncached;
   // GPU clock
   const char *gt_cur_freq;
-  const char *gt_cur_freq_sysattr = gpu_info->driver == DRIVER_XE ? 
"tile0/gt0/freq0/cur_freq" : "gt_cur_freq_mhz";
+  const char *gt_cur_freq_sysattr = is_xe ? "tile0/gt0/freq0/cur_freq" : 
"gt_cur_freq_mhz";
   if (nvtop_device_get_sysattr_value(clock_device, gt_cur_freq_sysattr, 
&gt_cur_freq) >= 0) {
     unsigned val = strtoul(gt_cur_freq, NULL, 10);
     SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, val);
   }
   const char *gt_max_freq;
-  const char *gt_max_freq_sysattr = gpu_info->driver == DRIVER_XE ? 
"tile0/gt0/freq0/max_freq" : "gt_max_freq_mhz";
+  const char *gt_max_freq_sysattr = is_xe ? "tile0/gt0/freq0/max_freq" : 
"gt_max_freq_mhz";
   if (nvtop_device_get_sysattr_value(clock_device, gt_max_freq_sysattr, 
&gt_max_freq) >= 0) {
     unsigned val = strtoul(gt_max_freq, NULL, 10);
     SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, val);
   }
 
-  // TODO: find how to extract global utilization
-  // gpu util will be computed as the sum of all the processes utilization for 
now
-
   if (!static_info->integrated_graphics) {
     nvtop_pcie_link curr_link_characteristics;
-    int ret = nvtop_device_current_pcie_link(driver_dev_noncached, 
&curr_link_characteristics);
+    int ret = nvtop_device_current_pcie_link(bridge_dev_noncached, 
&curr_link_characteristics);
     if (ret >= 0) {
       SET_GPUINFO_DYNAMIC(dynamic_info, pcie_link_width, 
curr_link_characteristics.width);
       unsigned pcieGen = 
nvtop_pcie_gen_from_link_speed(curr_link_characteristics.speed);
@@ -243,39 +270,59 @@
     }
   }
 
-  // TODO: Attributes such as memory, fan, temperature, power info should be 
available once the hwmon patch lands
   if (hwmon_dev_noncached) {
     const char *hwmon_fan;
-    // maxFanValue is just a guess, there is no way to get the max fan speed 
from hwmon
     if (nvtop_device_get_sysattr_value(hwmon_dev_noncached, "fan1_input", 
&hwmon_fan) >= 0) {
       unsigned val = strtoul(hwmon_fan, NULL, 10);
       SET_GPUINFO_DYNAMIC(dynamic_info, fan_rpm, val);
     }
+
     const char *hwmon_temp;
-    if (nvtop_device_get_sysattr_value(hwmon_dev_noncached, "temp1_input", 
&hwmon_temp) >= 0) {
+    // temp1 is for i915, temp2 is for `pkg` on xe
+    if (nvtop_device_get_sysattr_value(hwmon_dev_noncached, is_xe ? 
"temp2_input" : "temp1_input", &hwmon_temp) >= 0) {
       unsigned val = strtoul(hwmon_temp, NULL, 10);
       SET_GPUINFO_DYNAMIC(dynamic_info, gpu_temp, val / 1000);
     }
 
-    const char *hwmon_power_max;
-    // power1 is for i915, power2 is for xe
-    if (nvtop_device_get_sysattr_value(hwmon_dev_noncached, "power1_max", 
&hwmon_power_max) >= 0 ||
-        nvtop_device_get_sysattr_value(hwmon_dev_noncached, "power2_max", 
&hwmon_power_max) >= 0) {
+    const char *hwmon_power_max = NULL;
+    const char *hwmon_energy = NULL;
+    for (unsigned i = 0; i < (is_xe ? 2 : 1); i++) {
+      // Max Power
+      if (hwmon_power_max == NULL || hwmon_power_max[0] == '0') {
+        // power1 is for i915 and `card` on supported cards on xe, power2 is 
`pkg` on xe
+        nvtop_device_get_sysattr_value(hwmon_dev_noncached, i == 0 ? 
"power1_max" : "power2_max", &hwmon_power_max);
+      }
+      if (hwmon_power_max == NULL || hwmon_power_max[0] == '0') {
+        // Battlemage (xe) uses power*_crit
+        nvtop_device_get_sysattr_value(hwmon_dev_noncached, i == 0 ? 
"power1_crit" : "power2_crit", &hwmon_power_max);
+      }
+      if (hwmon_power_max == NULL || hwmon_power_max[0] == '0') {
+        // Both drivers have this, but it seems to be 0
+        nvtop_device_get_sysattr_value(hwmon_dev_noncached, i == 0 ? 
"power1_rated_max" : "power2_rated_max", &hwmon_power_max);
+      }
+
+      // Energy Usage
+      if (hwmon_energy == NULL || hwmon_energy[0] == '0') {
+        // energy1 is for i915 and `card` on supported cards on xe, energy2 is 
`pkg` on xe
+        nvtop_device_get_sysattr_value(hwmon_dev_noncached, i == 0 ? 
"energy1_input" : "energy2_input", &hwmon_energy);
+      }
+    }
+
+    // Check if we found the max power draw
+    if (hwmon_power_max != NULL) {
       unsigned val = strtoul(hwmon_power_max, NULL, 10);
       SET_GPUINFO_DYNAMIC(dynamic_info, power_draw_max, val / 1000);
     }
 
-    const char *hwmon_energy;
-    // energy1 is for i915, energy2 is for xe
-    if (nvtop_device_get_sysattr_value(hwmon_dev_noncached, "energy1_input", 
&hwmon_energy) >= 0 ||
-        nvtop_device_get_sysattr_value(hwmon_dev_noncached, "energy2_input", 
&hwmon_energy) >= 0) {
-      nvtop_time ts, ts_diff;
+    // Check if we found the energy usage and convert it into a wattage
+    if (hwmon_energy != NULL) {
+      nvtop_time ts;
       nvtop_get_current_time(&ts);
       unsigned val = strtoul(hwmon_energy, NULL, 10);
-      unsigned old = gpu_info->energy.energy_uj;
-      uint64_t time = nvtop_difftime_u64(gpu_info->energy.time, ts);
       // Skip the first update so we have a time delta
       if (gpu_info->energy.time.tv_sec != 0) {
+        unsigned old = gpu_info->energy.energy_uj;
+        uint64_t time = nvtop_difftime_u64(gpu_info->energy.time, ts);
         unsigned power = ((val - old) * 1000000000LL) / time;
         SET_GPUINFO_DYNAMIC(dynamic_info, power_draw, power / 1000);
       }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_intel.h 
new/nvtop-3.2.0+14/src/extract_gpuinfo_intel.h
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_intel.h     2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_intel.h      2025-06-28 
15:30:10.000000000 +0200
@@ -61,6 +61,8 @@
   struct nvtop_device *hwmon_device;
   struct intel_process_info_cache *last_update_process_cache, 
*current_update_process_cache; // Cached processes info
 
+  struct nvtop_device *bridge_device;
+
   struct {
     unsigned energy_uj;
     struct timespec time;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_intel_i915.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_intel_i915.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_intel_i915.c        2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_intel_i915.c 2025-06-28 
15:30:10.000000000 +0200
@@ -255,6 +255,8 @@
   struct intel_process_info_cache *cache_entry;
   struct unique_cache_id ucid = {.client_id = cid, .pid = process_info->pid, 
.pdev = gpu_info->base.pdev};
   HASH_FIND_CLIENT(gpu_info->last_update_process_cache, &ucid, cache_entry);
+  // TODO: find how to extract global utilization
+  // gpu util will be computed as the sum of all the processes utilization for 
now
   if (cache_entry) {
     uint64_t time_elapsed = 
nvtop_difftime_u64(cache_entry->last_measurement_tstamp, current_time);
     HASH_DEL(gpu_info->last_update_process_cache, cache_entry);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_intel_xe.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_intel_xe.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_intel_xe.c  2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_intel_xe.c   2025-06-28 
15:30:10.000000000 +0200
@@ -79,7 +79,7 @@
   struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info;
 
   if (gpu_info->card_fd) {
-    int32_t length = 0;
+    uint32_t length = 0;
     struct drm_xe_query_mem_regions *regions =
         xe_device_query_alloc_fetch(gpu_info->card_fd, 
DRM_XE_DEVICE_QUERY_MEM_REGIONS, &length);
     if (regions) {
@@ -103,7 +103,7 @@
 }
 
 static const char xe_drm_intel_vram[] = "drm-total-vram0";
-static const char xe_drm_intel_gtt[] = "drm-total-gtt";
+// static const char xe_drm_intel_gtt[] = "drm-total-gtt";
 // Render
 static const char xe_drm_intel_cycles_rcs[] = "drm-cycles-rcs";
 static const char xe_drm_intel_total_cycles_rcs[] = "drm-total-cycles-rcs";
@@ -222,20 +222,27 @@
   if (cache_entry) {
     HASH_DEL(gpu_info->last_update_process_cache, cache_entry);
 
+    // TODO: find how to extract global utilization
+    // gpu util will be computed as the sum of all the processes utilization 
for now
     {
       uint64_t cycles_delta = gpu_cycles.rcs - cache_entry->gpu_cycles.rcs;
       uint64_t total_cycles_delta = total_cycles.rcs - 
cache_entry->total_cycles.rcs;
-      SET_GPUINFO_PROCESS(process_info, gpu_usage, cycles_delta * 100 / 
total_cycles_delta);
+      if (total_cycles_delta > 0)
+        SET_GPUINFO_PROCESS(process_info, gpu_usage, cycles_delta * 100 / 
total_cycles_delta);
+      else
+        SET_GPUINFO_PROCESS(process_info, gpu_usage, 0);
     }
     {
       uint64_t cycles_delta = gpu_cycles.ccs - cache_entry->gpu_cycles.ccs;
       uint64_t total_cycles_delta = total_cycles.ccs - 
cache_entry->total_cycles.ccs;
-      SET_GPUINFO_PROCESS(process_info, gpu_usage, process_info->gpu_usage + 
cycles_delta * 100 / total_cycles_delta);
+      if (total_cycles_delta > 0)
+        SET_GPUINFO_PROCESS(process_info, gpu_usage, process_info->gpu_usage + 
cycles_delta * 100 / total_cycles_delta);
     }
     {
       uint64_t cycles_delta = gpu_cycles.vcs - cache_entry->gpu_cycles.vcs;
       uint64_t total_cycles_delta = total_cycles.vcs - 
cache_entry->total_cycles.vcs;
-      SET_GPUINFO_PROCESS(process_info, decode_usage, cycles_delta * 100 / 
total_cycles_delta);
+      if (total_cycles_delta > 0)
+        SET_GPUINFO_PROCESS(process_info, decode_usage, cycles_delta * 100 / 
total_cycles_delta);
     }
 
   } else {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_mali_common.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_mali_common.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_mali_common.c       2025-01-19 
09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_mali_common.c        2025-06-28 
15:30:10.000000000 +0200
@@ -456,6 +456,7 @@
                                  check_fdinfo_keys match_keys,
                                  struct fdinfo_data *fid)
 {
+  struct gpuinfo_static_info *static_info = &info->static_info;
   static char *line = NULL;
   static size_t line_buf_size = 0;
   uint64_t total_time = 0;
@@ -529,8 +530,10 @@
     }
   }
 
-  if (fid->engine_count)
-    SET_GPUINFO_PROCESS(process_info, gfx_engine_used, total_time);
+  if (fid->engine_count) {
+          SET_GPUINFO_PROCESS(process_info, gfx_engine_used, total_time);
+          SET_GPUINFO_STATIC(static_info, engine_count, fid->engine_count);
+  }
 
   if (!client_id_set)
     return false;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_gpuinfo_tpu.c 
new/nvtop-3.2.0+14/src/extract_gpuinfo_tpu.c
--- old/nvtop-3.1.0+100/src/extract_gpuinfo_tpu.c       1970-01-01 
01:00:00.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_gpuinfo_tpu.c        2025-06-28 
15:30:10.000000000 +0200
@@ -0,0 +1,301 @@
+/*
+ *
+ * Copyright (C) 2025 Robert Dyro <robert.d...@gmail.com>
+ *
+ * This file is part of Nvtop
+ *
+ * Nvtop is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nvtop is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nvtop.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "nvtop/extract_gpuinfo_common.h"
+#include "nvtop/time.h"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <math.h>
+#include <dlfcn.h>
+#include <sys/time.h>
+
+struct gpu_info_tpu {
+  struct gpu_info base;
+  int device_id;
+};
+
+struct tpu_chip_usage_data {
+  char name[8];
+  int64_t device_id;
+  int64_t memory_usage;
+  int64_t total_memory;
+  double duty_cycle_pct;
+  int64_t pid;
+};
+
+static bool gpuinfo_tpu_init(void);
+static void gpuinfo_tpu_shutdown(void);
+static const char *gpuinfo_tpu_last_error_string(void);
+static bool gpuinfo_tpu_get_device_handles(struct list_head *devices, unsigned 
*count);
+static void gpuinfo_tpu_populate_static_info(struct gpu_info *_gpu_info);
+static void gpuinfo_tpu_refresh_dynamic_info(struct gpu_info *_gpu_info);
+static void gpuinfo_tpu_get_running_processes(struct gpu_info *_gpu_info);
+static bool is_cache_valid(void);
+static bool refresh_tpu_cache(void);
+static void reset_tpu_cache(bool);
+static void free_ptr(void **ptr);
+
+struct gpu_vendor gpu_vendor_tpu = {
+    .init = gpuinfo_tpu_init,
+    .shutdown = gpuinfo_tpu_shutdown,
+    .last_error_string = gpuinfo_tpu_last_error_string,
+    .get_device_handles = gpuinfo_tpu_get_device_handles,
+    .populate_static_info = gpuinfo_tpu_populate_static_info,
+    .refresh_dynamic_info = gpuinfo_tpu_refresh_dynamic_info,
+    .refresh_running_processes = gpuinfo_tpu_get_running_processes,
+    .name = "TPU",
+};
+
+__attribute__((constructor)) static void init_extract_gpuinfo_tpu(void) { 
+  register_gpu_vendor(&gpu_vendor_tpu);
+}
+
+int64_t tpu_chip_count = -1;
+static struct gpu_info_tpu *gpu_infos;
+
+#define STRINGIFY(x) STRINGIFY_HELPER_(x)
+#define STRINGIFY_HELPER_(x) #x
+
+#define VENDOR_TPU 0x1ae0
+#define VENDOR_TPU_STR STRINGIFY(VENDOR_TPU)
+
+#define MAX(x, y) ((x >= y) ? (x) : (y))
+#define MIN(x, y) ((x <= y) ? (x) : (y))
+
+#define int64 long long
+
+int (*_tpu_chip_count)(void);
+int (*_tpu_metrics)(int port, int64 *device_ids, int64 *memory_usage, 
+                    int64 *total_memory, double *duty_cycle_pct, int n);
+int (*_tpu_pids)(int64 *pids, int n);
+
+char *libname = "libtpuinfo.so";
+// -1 means allowing libtpuinfo to select the default port
+// env LIBTPUINFO_GRPC_PORT={int} allows setting the port via an environment 
variable
+// $ env LIBTPUINFO_GRPC_PORT=8431 nvtop
+int tpu_runtime_monitoring_port = -1;  
+
+/* TPU info cache 
------------------------------------------------------------------------------- 
*/
+struct tpu_chip_usage_data *latest_chips_usage_data = NULL;
+nvtop_time last_cache_refresh;
+int64 *_pids, *_device_ids, *_memory_usage, *_total_memory;
+double* _duty_cycle_pct;
+
+bool is_cache_valid(void) {
+  nvtop_time current_time;
+  nvtop_get_current_time(&current_time);
+  uint64_t t_diff_ns = nvtop_difftime_u64(last_cache_refresh, current_time);
+  return t_diff_ns < 900 * 1000 * 1000; // 900ms
+}
+
+bool refresh_tpu_cache(void) {
+  if (is_cache_valid()) return true;
+  nvtop_get_current_time(&last_cache_refresh);
+  if (tpu_chip_count <= 0) return false;
+  if (_tpu_pids(_pids, tpu_chip_count) != 0) {
+    reset_tpu_cache(false);
+    return false;
+  }
+  for (int64_t i = 0; i < tpu_chip_count; i++) latest_chips_usage_data[i].pid 
= _pids[i];
+
+  if (_tpu_metrics(tpu_runtime_monitoring_port, _device_ids, _memory_usage, 
_total_memory,
+                   _duty_cycle_pct, tpu_chip_count) != 0) return false;
+  for (int64_t i = 0; i < tpu_chip_count; i++) {
+    latest_chips_usage_data[i].device_id = _device_ids[i];
+    latest_chips_usage_data[i].memory_usage = _memory_usage[i];
+    latest_chips_usage_data[i].total_memory = _total_memory[i];
+    latest_chips_usage_data[i].duty_cycle_pct = _duty_cycle_pct[i];
+  }
+  return true;
+}
+
+void reset_tpu_cache(bool fully) {
+  for (int64_t i = 0; i < tpu_chip_count; i++) {
+    latest_chips_usage_data[i].memory_usage = 0;
+    latest_chips_usage_data[i].duty_cycle_pct = 0;
+    latest_chips_usage_data[i].pid = -1;
+    if (fully) {
+      snprintf(latest_chips_usage_data[i].name, 
sizeof(latest_chips_usage_data[i].name), "%s", "N/A");
+      latest_chips_usage_data[i].device_id = 0;
+      latest_chips_usage_data[i].total_memory = 0;
+    }
+  }
+}
+/* TPU info cache 
------------------------------------------------------------------------------- 
*/
+
+bool gpuinfo_tpu_init(void) {
+  char* error_msg;
+  nvtop_get_current_time(&last_cache_refresh);
+  // invalidate cache by putting it in the past
+  last_cache_refresh = nvtop_substract_time(last_cache_refresh, 
(nvtop_time){10, 0}); 
+
+  // Load dynamic library symbols
+  void *handle = dlopen(libname, RTLD_LAZY);
+  if (!handle) {
+      error_msg = dlerror();
+#ifndef NDEBUG
+      if (error_msg != NULL) fprintf(stderr, "TPU support error: %s\n", 
error_msg);
+#endif
+      return false;
+  }
+
+  // Resolve the necessary symbols within the library
+  _tpu_chip_count = dlsym(handle, "tpu_chip_count");
+  error_msg = dlerror();
+  if (error_msg != NULL) {
+#ifndef NDEBUG
+      fprintf(stderr, "libtpuinfo can't resolve symbol `tpu_chip_count` with 
error: %s\n", error_msg);
+#endif
+      return false;
+  }
+  _tpu_pids = dlsym(handle, "tpu_pids");
+  error_msg = dlerror();
+  if (error_msg != NULL) {
+#ifndef NDEBUG
+      fprintf(stderr, "libtpuinfo can't resolve symbol `tpu_pids` with error: 
%s\n", error_msg);
+#endif
+      return false;
+  }
+  _tpu_metrics = dlsym(handle, "tpu_metrics");
+  error_msg = dlerror();
+  if (error_msg != NULL) {
+#ifndef NDEBUG
+      fprintf(stderr, "libtpuinfo can't resolve symbol `tpu_metrics` with 
error: %s\n", error_msg);
+#endif
+      return false;
+  }
+
+  // Discover TPU devices
+  tpu_chip_count = _tpu_chip_count();
+  if (tpu_chip_count == 0) {
+#ifndef NDEBUG
+    fprintf(stderr, "Found 0 TPU devices on the system.\n");
+#endif
+    return false;
+  }
+
+  // Allocate memory for TPU device data cache
+  latest_chips_usage_data = (struct 
tpu_chip_usage_data*)malloc(tpu_chip_count*sizeof(struct tpu_chip_usage_data));
+  _pids = (int64*)malloc(sizeof(int64) * tpu_chip_count);
+  _device_ids = (int64*)malloc(sizeof(int64) * tpu_chip_count);
+  _memory_usage = (int64*)malloc(sizeof(int64) * tpu_chip_count);
+  _total_memory = (int64*)malloc(sizeof(int64) * tpu_chip_count);
+  _duty_cycle_pct = (double*)malloc(sizeof(double) * tpu_chip_count);
+  reset_tpu_cache(true);
+  return true;
+}
+
+void free_ptr(void **ptr) {
+  if (ptr != NULL && *ptr != NULL) {
+    free(*ptr);
+    *ptr = NULL;
+  }
+}
+
+void gpuinfo_tpu_shutdown(void) {
+  free_ptr((void **)&gpu_infos);
+  free_ptr((void **)&latest_chips_usage_data);
+  free_ptr((void **)&_pids);
+  free_ptr((void **)&_device_ids);
+  free_ptr((void **)&_memory_usage);
+  free_ptr((void **)&_total_memory);
+  free_ptr((void **)&_duty_cycle_pct);
+  tpu_chip_count = -1;
+}
+
+const char *gpuinfo_tpu_last_error_string(void) { return "Err"; }
+
+static void add_tpu_chip(struct list_head *devices, unsigned *count) {
+  struct gpu_info_tpu *this_tpu = &gpu_infos[*count];
+  this_tpu->base.vendor = &gpu_vendor_tpu;
+  this_tpu->device_id = *count;
+  snprintf(this_tpu->base.pdev, PDEV_LEN, "TPU%u", *count);
+  list_add_tail(&this_tpu->base.list, devices);
+
+  this_tpu->base.processes_count = 0;
+  this_tpu->base.processes = NULL;
+  this_tpu->base.processes_array_size = 0;
+
+  *count = *count + 1;
+}
+
+bool gpuinfo_tpu_get_device_handles(struct list_head *devices_list, unsigned 
*count) {
+  *count = 0;
+  if (tpu_chip_count <= 0) return false;
+  gpu_infos = (struct gpu_info_tpu *)calloc(tpu_chip_count, 
sizeof(*gpu_infos));
+  if (!gpu_infos) return false;
+  for (int64_t i = 0; i < tpu_chip_count; i++) add_tpu_chip(devices_list, 
count);
+  return true;
+}
+
+void gpuinfo_tpu_populate_static_info(struct gpu_info *_gpu_info) {
+  struct gpu_info_tpu *gpu_info = container_of(_gpu_info, struct gpu_info_tpu, 
base);
+  struct gpuinfo_static_info *static_info = &gpu_info->base.static_info;
+  static_info->integrated_graphics = false;
+  static_info->encode_decode_shared = false;
+  RESET_ALL(static_info->valid);
+  snprintf(static_info->device_name, MIN(sizeof(static_info->device_name), 
PDEV_LEN), "%s", gpu_info->base.pdev);
+  SET_VALID(gpuinfo_device_name_valid, static_info->valid);
+}
+
+void gpuinfo_tpu_refresh_dynamic_info(struct gpu_info *_gpu_info) {
+  struct gpu_info_tpu *gpu_info = container_of(_gpu_info, struct gpu_info_tpu, 
base);
+  // struct gpuinfo_static_info *static_info = &gpu_info->base.static_info; // 
unused
+  struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info;
+
+  refresh_tpu_cache();
+
+  if (gpu_info->device_id >= tpu_chip_count) return;
+  struct tpu_chip_usage_data usage_data = 
latest_chips_usage_data[gpu_info->device_id];
+  double mem_util = round(1e2 * (double)(usage_data.memory_usage) / 
(double)MAX(1, usage_data.total_memory));
+  double tpu_util = round(usage_data.duty_cycle_pct);
+  SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, (int)tpu_util);
+  SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate, (int)mem_util);
+  SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, usage_data.total_memory);
+  SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, usage_data.memory_usage);
+  SET_GPUINFO_DYNAMIC(dynamic_info, free_memory, usage_data.total_memory - 
usage_data.memory_usage);
+
+  return;
+}
+
+void gpuinfo_tpu_get_running_processes(struct gpu_info *_gpu_info) {
+  struct gpu_info_tpu *gpu_info = container_of(_gpu_info, struct gpu_info_tpu, 
base);
+  if (gpu_info->device_id >= tpu_chip_count) return;
+  if (tpu_chip_count <= 0 || latest_chips_usage_data[gpu_info->device_id].pid 
< 0) {
+    _gpu_info->processes_count = 0;
+    return;
+  }
+  _gpu_info->processes_count = 1;
+  if (_gpu_info->processes_array_size == 0) {
+    _gpu_info->processes_array_size = 1;
+    _gpu_info->processes = (struct gpu_process*)malloc(1 * sizeof(struct 
gpu_process));
+    memset(_gpu_info->processes, 0, _gpu_info->processes_count * 
sizeof(*_gpu_info->processes));
+  }
+  _gpu_info->processes[0].type = gpu_process_compute;
+  _gpu_info->processes[0].pid = 
latest_chips_usage_data[gpu_info->device_id].pid;
+  _gpu_info->processes[0].gpu_memory_usage = 
_gpu_info->dynamic_info.used_memory;
+
+  SET_VALID(gpuinfo_process_gpu_memory_usage_valid, 
_gpu_info->processes[0].valid);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/extract_npuinfo_rockchip.c 
new/nvtop-3.2.0+14/src/extract_npuinfo_rockchip.c
--- old/nvtop-3.1.0+100/src/extract_npuinfo_rockchip.c  1970-01-01 
01:00:00.000000000 +0100
+++ new/nvtop-3.2.0+14/src/extract_npuinfo_rockchip.c   2025-06-28 
15:30:10.000000000 +0200
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright (C) 2025 YuLong Yao <feilongph...@gmail.com>
+ *
+ * This file is part of Nvtop and adapted from igt-gpu-tools from Intel 
Corporation.
+ *
+ * Nvtop is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Nvtop is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with nvtop.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "nvtop/extract_gpuinfo_common.h"
+#include "nvtop/time.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+struct gpu_info_rknpu {
+  struct gpu_info base;
+};
+
+static struct gpu_info_rknpu *rknpu_info = NULL;
+
+static bool gpuinfo_rknpu_init(void) {
+  if (access("/sys/kernel/debug/rknpu/load", R_OK) != 0) {
+    return false;
+  }
+  return true;
+}
+
+static void gpuinfo_rknpu_shutdown(void) {
+  if (rknpu_info) {
+    free(rknpu_info);
+    rknpu_info = NULL;
+  }
+}
+
+static const char *gpuinfo_rknpu_last_error_string(void) {
+  return "RK-NPU error";
+}
+
+static void add_rknpu_chip(struct list_head *devices, unsigned *count) {
+  extern struct gpu_vendor gpu_vendor_rknpu;
+  struct gpu_info_rknpu *this_npu = &rknpu_info[*count];
+  this_npu->base.vendor = &gpu_vendor_rknpu;
+  snprintf(this_npu->base.pdev, PDEV_LEN, "RK-NPU%d", *count);
+  list_add_tail(&this_npu->base.list, devices);
+
+  this_npu->base.processes_count = 0;
+  this_npu->base.processes = NULL;
+  this_npu->base.processes_array_size = 0;
+
+  *count += 1;
+}
+
+static bool gpuinfo_rknpu_get_device_handles(struct list_head *devices_list, 
unsigned *count) {
+  *count = 0;
+  rknpu_info = calloc(1, sizeof(struct gpu_info_rknpu));
+  if (!rknpu_info) return false;
+  add_rknpu_chip(devices_list, count);
+  return true;
+}
+
+static void gpuinfo_rknpu_populate_static_info(struct gpu_info *_gpu_info) {
+  struct gpu_info_rknpu *gpu_info = container_of(_gpu_info, struct 
gpu_info_rknpu, base);
+  struct gpuinfo_static_info *static_info = &gpu_info->base.static_info;
+
+  static_info->integrated_graphics = true;
+  static_info->encode_decode_shared = false;
+
+  RESET_ALL(static_info->valid);
+  snprintf(static_info->device_name, sizeof(static_info->device_name), "%s", 
gpu_info->base.pdev);
+  SET_VALID(gpuinfo_device_name_valid, static_info->valid);
+}
+
+static int read_int_from_file(const char *path) {
+  int value = 0;
+  FILE *fp = fopen(path, "r");
+  if (fp) {
+    fscanf(fp, "%d", &value);
+    fclose(fp);
+  }
+  return value;
+}
+
+static int read_npu_load(const char *file) {
+  FILE *fp = fopen(file, "r");
+  if (!fp) return -1;
+
+  char line[256]; int sum = 0, load=0, count = 0;
+  if (fgets(line, sizeof(line), fp))
+      for (char *p = line; (p = strstr(p, "Core")); p++)
+          if (sscanf(p, "Core%*d: %d%%", &load) == 1) {
+            sum += load;
+            count++;
+          }
+
+  fclose(fp);
+  return count ? sum / count : -1;
+}
+
+static int set_gpuinfo_dynamic_memory(struct gpuinfo_dynamic_info 
*dynamic_info) {
+  FILE *fp = fopen("/proc/meminfo", "r");
+  if (!fp) return -1;
+  char line[256];
+  unsigned long mem_total = 0, mem_available = 0;
+  while (fgets(line, sizeof(line), fp)) {
+    if (sscanf(line, "MemTotal: %lu kB", &mem_total) == 1) {
+      mem_total *= 1024;
+      SET_GPUINFO_DYNAMIC(dynamic_info, total_memory, mem_total);
+    } else if (sscanf(line, "MemAvailable: %lu kB", &mem_available) == 1) {
+      mem_available *= 1024;
+      SET_GPUINFO_DYNAMIC(dynamic_info, free_memory, mem_available);
+    }
+  }
+  fclose(fp);
+  if (mem_total > 0 && mem_available > 0) {
+    SET_GPUINFO_DYNAMIC(dynamic_info, used_memory, mem_total - mem_available);
+    SET_GPUINFO_DYNAMIC(dynamic_info, mem_util_rate,
+                        (dynamic_info->total_memory - 
dynamic_info->free_memory) * 100 / dynamic_info->total_memory);
+  }
+  return 0; 
+}
+
+static void gpuinfo_rknpu_refresh_dynamic_info(struct gpu_info *_gpu_info) {
+  struct gpu_info_rknpu *gpu_info = container_of(_gpu_info, struct 
gpu_info_rknpu, base);
+  struct gpuinfo_dynamic_info *dynamic_info = &gpu_info->base.dynamic_info;
+
+  int gpu_clock_speed = 
read_int_from_file("/sys/class/devfreq/fdab0000.npu/cur_freq") / 1000000;
+  int gpu_clock_speed_max = 
read_int_from_file("/sys/class/devfreq/fdab0000.npu/max_freq") / 1000000;
+  int gpu_util_rate = read_npu_load("/sys/kernel/debug/rknpu/load");
+  if (gpu_util_rate >= 0) 
+    SET_GPUINFO_DYNAMIC(dynamic_info, gpu_util_rate, gpu_util_rate);
+
+  SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed, gpu_clock_speed);
+  SET_GPUINFO_DYNAMIC(dynamic_info, gpu_clock_speed_max, gpu_clock_speed_max);
+
+  int gpu_temp = read_int_from_file("/sys/class/thermal/thermal_zone6/temp") / 
1000;
+  SET_GPUINFO_DYNAMIC(dynamic_info, gpu_temp, gpu_temp);
+
+  set_gpuinfo_dynamic_memory(dynamic_info);
+}
+
+static void gpuinfo_rknpu_get_running_processes(struct gpu_info *_gpu_info) {
+  _gpu_info->processes_count = 0;
+}
+
+struct gpu_vendor gpu_vendor_rknpu = {
+  .init = gpuinfo_rknpu_init,
+  .shutdown = gpuinfo_rknpu_shutdown,
+  .last_error_string = gpuinfo_rknpu_last_error_string,
+  .get_device_handles = gpuinfo_rknpu_get_device_handles,
+  .populate_static_info = gpuinfo_rknpu_populate_static_info,
+  .refresh_dynamic_info = gpuinfo_rknpu_refresh_dynamic_info,
+  .refresh_running_processes = gpuinfo_rknpu_get_running_processes,
+  .name = "RK-NPU"
+};
+
+__attribute__((constructor)) static void init_extract_gpuinfo_rknpu(void) {
+  register_gpu_vendor(&gpu_vendor_rknpu);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/interface.c 
new/nvtop-3.2.0+14/src/interface.c
--- old/nvtop-3.1.0+100/src/interface.c 2025-01-19 09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/interface.c  2025-06-28 15:30:10.000000000 +0200
@@ -2055,3 +2055,93 @@
   }
   return dontShowAgainOption;
 }
+
+void print_snapshot(struct list_head *devices, bool use_fahrenheit_option) {
+  gpuinfo_populate_static_infos(devices);
+  gpuinfo_refresh_dynamic_info(devices);
+  struct gpu_info *device;
+
+  printf("[\n");
+  list_for_each_entry(device, devices, list) {
+    const char *indent_level_two = "  ";
+    const char *indent_level_four = "   ";
+
+    const char *device_name_field = "device_name";
+    const char *gpu_clock_field = "gpu_clock";
+    const char *mem_clock_field = "mem_clock";
+    const char *temp_field = "temp";
+    const char *fan_field = "fan_speed";
+    const char *power_field = "power_draw";
+    const char *gpu_util_field = "gpu_util";
+    const char *mem_util_field = "mem_util";
+
+    printf("%s{\n", indent_level_two);
+
+    // Device Name
+    if (GPUINFO_STATIC_FIELD_VALID(&device->static_info, device_name))
+      printf("%s\"%s\": \"%s\",\n", indent_level_four, device_name_field, 
device->static_info.device_name);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, device_name_field);
+
+    // GPU Clock Speed
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, gpu_clock_speed))
+      printf("%s\"%s\": \"%uMHz\",\n", indent_level_four, gpu_clock_field, 
device->dynamic_info.gpu_clock_speed);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, gpu_clock_field);
+
+    // MEM Clock Speed
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, mem_clock_speed))
+      printf("%s\"%s\": \"%uMHz\",\n", indent_level_four, mem_clock_field, 
device->dynamic_info.mem_clock_speed);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, mem_clock_field);
+
+    // GPU Temperature
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, gpu_temp)) {
+      unsigned int temp_convert;
+      if (!use_fahrenheit_option)
+        temp_convert = device->dynamic_info.gpu_temp;
+      else
+        temp_convert = (unsigned)(32 + nearbyint(device->dynamic_info.gpu_temp 
* 1.8));
+
+      printf("%s\"%s\": \"%u%s\",\n", indent_level_four, temp_field, 
temp_convert, use_fahrenheit_option ? "F" : "C");
+    } else {
+      printf("%s\"%s\": null,\n", indent_level_four, temp_field);
+    }
+
+    // Fan speed
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, fan_speed))
+      printf("%s\"%s\": \"%u%%\",\n", indent_level_four, fan_field,
+             device->dynamic_info.fan_speed > 100 ? 100 : 
device->dynamic_info.fan_speed);
+    else if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, fan_rpm))
+      printf("%s\"%s\": \"%uRPM\",\n", indent_level_four, fan_field,
+             device->dynamic_info.fan_rpm > 9999 ? 9999 : 
device->dynamic_info.fan_rpm);
+    else if (device->static_info.integrated_graphics)
+      printf("%s\"%s\": \"CPU Fan\",\n", indent_level_four, fan_field);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, fan_field);
+
+    // Power draw
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, power_draw))
+      printf("%s\"%s\": \"%uW\",\n", indent_level_four, power_field, 
device->dynamic_info.power_draw / 1000);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, power_field);
+
+    // GPU Utilization
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, gpu_util_rate))
+      printf("%s\"%s\": \"%u%%\",\n", indent_level_four, gpu_util_field, 
device->dynamic_info.gpu_util_rate);
+    else
+      printf("%s\"%s\": null,\n", indent_level_four, gpu_util_field);
+
+    // Memory Utilization
+    if (GPUINFO_DYNAMIC_FIELD_VALID(&device->dynamic_info, mem_util_rate))
+      printf("%s\"%s\": \"%u%%\"\n", indent_level_four, mem_util_field, 
device->dynamic_info.mem_util_rate);
+    else
+      printf("%s\"%s\": null\n", indent_level_four, mem_util_field);
+
+    if (device->list.next == devices)
+      printf("%s}\n", indent_level_two);
+    else
+      printf("%s},\n", indent_level_two);
+  }
+  printf("]\n");
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/nvtop-3.1.0+100/src/nvtop.c 
new/nvtop-3.2.0+14/src/nvtop.c
--- old/nvtop-3.1.0+100/src/nvtop.c     2025-01-19 09:13:14.000000000 +0100
+++ new/nvtop-3.2.0+14/src/nvtop.c      2025-06-28 15:30:10.000000000 +0200
@@ -66,7 +66,9 @@
                                  "  -i --gpu-info     : Show bar with 
additional GPU parametres\n"
                                  "  -E --encode-hide  : Set encode/decode auto 
hide time in seconds "
                                  "(default 30s, negative = always on screen)\n"
-                                 "  -h --help         : Print help and exit\n";
+                                 "  -h --help         : Print help and exit\n"
+                                 "  -s --snapshot     : Output the current gpu 
stats without ncurses"
+                                 "(useful for scripting)\n";
 
 static const char versionString[] = "nvtop version " NVTOP_VERSION_STRING;
 
@@ -83,10 +85,11 @@
     {.name = "no-plot", .has_arg = no_argument, .flag = NULL, .val = 'p'},
     {.name = "no-processes", .has_arg = no_argument, .flag = NULL, .val = 'P'},
     {.name = "reverse-abs", .has_arg = no_argument, .flag = NULL, .val = 'r'},
+    {.name = "snapshot", .has_arg = no_argument, .flag = NULL, .val = 's'},
     {0, 0, 0, 0},
 };
 
-static const char opts[] = "hvd:c:CfE:pPri";
+static const char opts[] = "hvd:c:CfE:pPris";
 
 int main(int argc, char **argv) {
   (void)setlocale(LC_CTYPE, "");
@@ -101,6 +104,7 @@
   bool reverse_plot_direction_option = false;
   bool encode_decode_timer_option_set = false;
   bool show_gpu_info_bar = false;
+  bool show_snapshot = false;
   double encode_decode_hide_time = -1.;
   char *custom_config_file_path = NULL;
   while (true) {
@@ -161,6 +165,9 @@
     case 'r':
       reverse_plot_direction_option = true;
       break;
+    case 's':
+      show_snapshot = true;
+      break;
     case ':':
     case '?':
       switch (optopt) {
@@ -208,6 +215,12 @@
     return EXIT_SUCCESS;
   }
 
+  if (show_snapshot) {
+    print_snapshot(&monitoredGpus, use_fahrenheit_option);
+    gpuinfo_shutdown_info_extraction(&monitoredGpus);
+    return EXIT_SUCCESS;
+  }
+
   unsigned numWarningMessages = 0;
   const char **warningMessages;
   get_info_messages(&monitoredGpus, &numWarningMessages, &warningMessages);

++++++ nvtop.obsinfo ++++++
--- /var/tmp/diff_new_pack.y9H9hZ/_old  2025-07-14 10:56:43.157110378 +0200
+++ /var/tmp/diff_new_pack.y9H9hZ/_new  2025-07-14 10:56:43.161110544 +0200
@@ -1,5 +1,5 @@
 name: nvtop
-version: 3.1.0+100
-mtime: 1737274394
-commit: f901275e5b922835e9cf0301f873600bbdb9753b
+version: 3.2.0+14
+mtime: 1751117410
+commit: 339ee0b10a64ec51f43d27357b0068a40f16e9e4
 

Reply via email to