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, >_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, >_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(¤t_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