# -*- coding: UTF-8 -*- # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). # Copyright (c) 2019-2023 Raffaello Bonghi. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import re import os import pwd from .common import cat # Logging import logging # Create logger logger = logging.getLogger(__name__) MEM_TABLE_REG = re.compile(r'^(?P\w+)\s+(?P[^ ]+)\s+(?P\d+)\s+(?P\d+)(?P\w)\n') TOT_TABLE_REG = re.compile(r'total\s+(?P\d+)(?P\w)') def read_process_table(path_table): """ This method list all processes working with GPU ========== ============ ======== ============= user process PID size ========== ============ ======== ============= user name process number dictionary ========== ============ ======== ============= :return: list of all processes :type spin: list """ table = [] total = {} with open(path_table, "r") as fp: for line in fp: # Search line match = re.search(MEM_TABLE_REG, line) if match: parsed_line = match.groupdict() data = [ parsed_line['PID'], parsed_line['user'], parsed_line['process'], int(parsed_line['size']), ] table += [data] continue # Find total on table match = re.search(TOT_TABLE_REG, line) if match: parsed_line = match.groupdict() total = int(parsed_line['size']) continue # return total and table return total, table class ProcessService(object): def __init__(self): self.usernames = {4294967295: "root"} # board type self._root_path = "/sys/kernel" if os.getenv('JTOP_TESTING', False): self._root_path = "/fake_sys/kernel" logger.warning("Running in JTOP_TESTING folder={root_dir}".format(root_dir=self._root_path)) # Check if jetson board self._isJetson = os.path.isfile(self._root_path + "/debug/nvmap/iovmm/maps") # Get the clock ticks per second and page size self._clk_tck = os.sysconf('SC_CLK_TCK') # self._page_size = os.sysconf('SC_PAGE_SIZE') # Initialization memory logger.info("Process service started") def get_process_info(self, pid, gpu_mem_usage, process_name, uptime): # Check if exist folder if not os.path.isdir(os.path.join('/proc', pid)): return [] # https://man7.org/linux/man-pages/man5/proc.5.html stat = cat(os.path.join('/proc', pid, 'stat')).split() # Decode uid and find username uid = int(cat(os.path.join('/proc', pid, 'loginuid'))) if uid not in self.usernames: self.usernames[uid] = pwd.getpwuid(uid).pw_name # Read memory process # Extract resident set size (VmRSS) (Second field) # VmRSS is the resident set size of the process, which is the portion of the process's memory # that is held in RAM and is not swapped out to disk. This is the amount of memory that the process is currently using. mem_raw = cat(os.path.join('/proc', pid, 'statm')).split() vm_rss = int(mem_raw[1]) # CPU percent # https://stackoverflow.com/questions/16726779/how-do-i-get-the-total-cpu-usage-of-an-application-from-proc-pid-stat utime = float(stat[13]) stime = float(stat[14]) starttime = float(stat[21]) / self._clk_tck total_time = (utime + stime) / self._clk_tck proc_uptime = max(1, uptime - starttime) cpu_percent = 100 * (total_time / proc_uptime) process = [ int(pid), # pid process self.usernames[uid], # username "I", # GPU name "Graphic", # type process int(stat[17]), # Priority stat[2], # state cpu_percent, # CPU percent vm_rss, # MEM process gpu_mem_usage, # GPU mem usage process_name, # Process name ] return process def get_status(self): total = {} table = [] # Update table if self._isJetson: # Use the memory table to measure total, table = read_process_table(self._root_path + "/debug/nvmap/iovmm/maps") uptime = float(open('/proc/uptime', 'r').readline().split()[0]) table = [self.get_process_info(prc[0], prc[3], prc[2], uptime) for prc in table] return total, table # EOF