Skip to content

Commit

Permalink
Merge pull request #70 from rbonghi/release/3.0.0
Browse files Browse the repository at this point in the history
Release/3.0.0
  • Loading branch information
rbonghi authored Jul 19, 2020
2 parents feef704 + fa47bdd commit b247480
Show file tree
Hide file tree
Showing 31 changed files with 1,256 additions and 539 deletions.
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ jobs:
name: Test on python ${{ matrix.python-version }}
runs-on: ubuntu-latest
strategy:
# max-parallel: 1
matrix:
python-version: [2.7, 3.5, 3.6, 3.7, 3.8]

Expand Down
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ When you install jetson-stats are included:
* [jetson_config](#jetson_config)
* [jetson_release](#jetson_release)
* [jetson_swap](#jetson_swap)
* [jetson_variables](#jetson_variables)
* [jetson variables](#jetson_variables)

Read the [Wiki](https://github.com/rbonghi/jetson_stat/wiki) for more detailed information or read the package [documentation](https://rbonghi.github.io/jetson_stats).

Expand All @@ -31,27 +31,29 @@ It is a system monitoring utility that runs on the terminal and see and **contro
The prompt interface will be show like this image, **now clickable!**:
![jtop](https://github.com/rbonghi/jetson_stats/wiki/images/jtop.gif)

You can run the jtop with (_Suggested_ to run with **sudo**)
```elm
sudo jtop
You can run the jtop simple using a simple command `jtop`

YES! Sudo is **not** more required!
```console
nvidia@jetson-xavier-nx:~/$ jtop
```
Other options are availables with `-h` option:

Other options are available with `-h` option:
```console
nvidia@jetson-nano:~/$ sudo jtop -h
usage: jtop [-h] [--debug] [--no-warnings] [--restore] [--loop] [-r REFRESH]
[-p PAGE] [-v]
nvidia@jetson-xavier-nx:~/$ jtop -h
usage: jtop [-h] [--no-warnings] [--restore] [--loop] [-r REFRESH] [-p PAGE]
[-v]

jtop is system monitoring utility and runs on terminal

optional arguments:
-h, --help show this help message and exit
--debug Run with debug logger
--no-warnings Do not show warnings
--restore Reset Jetson configuration
--loop Automatically switch page every 5s
--no-warnings Do not show warnings (default: False)
--restore Reset Jetson configuration (default: False)
--loop Automatically switch page every 5s (default: False)
-r REFRESH, --refresh REFRESH
refresh interval
-p PAGE, --page PAGE Open fix page
refresh interval (default: 500)
-p PAGE, --page PAGE Open fix page (default: 1)
-v, --version show program's version number and exit
```
You can change page using _left_, _right_ arrow or _TAB_ to change page.
Expand Down Expand Up @@ -102,8 +104,8 @@ usage: createSwapFile [[[-d directory ] [-s size] -a] | [-h] | [--off]]
-h | --help This message
```

## [**jetson_variables**][jetson_variables]
This script generate the easy environment variables to know which is your Hardware version of the Jetson and which Jetpack you have already installed
## [**jetson variables**][jetson_variables]
When you install jetson-stats in your bash will be available a list of new environment variables to know which which hardware version is available are you working, which Jetpack is installed and other variable show below

![jtop](https://github.com/rbonghi/jetson_stats/wiki/images/jetson_env.png)

Expand Down
77 changes: 77 additions & 0 deletions examples/jtop_properties.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python
# -*- 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 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 <http://www.gnu.org/licenses/>.

from jtop import jtop


if __name__ == "__main__":

print("All accessible jtop properities")

with jtop() as jetson:
# boards
print('*** board ***')
print(jetson.board)
# jetson.ok() will provide the proper update frequency
while jetson.ok():
# CPU
print('*** CPUs ***')
print(jetson.cpu)
# GPU
print('*** GPU ***')
print(jetson.gpu)
# Engines
print('*** engine ***')
print(jetson.engine)
# nvpmodel
print('*** NV Power Model ***')
print(jetson.nvpmodel)
# jetson_clocks
print('*** jetson_clocks ***')
print(jetson.jetson_clocks)
# Status disk
print('*** disk ***')
print(jetson.disk)
# Status fans
print('*** fan ***')
print(jetson.fan)
# uptime
print('*** uptime ***')
print(jetson.uptime)
# local interfaces
print('*** local interfaces ***')
print(jetson.local_interfaces)
# Temperature
print('*** temperature ***')
print(jetson.temperature)
# Power
print('*** power ***')
print(jetson.power)
# EMC
print('*** emc ***')
print(jetson.emc)
# IRAM
print('*** ram ***')
print(jetson.ram)
# IRAM
print('*** iram ***')
print(jetson.iram)
# MTS
print('*** mts ***')
print(jetson.mts)
# EOF
14 changes: 0 additions & 14 deletions examples/quick_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,4 @@
while jetson.ok():
# Read tegra stats
print(jetson.stats)
# nvpmodel
print(jetson.nvpmodel)
# jetson_clocks
print(jetson.jetson_clocks)
# Status disk
print(jetson.disk)
# Status fans
print(jetson.fan)
# uptime
print(jetson.uptime)
# local interfaces
print(jetson.local_interfaces)
# boards
print(jetson.board)
# EOF
2 changes: 1 addition & 1 deletion jtop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
__email__ = "[email protected]"
# Version package
# https://packaging.python.org/guides/distributing-packages-using-setuptools/#choosing-a-versioning-scheme
__version__ = "3.0.0rc2"
__version__ = "3.0.0"
# EOF
17 changes: 11 additions & 6 deletions jtop/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import re
import signal
import os
import sys
Expand All @@ -26,15 +27,17 @@
# jtop service
from .service import JtopServer
# jtop client
from .jtop import jtop, get_version
from .jtop import jtop
# jtop exception
from .core import JtopException
from .core import JtopException, get_var
# GUI jtop interface
from .gui import JTOPGUI, ALL, GPU, CPU, MEM, CTRL, INFO
# Load colors
from .github import jetpack_missing, board_missing
# Create logger
logger = logging.getLogger(__name__)
# Version match
VERSION_RE = re.compile(r""".*__version__ = ["'](.*?)['"]""", re.S)
# Reference repository
REPOSITORY = "https://github.com/rbonghi/jetson_stats/issues"
LOOP_SECONDS = 5
Expand Down Expand Up @@ -66,6 +69,8 @@ def fail(message="ERR"):
def warning_messages(jetson, no_warnings=False):
if no_warnings:
return
# Read status version
version = get_var(VERSION_RE)
# Check is well stored the default jetson_clocks configuration
if jetson.jetson_clocks:
if not jetson.jetson_clocks.is_config:
Expand All @@ -75,10 +80,10 @@ def warning_messages(jetson, no_warnings=False):
print("[{status}] SUDO is no more required".format(status=bcolors.warning()))
# Check if jetpack is missing
if jetson.board.hardware['TYPE'] == "UNKNOWN" and jetson.board.hardware['BOARD'] and 'JETSON_DEBUG' not in os.environ:
print("[{status}] {link}".format(status=bcolors.warning(), link=board_missing(REPOSITORY, jetson, get_version())))
print("[{status}] {link}".format(status=bcolors.warning(), link=board_missing(REPOSITORY, jetson, version)))
# Check if jetpack is missing
if jetson.board.info['jetpack'] == "UNKNOWN" and jetson.board.info['L4T'] != "N.N.N":
print("[{status}] {link}".format(status=bcolors.warning(), link=jetpack_missing(REPOSITORY, jetson, get_version())))
print("[{status}] {link}".format(status=bcolors.warning(), link=jetpack_missing(REPOSITORY, jetson, version)))


def exit_signal(signum, frame):
Expand All @@ -97,7 +102,7 @@ def main():
parser.add_argument('--loop', dest="loop", help='Automatically switch page every {sec}s'.format(sec=LOOP_SECONDS), action="store_true", default=False)
parser.add_argument('-r', '--refresh', dest="refresh", help='refresh interval', type=int, default='500')
parser.add_argument('-p', '--page', dest="page", help='Open fix page', type=int, default=1)
parser.add_argument('-v', '--version', action='version', version='%(prog)s {version}'.format(version=get_version()))
parser.add_argument('-v', '--version', action='version', version='%(prog)s {version}'.format(version=get_var(VERSION_RE)))
# Parse arguments
args = parser.parse_args()
# Initialize signals
Expand All @@ -106,7 +111,7 @@ def main():
# Run jtop service
if args.service == 'service':
# Initialize logging level
logging.basicConfig(level=logging.INFO, filemode='w', format='%(name)s - %(levelname)s - %(message)s')
logging.basicConfig(level=logging.INFO, filemode='w', format='[%(levelname)s] %(name)s - %(message)s')
# Run service
try:
# Initialize stats server
Expand Down
3 changes: 2 additions & 1 deletion jtop/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@
from .cpu import CPU, cpu_models
from .engine import Engine, nvjpg
from .config import Config
from .memory import Memory, MemoryService
from .memory import MemoryService
from .command import Command
from .common import (
Board,
locate_commands,
import_os_variables,
get_var,
Expand Down
30 changes: 24 additions & 6 deletions jtop/core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import os
import sys
import logging
import threading
Expand All @@ -27,6 +28,7 @@
import Queue as queue
# Create logger
logger = logging.getLogger(__name__)
EXTRA_TIMEOUT = 1.0
# Reference:
# https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/
# https://stackoverflow.com/questions/37942022/returncode-of-popen-object-is-none-after-the-process-is-terminated/42376107
Expand All @@ -47,6 +49,16 @@ def __init__(self, message, errno=-1):
def __str__(self):
return "[errno:{errno}] {message}".format(message=self.message, errno=self.errno)

@staticmethod
def run_command(command, repeat=5, timeout=2):
cmd = Command(command)
for idx in range(repeat):
try:
return cmd(timeout=timeout)
except Command.TimeoutException as error:
logger.error("[{idx}] {error}".format(idx=idx, error=error))
raise Command.TimeoutException("Error to start {command}".format(command=command))

def __init__(self, command):
self.process = None
self.command = command
Expand All @@ -55,7 +67,8 @@ def __call__(self, timeout=None):
def target(out_queue, err_queue):
# Run process
try:
self.process = sp.Popen(self.command, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE)
# https://stackoverflow.com/questions/33277452/prevent-unexpected-stdin-reads-and-lock-in-subprocess
self.process = sp.Popen(self.command, stdout=sp.PIPE, stderr=sp.PIPE, stdin=open(os.devnull), preexec_fn=os.setsid)
# Read lines output
for line in iter(self.process.stdout.readline, b''):
line = line.decode('utf-8')
Expand All @@ -68,25 +81,30 @@ def target(out_queue, err_queue):
# Store error message
err_queue.put(sys.exc_info())
# Initialize lists
is_timeout = False
out_queue = queue.Queue()
err_queue = queue.Queue()
thread = threading.Thread(target=target, args=(out_queue, err_queue, ))
thread.start()
# Wait timeout process
thread.join(timeout)
if thread.is_alive():
logger.error('Terminating process')
if self.process:
logger.error('Terminating process: {command}'.format(command=self.command))
if self.process is not None:
self.process.terminate()
thread.join()
thread.join(timeout=EXTRA_TIMEOUT)
logger.warning('Process terminated: {command}'.format(command=self.command))
is_timeout = True
# Read the output
# Extract exception and raise
if not err_queue.empty():
ex_type, ex_value, tb_str = err_queue.get()
ex_value.__traceback__ = tb_str
raise ex_value
if not out_queue.queue and self.process.returncode != 0:
raise Command.TimeoutException('Process does not replied in time', self.process.returncode)
if is_timeout:
raise Command.TimeoutException('Process does not replied in time', -1)
if self.process.returncode != 0:
raise Command.TimeoutException('Error process:', self.process.returncode)
return list(out_queue.queue)

def communicate(self, timeout=None):
Expand Down
42 changes: 42 additions & 0 deletions jtop/core/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,48 @@
logger = logging.getLogger(__name__)


class Board:

def __init__(self):
self.info = {}
self.hardware = {}
self.libraries = {}
self._board = {'info': self.info, 'hardware': self.hardware, 'libraries': self.libraries}

def _update_libraries(self, libraries):
self.libraries = libraries
self._board = {'info': self.info, 'hardware': self.hardware, 'libraries': self.libraries}

def _update_init(self, init):
self.info = init['info']
self.hardware = init['hardware']
self._board = {'info': self.info, 'hardware': self.hardware, 'libraries': self.libraries}

def items(self):
return self._board.items()

def get(self, name, value=None):
if name in self._board:
return self._board[name]
else:
return value

def __getitem__(self, name):
return self._board[name]

def __iter__(self):
return iter(self._board)

def __next__(self):
return next(self._board)

def __len__(self):
return len(self._board)

def __repr__(self):
return str(self._board)


def locate_commands(name, commands):
for cmd in commands:
if os.path.exists(cmd):
Expand Down
7 changes: 6 additions & 1 deletion jtop/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ def get(self, instance, data):

@property
def path(self):
path = sys.prefix
if hasattr(sys, 'real_prefix'):
path = sys.real_prefix
if hasattr(sys, 'base_prefix'):
path = sys.base_prefix
# Return directory folder
return sys.prefix + '/local/jetson_stats'
return path + '/local/jetson_stats'

def _load(self):
config = {}
Expand Down
Loading

0 comments on commit b247480

Please sign in to comment.