├── .dockerignore ├── .github ├── CODEOWNERS ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── config.yml │ ├── engine-gui.md │ ├── feature-request.md │ ├── hardware-missing.md │ └── jetpack-missing.md ├── PULL_REQUEST_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE │ └── jetpack-missing.md ├── SECURITY.md ├── dependabot.yml └── workflows │ ├── auto-merge-dependabot.yml │ ├── auto-merge-jetpack.yml │ └── build-and-push.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── README.md ├── docs ├── Makefile ├── _templates │ ├── base.html │ └── sidebar │ │ └── adsense.html ├── advanced-usage.rst ├── conf.py ├── contributing.rst ├── docker.rst ├── images │ ├── architecture-old.drawio.png │ ├── architecture.drawio.png │ ├── favicon.png │ ├── jetson_config-01-main.png │ ├── jetson_config-02-jtop.png │ ├── jetson_config-03-desktop.png │ ├── jetson_env.png │ ├── jetson_release.png │ ├── jtop-color-filter.png │ ├── jtop-fail-user.png │ ├── jtop-restore.png │ ├── jtop.gif │ ├── jtop.png │ └── pages │ │ ├── 01-jtop.png │ │ ├── 02-jtop.png │ │ ├── 03-jtop.png │ │ ├── 04-jtop.png │ │ ├── 05-jtop.png │ │ ├── 06-jtop.png │ │ ├── 06B-jtop.png │ │ └── 07-jtop.png ├── index.rst ├── jtop │ ├── how_is_it_works.rst │ └── jtop.rst ├── make.bat ├── other-tools │ ├── environment_variables.rst │ ├── index.rst │ ├── jetson_config.rst │ ├── jetson_release.rst │ └── jetson_swap.rst ├── reference │ ├── exceptions.rst │ ├── fan.rst │ ├── gpu.rst │ ├── index.rst │ ├── jetson_clocks.rst │ ├── jtop.rst │ ├── memory.rst │ └── nvpmodel.rst ├── requirements.txt ├── sponsors.rst └── troubleshooting.rst ├── examples ├── jtop_callback.py ├── jtop_control.py ├── jtop_cpu.py ├── jtop_engines.py ├── jtop_fan.py ├── jtop_flask_server.py ├── jtop_hardware.py ├── jtop_logger.py ├── jtop_memory.py ├── jtop_processes.py ├── jtop_properties.py ├── jtop_server.py └── quick_read.py ├── jtop ├── __init__.py ├── __main__.py ├── core │ ├── __init__.py │ ├── command.py │ ├── common.py │ ├── config.py │ ├── cpu.py │ ├── engine.py │ ├── exceptions.py │ ├── fan.py │ ├── gpu.py │ ├── hardware.py │ ├── jetson_clocks.py │ ├── jetson_libraries.py │ ├── jetson_variables.py │ ├── memory.py │ ├── nvpmodel.py │ ├── power.py │ ├── processes.py │ ├── tegra_parse.py │ ├── tegrastats.py │ ├── temperature.py │ └── timer_reader.py ├── github.py ├── gui │ ├── __init__.py │ ├── jtopgui.py │ ├── jtopguiconfig.py │ ├── lib │ │ ├── __init__.py │ │ ├── chart.py │ │ ├── colors.py │ │ ├── common.py │ │ ├── dialog_window.py │ │ ├── linear_gauge.py │ │ ├── process_table.py │ │ └── smallbutton.py │ ├── pall.py │ ├── pcontrol.py │ ├── pcpu.py │ ├── pengine.py │ ├── pgpu.py │ ├── pinfo.py │ └── pmem.py ├── jetson_config.py ├── jetson_release.py ├── jetson_swap.py ├── jtop.py ├── service.py ├── terminal_colors.py ├── tests │ ├── __init__.py │ ├── conftest.py │ ├── marco_functions.py │ ├── test_00_service.py │ ├── test_01_outputs.py │ ├── test_02_fan.py │ ├── test_03_jetson_clocks.py │ ├── test_04_nvpmodel.py │ └── test_05_gui.py └── tests_gui │ ├── __init__.py │ ├── test_chart.py │ ├── test_gui_page.py │ ├── test_linear_gauge.py │ └── test_process_table.py ├── requirements.txt ├── scripts └── jtop_env.sh ├── services └── jtop.service ├── setup.cfg ├── setup.py ├── tests ├── Dockerfile.sphinx ├── Dockerfile.tox ├── jetson_clocks ├── local_test.sh ├── nvfancontrol ├── nvfancontrol.conf ├── nvfancontrol.service ├── nvpmodel └── tegrastats └── tox.ini /.dockerignore: -------------------------------------------------------------------------------- 1 | # Compiled python modules. 2 | *.pyc 3 | 4 | # Setuptools distribution folder. 5 | _dist/ 6 | _build/ 7 | doc/ 8 | 9 | # Python egg metadata, regenerated from source files by setuptools. 10 | /*.egg-info 11 | 12 | # log file 13 | *.log 14 | 15 | # Test virtual environment 16 | venv/ -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Restrict all files related to deploying to 2 | # require lead maintainer approval. 3 | 4 | .github/workflows/ @rbonghi 5 | .github/CODEOWNERS @rbonghi 6 | setup.py @rbonghi 7 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: rbonghi 4 | patreon: # Replace with a single Patreon username 5 | custom: https://paypal.me/RaffaelloBonghi 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | 12 | A clear and concise description of what the bug is. 13 | 14 | ## To Reproduce 15 | 16 | Steps to reproduce the behavior: 17 | 18 | 1. Go to '...' 19 | 2. Click on '....' 20 | 3. Scroll down to '....' 21 | 4. See error 22 | 23 | ## Screenshots 24 | 25 | If applicable, add screenshots to help explain your problem. 26 | 27 | ## Expected behavior 28 | 29 | A clear and concise description of what you expected to happen. 30 | 31 | ## Additional context 32 | 33 | Add any other context about the problem here. 34 | 35 | ### Board 36 | 37 | Output from `jetson_release -v`: 38 | 44 | 45 | * jetson-stats version: [e.g. 1.8] 46 | * P-Number: [e.g. pXXXX-XXXX] 47 | * Module: [e.g. NVIDIA Jetson XXX] 48 | * Jetpack: [e.g. 4.3] 49 | * L4T: [e.g. 5.2.1] 50 | 51 | ### Log from jtop.service 52 | 53 | Attach here the output from: `journalctl -u jtop.service -n 100 --no-pager` 54 | 55 | 58 | 59 | ### Log from jetson-stats installation 60 | 61 | Attach here the output from: `sudo -H pip3 install --no-cache-dir -v -U jetson-stats` 62 | 63 | 66 | 67 | ### RAW Data 68 | 69 | File from `jtop --error-log` attached 70 | 71 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 🆘 Troubleshooting 4 | url: https://rnext.it/jetson_stats/troubleshooting.html 5 | about: Check if your bug is documented here 6 | - name: 💬 Ask the Community 7 | url: https://discord.gg/BFbuJNhYzS 8 | about: Join jtop's Discord server 9 | - name: 📚 Documentation 10 | url: https://https://rnext.it/jetson_stats 11 | about: Make sure you read the relevant docs -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/engine-gui.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: GUI compact engine page missing 3 | about: jtop is not using a specific compact information page for this board 4 | title: GUI compact engine page missing [] 5 | labels: GUI,missing 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please add a new jtop engine compact page for 11 | 16 | ### Board 17 | 18 | - Model: 19 | - 699-level Part Number: 20 | - P-Number: 21 | - Module: 22 | - SoC: 23 | - CUDA Arch BIN: 24 | - Codename: 25 | - L4T: 26 | - Jetpack: 27 | 28 | 29 | ### jetson-stats 30 | 31 | - Version: 32 | 33 | 34 | ### Screenshot engine page 35 | 36 | Screnshoot page from jtop engine page attacched -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/hardware-missing.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Hardware Missing 3 | about: jetson-stats does not recognize this hardware 4 | title: Hardware Missing [] 5 | labels: Hardware,missing 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please update jetson-stats with this board 11 | 16 | ### Board 17 | 18 | - Model: 19 | - 699-level Part Number: 20 | - P-Number: 21 | - Module: 22 | - SoC: 23 | - CUDA Arch BIN: 24 | - Codename: 25 | - L4T: 26 | - Jetpack: 27 | 28 | 29 | ### jetson-stats 30 | 31 | - Version: 32 | 33 | 34 | ### RAW Data 35 | 36 | File from `jtop --error-log` attached 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/jetpack-missing.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Jetpack missing 3 | about: jetson-stats miss a JetPack 4 | title: Jetpack missing [L4T e.g. 5.2.1] 5 | labels: Jetpack,missing 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please update jetson-stats with new jetpack 11 | 12 | ### Linux for Tegra 13 | 14 | - L4T: 15 | 16 | ### jetson-stats 17 | 18 | - Version: 19 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/jetpack-missing.md: -------------------------------------------------------------------------------- 1 | # Add New Jetpack 2 | 3 | To add a new NVIDIA Jetpack release you can quicky do: 4 | 5 | Open file **`jtop/core/jetson_variables.py`** around line *49* there is a variable called **`NVIDIA_JETPACK`** add the new jetpack following the rule below: 6 | 7 | ```python 8 | "L4T version": "Jetpack" 9 | ``` 10 | 11 | Checklist: 12 | 13 | * [ ] Add Jetpack on **`jtop/core/jetson_variables.py`** 14 | * [ ] Increase with a minor release jtop variable **`__version__`** in **`jtop/__init__.py`** 15 | * [ ] See if all tests pass 16 | * [ ] Merge the release pull request with message "`Jetpack Release `" where `` is the same release in **`jtop/__init__.py`** 17 | * [ ] Get the release Pull request approved by a [CODEOWNER](https://github.com/rbonghi/jetson_stats/blob/master/.github/CODEOWNERS) 18 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | In general, due to limited maintainer bandwidth, only the latest version of jetson-stats is supported with patch releases. 6 | Exceptions may be made depending on the severity of the bug and the feasibility of backporting a fix to older releases. 7 | 8 | ## Reporting a Vulnerability 9 | 10 | jetson-stats uses GitHub's security advisory functionality for private vulnerability reports. 11 | To make a private report, use the "Report a vulnerability" button on https://github.com/rbonghi/jetson_stats/security/advisories 12 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: docker 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | # Check for updates to GitHub Actions every weekday 12 | interval: "daily" 13 | -------------------------------------------------------------------------------- /.github/workflows/auto-merge-dependabot.yml: -------------------------------------------------------------------------------- 1 | name: Auto-approve and merge Dependabot PRs 2 | 3 | on: 4 | pull_request_target: 5 | types: 6 | - opened 7 | - synchronize 8 | - reopened 9 | 10 | jobs: 11 | auto-approve: 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | # Step to auto-approve Dependabot PRs 16 | - name: Auto-approve Dependabot PR 17 | if: github.actor == 'dependabot[bot]' 18 | uses: hmarr/auto-approve-action@v4 19 | with: 20 | github-token: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | auto-merge: 23 | needs: auto-approve 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | # Step to checkout the repository 28 | - name: Checkout repository 29 | uses: actions/checkout@v4 30 | 31 | # Step to find the PR number of the open Dependabot PR 32 | - name: Find PR number 33 | id: find_pr 34 | run: | 35 | PR_NUMBER=$(gh pr list --state open --author "dependabot[bot]" --json number --jq '.[0].number') 36 | echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | 40 | # Step to enable auto-merge for the found PR number 41 | - name: Enable auto-merge 42 | if: ${{ steps.find_pr.outputs.PR_NUMBER != '' }} 43 | run: gh pr merge --auto --squash "${{ steps.find_pr.outputs.PR_NUMBER }}" 44 | env: 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/auto-merge-jetpack.yml: -------------------------------------------------------------------------------- 1 | name: Check Jetpack Release Version 2 | 3 | on: 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | 9 | jobs: 10 | check_version: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v4 15 | 16 | - name: Extract version from PR title 17 | id: extract_version 18 | run: | 19 | PR_TITLE="${{ github.event.pull_request.title }}" 20 | if [[ "$PR_TITLE" =~ ^Jetpack\ Release\ ([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then 21 | echo "version=${BASH_REMATCH[1]}" >> $GITHUB_ENV 22 | else 23 | echo "::notice::Skipping workflow as PR title does not match 'Jetpack Release '." 24 | exit 0 25 | fi 26 | 27 | - name: Check version in jtop/__init__.py 28 | id: check_version 29 | run: | 30 | FILE_VERSION=$(grep -oP "__version__\s*=\s*\"\K[0-9]+\.[0-9]+\.[0-9]+(?=\")" jtop/__init__.py) 31 | if [[ "$FILE_VERSION" == "${{ env.version }}" ]]; then 32 | echo "Version match confirmed." 33 | else 34 | echo "Version mismatch: Expected $FILE_VERSION but found ${{ env.version }} in PR title." >&2 35 | echo "::error::Version mismatch: Expected $FILE_VERSION but found ${{ env.version }} in PR title." >> $GITHUB_STEP_SUMMARY 36 | exit 1 37 | fi 38 | 39 | - name: Check version upgrade step 40 | id: check_version_step 41 | run: | 42 | OLD_VERSION=$(grep -oP "__version__\s*=\s*\"\K[0-9]+\.[0-9]+\.[0-9]+(?=\")" jtop/__init__.py) 43 | IFS='.' read -r old_major old_minor old_patch <<< "$OLD_VERSION" 44 | IFS='.' read -r new_major new_minor new_patch <<< "${{ env.version }}" 45 | if [[ "$new_major" -ne "$old_major" || "$new_minor" -ne "$old_minor" || "$new_patch" -ne $((old_patch + 1)) ]]; then 46 | echo "::error::Version upgrade is not a minor step upgrade. Expected $old_major.$old_minor.$((old_patch + 1)) but found $new_major.$new_minor.$new_patch." >> $GITHUB_STEP_SUMMARY 47 | exit 1 48 | fi 49 | 50 | - name: Check if jtop/core/jetson_variables.py is modified 51 | id: check_file_modified 52 | run: | 53 | if git diff --name-only origin/${{ github.event.pull_request.base.ref }} | grep -q "jtop/core/jetson_variables.py"; then 54 | echo "File jtop/core/jetson_variables.py is modified." 55 | else 56 | echo "File jtop/core/jetson_variables.py is not modified." >&2 57 | echo "::error::File jtop/core/jetson_variables.py is not modified but is required." >> $GITHUB_STEP_SUMMARY 58 | exit 1 59 | 60 | - name: Add comment on PR if check fails 61 | if: failure() 62 | uses: actions/github-script@v6 63 | with: 64 | github-token: ${{ secrets.GITHUB_TOKEN }} 65 | script: | 66 | const issue_number = context.payload.pull_request.number; 67 | const message = `🚨 The PR is missing required changes: 68 | - Ensure the PR title follows 'Jetpack Release '. 69 | - Ensure the version in jtop/__init__.py matches the PR title. 70 | - Ensure the version upgrade is a minor step (y.x.z → y.x.z+1). 71 | - Ensure jtop/core/jetson_variables.py is modified with the new Jetpack.`; 72 | github.rest.issues.createComment({ 73 | owner: context.repo.owner, 74 | repo: context.repo.repo, 75 | issue_number: issue_number, 76 | body: message 77 | }); 78 | 79 | - name: Create Release Branch 80 | if: success() 81 | run: | 82 | git checkout -b release/${{ env.version }} 83 | git push origin release/${{ env.version }} 84 | 85 | - name: Request Maintainer Approval 86 | if: success() 87 | uses: hmarr/auto-approve-action@v4 88 | with: 89 | github-token: ${{ secrets.GITHUB_TOKEN }} 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled python modules. 2 | *.pyc 3 | 4 | # Setuptools distribution folder. 5 | _build/ 6 | dist/ 7 | doc/ 8 | build/ 9 | 10 | # Python egg metadata, regenerated from source files by setuptools. 11 | /*.egg-info 12 | 13 | # log file 14 | *.log 15 | 16 | # Test virtual environment 17 | venv/ 18 | 19 | # Unit test / coverage reports 20 | .tox/ 21 | .cache/ 22 | swapfile 23 | test.* 24 | jtop_test.* 25 | *.csv 26 | .dccache 27 | 28 | .vscode/ 29 | .venv*/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | @rbonghi. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | FROM python:3.13.3-slim-bullseye 20 | 21 | ADD . /jetson_stats 22 | 23 | WORKDIR /jetson_stats 24 | 25 | RUN python3 -m pip install --upgrade pip && \ 26 | pip3 install -v . 27 | 28 | CMD ["jtop"] -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 2 | # Copyright (c) 2019-2023 Raffaello Bonghi. 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | # Include the README 18 | include *.md 19 | 20 | # Include requirements 21 | include requirements.txt 22 | 23 | # Include the license file 24 | include LICENSE 25 | 26 | # Include the data files 27 | recursive-include scripts * 28 | 29 | # Include services files 30 | recursive-include services *.service 31 | 32 | # Remove test docs and examples and tox.ini 33 | exclude tox.ini 34 | exclude Dockerfile 35 | exclude .dockerignore 36 | prune tests 37 | prune docs 38 | prune examples 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | jetson-stats 4 | 5 | [![jetson-stats](https://github.com/rbonghi/jetson_stats/raw/master/docs/images/jtop.png)](https://rnext.it/jetson_stats/) 6 | 7 |

8 | 9 |

10 | PyPI - Downloads 11 | PyPI version 12 | PyPI - Python Version 13 | PyPI - Format 14 | GitHub 15 | jetson-stats 16 | Docker Image Size (tag) 17 | Docker Pulls 18 | CI & CD 19 | CodeQL 20 |

21 |

22 | Twitter Follow 23 | robo.panther 24 | Join our Discord 25 |

26 | 27 | **jetson-stats** is a package for **monitoring** and **control** your [NVIDIA Jetson](https://developer.nvidia.com/buy-jetson) [Orin, Xavier, Nano, TX] series. 28 | 29 | jetson-stats is a powerful tool to analyze your board, you can use with a stand alone application with `jtop` or import in your python script, the main features are: 30 | 31 | - Decode hardware, architecture, L4T and NVIDIA Jetpack 32 | - Monitoring, CPU, GPU, Memory, Engines, fan 33 | - Control NVP model, fan speed, jetson_clocks 34 | - Importable in a python script 35 | - Dockerizable in a container 36 | - Do not need super user 37 | - Tested on many different hardware configurations 38 | - Works with all NVIDIA Jetpack 39 | 40 | ## Install 41 | 42 | jetson-stats can be installed with [pip](https://pip.pypa.io), but need **superuser**: 43 | 44 | ```console 45 | sudo pip3 install -U jetson-stats 46 | ``` 47 | 48 | _Don't forget to **logout/login** or **reboot** your board_ 49 | 50 |
51 | 52 | **🚀 That's it! 🚀** 53 | 54 |
55 | 56 | ## Run 57 | 58 | Start jtop it's pretty simple just write `jtop`! 59 | 60 | ```console 61 | jtop 62 | ``` 63 | 64 | A simple interface will appear on your terminal, more capabilities are documented at [_jtop_](https://rnext.it/jetson_stats/jtop/jtop.html) page. 65 | 66 |
67 | 68 | [![jtop](https://github.com/rbonghi/jetson_stats/raw/master/docs/images/jtop.gif)](https://github.com/rbonghi/jetson_stats) 69 | 70 |
71 | 72 | ## Library 73 | 74 | You can use jtop such a python library to integrate in your software 75 | 76 | ```python 77 | from jtop import jtop 78 | 79 | with jtop() as jetson: 80 | # jetson.ok() will provide the proper update frequency 81 | while jetson.ok(): 82 | # Read tegra stats 83 | print(jetson.stats) 84 | ``` 85 | 86 | You can also use jtop with your _virualenv_! 87 | 88 | More information available at [_advanced usage_](https://rnext.it/jetson_stats/advanced-usage.html) page. 89 | 90 | ## Docker 91 | 92 | You can run directly in Docker jtop, you need only to: 93 | 94 | 1. Install jetson-stats on your **host** 95 | 2. Install jetson-stats on your container as well 96 | 3. Pass to your container `/run/jtop.sock:/run/jtop.sock` 97 | 98 | You can try running this command 99 | 100 | ```console 101 | docker run --rm -it -v /run/jtop.sock:/run/jtop.sock rbonghi/jetson_stats:latest 102 | ``` 103 | 104 | More information available at [_docker_](https://rnext.it/jetson_stats/docker.html) documentation page. 105 | 106 | ## Sponsorship 107 | 108 | If your company benefits from this library, please consider [💖 sponsoring its development](https://github.com/sponsors/rbonghi). 109 | 110 | ## Documentation 111 | 112 | jetson-stats has usage and reference documentation at , there is also a [🆘 troubleshooting](https://rnext.it/jetson_stats/troubleshooting.html) page. 113 | 114 | ## Community 115 | 116 | jetson-stats has a [community Discord channel](https://discord.gg/BFbuJNhYzS) for asking questions and collaborating with other contributors. Drop by and say hello 👋 117 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS = '-W' 7 | SPHINXBUILD = sphinx-build 8 | PAPER = 9 | BUILDDIR = _build 10 | 11 | # Internal variables 12 | PAPEROPT_a4 = -D latex_paper_size=a4 13 | PAPEROPT_letter = -D latex_paper_size=letter 14 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . 15 | 16 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest 17 | 18 | help: 19 | @echo "Please use \`make ' where is one of" 20 | @echo " html to make standalone HTML files" 21 | @echo " dirhtml to make HTML files named index.html in directories" 22 | @echo " singlehtml to make a single large HTML file" 23 | @echo " pickle to make pickle files" 24 | @echo " json to make JSON files" 25 | @echo " htmlhelp to make HTML files and a HTML help project" 26 | @echo " qthelp to make HTML files and a qthelp project" 27 | @echo " devhelp to make HTML files and a Devhelp project" 28 | @echo " epub to make an epub" 29 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 30 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 31 | @echo " text to make text files" 32 | @echo " man to make manual pages" 33 | @echo " changes to make an overview of all changed/added/deprecated items" 34 | @echo " linkcheck to check all external links for integrity" 35 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 36 | 37 | clean: 38 | -rm -rf $(BUILDDIR)/* 39 | 40 | html: 41 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 42 | @echo 43 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 44 | 45 | dirhtml: 46 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 47 | @echo 48 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 49 | 50 | singlehtml: 51 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 52 | @echo 53 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 54 | 55 | pickle: 56 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 57 | @echo 58 | @echo "Build finished; now you can process the pickle files." 59 | 60 | json: 61 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 62 | @echo 63 | @echo "Build finished; now you can process the JSON files." 64 | 65 | htmlhelp: 66 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 67 | @echo 68 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 69 | ".hhp project file in $(BUILDDIR)/htmlhelp." 70 | 71 | qthelp: 72 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 73 | @echo 74 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 75 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 76 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/urllib3.qhcp" 77 | @echo "To view the help file:" 78 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/urllib3.qhc" 79 | 80 | devhelp: 81 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 82 | @echo 83 | @echo "Build finished." 84 | @echo "To view the help file:" 85 | @echo "# mkdir -p $$HOME/.local/share/devhelp/urllib3" 86 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/urllib3" 87 | @echo "# devhelp" 88 | 89 | epub: 90 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 91 | @echo 92 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 93 | 94 | latex: 95 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 96 | @echo 97 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 98 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 99 | "(use \`make latexpdf' here to do that automatically)." 100 | 101 | latexpdf: 102 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 103 | @echo "Running LaTeX files through pdflatex..." 104 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 105 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 106 | 107 | text: 108 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 109 | @echo 110 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 111 | 112 | man: 113 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 114 | @echo 115 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 116 | 117 | changes: 118 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 119 | @echo 120 | @echo "The overview file is in $(BUILDDIR)/changes." 121 | 122 | linkcheck: 123 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 124 | @echo 125 | @echo "Link check complete; look for any errors in the above output " \ 126 | "or in $(BUILDDIR)/linkcheck/output.txt." 127 | 128 | doctest: 129 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 130 | @echo "Testing of doctests in the sources finished, look at the " \ 131 | "results in $(BUILDDIR)/doctest/output.txt." 132 | 133 | # https://github.com/executablebooks/sphinx-autobuild 134 | livehtml: 135 | sphinx-autobuild "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_templates/base.html: -------------------------------------------------------------------------------- 1 | {% extends "!base.html" %} 2 | 3 | {%- block extrahead %} 4 | {{ super() }} 5 | {% endblock %} 6 | 7 | {% block theme_scripts %} 8 | {{ super() }} 9 | 10 | 11 | 12 | 19 | 20 | 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /docs/_templates/sidebar/adsense.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 9 |
-------------------------------------------------------------------------------- /docs/advanced-usage.rst: -------------------------------------------------------------------------------- 1 | 👨‍💻 Advanced Usage 2 | ===================== 3 | 4 | .. currentmodule:: jtop 5 | 6 | You can install jtop in a virtual environment or in a docker following the guidelines below 7 | 8 | .. admonition:: Virtual environment 9 | 10 | If you need to install in a virtual environment like *virtualenv*, you **must** install before in your host **and after** in your environment, like: 11 | 12 | .. code-block:: bash 13 | 14 | virtualenv venv 15 | source venv/bin/activate 16 | pip install -U jetson-stats 17 | 18 | 19 | **jtop** is a complete controller of all systems in your NVIDIA Jetson 20 | 21 | * Tegrastats 22 | * NVP Model 23 | * Fan 24 | * Status board (i.g. Model version, Jetpack, … ) 25 | 26 | .. code-block:: python 27 | 28 | from jtop import jtop 29 | 30 | 31 | You can initialize the jtop node like a file i.g. 32 | 33 | .. code-block:: python 34 | 35 | with jtop() as jetson: 36 | # jetson.ok() will provide the proper update frequency 37 | while jetson.ok(): 38 | # Read tegra stats 39 | print(jetson.stats) 40 | 41 | Or manually start up with the basic function open/close 42 | 43 | .. code-block:: python 44 | 45 | jetson = jtop() 46 | jetson.start() 47 | stat = jetson.stats 48 | jetson.close() 49 | 50 | You can read the status of your NVIDIA Jetson via callback 51 | 52 | .. code-block:: python 53 | 54 | def read_stats(jetson): 55 | print(jetson.stats) 56 | 57 | # Open the jtop 58 | jetson = jtop() 59 | # Attach a function where you can read the status of your jetson 60 | jetson.attach(read_stats) 61 | jetson.loop_for_ever() 62 | 63 | Other examples are available in `example folder `_. -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # flake8: noqa 2 | 3 | from __future__ import annotations 4 | import jtop 5 | 6 | import os 7 | import sys 8 | from datetime import date 9 | 10 | # -- Path setup -------------------------------------------------------------- 11 | 12 | # If extensions (or modules to document with autodoc) are in another directory, 13 | # add these directories to sys.path here. If the directory is relative to the 14 | # documentation root, use os.path.abspath to make it absolute, like shown here. 15 | root_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) 16 | sys.path.insert(0, root_path) 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | 21 | project = 'jetson-stats' 22 | author = 'Raffaello Bonghi' 23 | copyright = f"{date.today().year}, {author}" 24 | 25 | 26 | # The short X.Y version. 27 | version = jtop.__version__ 28 | # The full version, including alpha/beta/rc tags. 29 | release = version 30 | 31 | # -- General configuration --------------------------------------------------- 32 | 33 | # Add any Sphinx extension module names here, as strings. They can be 34 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 35 | # ones. 36 | # https://github.com/wpilibsuite/sphinxext-opengraph 37 | extensions = [ 38 | "sphinx.ext.autodoc", 39 | "sphinx_copybutton", 40 | "sphinx.ext.doctest", 41 | "sphinx.ext.intersphinx", 42 | "sphinxext.opengraph", 43 | ] 44 | 45 | source_suffix = ['.rst'] 46 | 47 | # The master toctree document. 48 | master_doc = "index" 49 | 50 | # Add any paths that contain templates here, relative to this directory. 51 | templates_path = ['_templates'] 52 | 53 | # List of patterns, relative to source directory, that match files and 54 | # directories to ignore when looking for source files. 55 | # This pattern also affects html_static_path and html_extra_path. 56 | exclude_patterns = ["_build"] 57 | 58 | # The name of the Pygments (syntax highlighting) style to use. 59 | pygments_style = "friendly" 60 | 61 | intersphinx_mapping = {"python": ("https://docs.python.org/3", None)} 62 | 63 | # Show typehints as content of the function or method 64 | autodoc_typehints = "description" 65 | 66 | copybutton_selector = "div:not(.no-copybutton) > div.highlight > pre" 67 | 68 | # opengraph configuration 69 | ogp_site_url = "https://rnext.it/jetson_stats/" 70 | ogp_image = "https://rnext.it/jetson_stats/_images/jtop.png" 71 | ogp_enable_meta_description = True 72 | 73 | # -- Options for HTML output ------------------------------------------------- 74 | 75 | # The theme to use for HTML and HTML Help pages. See the documentation for 76 | # a list of builtin themes. 77 | # 78 | # Reference: https://pradyunsg.me/furo/ 79 | html_theme = "furo" 80 | html_favicon = "images/favicon.png" 81 | 82 | html_title = f"{project} {version}" 83 | 84 | # Add any paths that contain custom static files (such as style sheets) here, 85 | # relative to this directory. They are copied after the builtin static files, 86 | # so a file named "default.css" will overwrite the builtin "default.css". 87 | html_static_path = [] 88 | 89 | html_theme_options = { 90 | "announcement": """ 91 | 93 | 💖 Support jetson-stats on GitHub Sponsors 94 | 95 | """, 96 | "footer_icons": [ 97 | { 98 | "name": "GitHub", 99 | "url": "https://github.com/rbonghi/jetson_stats", 100 | "html": """ 101 | 102 | 103 | 104 | """, 105 | "class": "", 106 | }, 107 | ], 108 | "source_repository": "https://github.com/rbonghi/jetson_stats", 109 | "source_branch": "master", 110 | "source_directory": "docs/", 111 | } 112 | 113 | html_sidebars = { 114 | "**": [ 115 | "sidebar/brand.html", 116 | "sidebar/search.html", 117 | "sidebar/scroll-start.html", 118 | "sidebar/navigation.html", 119 | "sidebar/ethical-ads.html", 120 | "sidebar/scroll-end.html", 121 | "sidebar/variant-selector.html", 122 | "sidebar/adsense.html", 123 | ] 124 | } 125 | # EOF 126 | -------------------------------------------------------------------------------- /docs/contributing.rst: -------------------------------------------------------------------------------- 1 | ✨ Contributing 2 | ================ 3 | 4 | jetson-stats is a community-maintained project and we happily accept contributions. 5 | 6 | If you want to add a new **Jetpack** release follow these quick rules or if you want make a new feature or fix a bug you are on the right page. 7 | 8 | Add a new Jetpack 9 | ----------------- 10 | 11 | If you want to add a new Jetpack to fix the warning: 12 | 13 | .. code-block:: console 14 | :class: no-copybutton 15 | 16 | user@board:~$ jtop 17 | [WARN] jetson-stats not supported for [L4T 35.2.1] 18 | Please, try: sudo pip3 install -U jetson-stats or 19 | open a Github issue (press CTRL + Click) 20 | 21 | 1. Open file **jtop/core/jetson_variables.py** around line *49* there is a variable called **NVIDIA_JETPACK** add the new jetpack following the rule below: 22 | 23 | .. code-block:: python 24 | :class: no-copybutton 25 | 26 | "L4T version": "Jetpack" 27 | 28 | 2. Increase with a minor release jtop variable **__version__** in **jtop/__init__.py** 29 | 3. Create a pull request and append ``&template=jetpack-missing.md`` to the URL before submitting in order to include our release checklist in the pull request description. 30 | 4. Open a pull request with message "**Jetpack Release **" where **** is the same release in **jtop/__init__.py** 31 | 5. Follow the checklist! 32 | 33 | Add new feature or fix a bug 34 | ---------------------------- 35 | 36 | If you wish to add a new feature or fix a bug: 37 | 38 | #. `Check for open issues `_ or open 39 | a fresh issue to start a discussion around a feature idea or a bug. There is 40 | a *Contributor Friendly* tag for issues that should be ideal for people who 41 | are not very familiar with the codebase yet. 42 | #. Fork the `jetson-stats repository on Github `_ 43 | to start making your changes. 44 | #. Write a test which shows that the bug was fixed or that the feature works 45 | as expected. 46 | #. Send a pull request and bug the maintainer until it gets merged and published. 47 | 48 | Setting up your developing environment 49 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 50 | 51 | Clone and build in developer mode jetson-stats 52 | 53 | .. code-block:: console 54 | 55 | git clone https://github.com/rbonghi/jetson_stats.git 56 | cd jetson_stats 57 | sudo pip3 install -v -e . 58 | 59 | 60 | Manually stop and disable jtop service 61 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 62 | 63 | If you want to manually control the jtop service you need to disable the service and manually start one in a terminal, 64 | following the commands below 65 | 66 | .. code-block:: console 67 | 68 | sudo systemctl stop jtop.service 69 | sudo systemctl disable jtop.service 70 | 71 | Now you can work running in your terminal the jtop service 72 | 73 | .. code-block:: console 74 | 75 | sudo JTOP_SERVICE=True jtop --force 76 | 77 | Restore jtop service 78 | ^^^^^^^^^^^^^^^^^^^^ 79 | 80 | .. code-block:: console 81 | 82 | sudo systemctl enable jtop.service 83 | sudo systemctl start jtop.service 84 | 85 | Test this package 86 | ----------------- 87 | 88 | Before commit you can test jetson-stats on multiple python version and check if the documentation is built 89 | 90 | This script works with docker, and you can quickly run it. 91 | 92 | .. code-block:: console 93 | 94 | bash tests/local_test.sh 95 | 96 | When you run this script will do: 97 | 98 | 1. Build and compile all python images (2.7,3.6,3.7,3.8,3.9.3.10,3.11) 99 | 2. Build documentation image (Sphinx) 100 | 101 | There are different options: 102 | 103 | .. code-block:: console 104 | :class: no-copybutton 105 | 106 | user@workstation:~/jetson_stats$ bash tests/local_test.sh --help 107 | Jetson_stats tox local test. USE ONLY IN A TEST DESKTOP MACHINE! 108 | Usage: 109 | tests/local_test.sh [options] 110 | options, 111 | -h|--help | This help 112 | --debug | Run image 113 | -py|--python [PYHTON] | Set a specific python version, example PYTHON=3.9 114 | --doc | Run and build ONLY the documentation 115 | 116 | Live docker with tox 117 | ^^^^^^^^^^^^^^^^^^^^ 118 | 119 | Run tox or work live from the terminal 120 | 121 | .. code-block:: console 122 | 123 | bash tests/local_test.sh --debug -py 3.9 124 | 125 | Test documentation 126 | ^^^^^^^^^^^^^^^^^^ 127 | 128 | If you want to run **only** the documentation: 129 | 130 | .. code-block:: console 131 | 132 | bash tests/local_test.sh --doc 133 | 134 | Test GUI 135 | ^^^^^^^^ 136 | 137 | If you want to test or develop the GUI library 138 | 139 | You can run this command from your terminal `python3 -m jtop.tests_gui.x` where **x** is the name of the file, example 140 | 141 | .. code-block:: console 142 | 143 | python3 -m jtop.tests_gui.gui_page 144 | 145 | Release 146 | ------- 147 | 148 | - Announce intent to release on `Discord `_, see if anyone wants to include last minute changes. 149 | - Update ``jtop/__init__.py`` with the proper version number 150 | - Commit the changes to a ``release-X.Y.Z`` branch. 151 | - Create a pull request with name ``Release/X.Y.Z`` 152 | - Release a new `tag `_ will automatically generate a new version 153 | 154 | .. code-block:: console 155 | :class: no-copybutton 156 | 157 | git tag -a -m -------------------------------------------------------------------------------- /docs/docker.rst: -------------------------------------------------------------------------------- 1 | 🐋 Docker 2 | ========= 3 | 4 | .. currentmodule:: jtop 5 | 6 | You can run directly in Docker jtop, you need only to: 7 | 8 | 1. Install jetson-stats on your **host** 9 | 2. Install jetson-stats on your container as well 10 | 3. Pass to your container ``/run/jtop.sock:/run/jtop.sock`` 11 | 12 | You can try running this command 13 | 14 | .. code-block:: bash 15 | 16 | docker run --rm -it -v /run/jtop.sock:/run/jtop.sock rbonghi/jetson_stats:latest 17 | 18 | Design your Dockerfile 19 | ---------------------- 20 | 21 | jetson-stats need few things to be installed on your container. 22 | 23 | 1. ``apt-get install -y python3`` 24 | 2. ``apt-get install -y python3-pip`` *or* you can install from **source** 25 | 26 | Below a simple example to install jetson-stats 27 | 28 | .. code-block:: docker 29 | 30 | FROM python:3-buster 31 | RUN pip install -U jetson-stats 32 | 33 | Tips and tricks 34 | --------------- 35 | 36 | If you work with different **multiple users** on your docker container: 37 | 38 | .. code-block:: bash 39 | 40 | docker run --group-add $JTOP_GID --rm -it -v /run/jtop.sock:/run/jtop.sock rbonghi/jetson_stats:latest 41 | 42 | You can get the ``JTOP_GID`` by running: 43 | 44 | .. code-block:: bash 45 | 46 | getent group jtop | awk -F: '{print $3}' 47 | 48 | Issue reference `#391 `_ -------------------------------------------------------------------------------- /docs/images/architecture-old.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/architecture-old.drawio.png -------------------------------------------------------------------------------- /docs/images/architecture.drawio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/architecture.drawio.png -------------------------------------------------------------------------------- /docs/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/favicon.png -------------------------------------------------------------------------------- /docs/images/jetson_config-01-main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jetson_config-01-main.png -------------------------------------------------------------------------------- /docs/images/jetson_config-02-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jetson_config-02-jtop.png -------------------------------------------------------------------------------- /docs/images/jetson_config-03-desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jetson_config-03-desktop.png -------------------------------------------------------------------------------- /docs/images/jetson_env.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jetson_env.png -------------------------------------------------------------------------------- /docs/images/jetson_release.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jetson_release.png -------------------------------------------------------------------------------- /docs/images/jtop-color-filter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jtop-color-filter.png -------------------------------------------------------------------------------- /docs/images/jtop-fail-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jtop-fail-user.png -------------------------------------------------------------------------------- /docs/images/jtop-restore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jtop-restore.png -------------------------------------------------------------------------------- /docs/images/jtop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jtop.gif -------------------------------------------------------------------------------- /docs/images/jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/jtop.png -------------------------------------------------------------------------------- /docs/images/pages/01-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/01-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/02-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/02-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/03-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/03-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/04-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/04-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/05-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/05-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/06-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/06-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/06B-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/06B-jtop.png -------------------------------------------------------------------------------- /docs/images/pages/07-jtop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rbonghi/jetson_stats/0a2796ec64eb63bdf76570661518509f719da0f0/docs/images/pages/07-jtop.png -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | jetson-stats 2 | ============ 3 | 4 | .. toctree:: 5 | :hidden: 6 | :maxdepth: 3 7 | 8 | rnext.it 9 | Community Discord 10 | sponsors 11 | jtop/jtop 12 | advanced-usage 13 | docker 14 | reference/index 15 | other-tools/index 16 | troubleshooting 17 | contributing 18 | GitHub 19 | 20 | .. toctree:: 21 | :hidden: 22 | :caption: Project 23 | 24 | ros_jetson_stats 25 | isaac_ros_jetson 26 | Grafana plugin 27 | 28 | 29 | jetson-stats is a package for monitoring and control your NVIDIA Jetson [Orin, Xavier, Nano, TX] series. Works with all NVIDIA Jetson ecosystem. 30 | 31 | .. image:: images/jtop.gif 32 | :align: center 33 | 34 | Installing 35 | ---------- 36 | 37 | jetson-stats can be installed with `pip `_ 38 | 39 | .. code-block:: bash 40 | 41 | sudo pip3 install -U jetson-stats 42 | 43 | Don't forget to **logout/login** or **reboot** your board 44 | 45 | **🚀 That's it! 🚀** 46 | 47 | Run 48 | --- 49 | 50 | Start jtop it's pretty simple just write `jtop`! 51 | 52 | .. code-block:: bash 53 | 54 | jtop 55 | 56 | A simple interface will appear on your terminal, more capabilities are documented at :doc:`jtop/jtop` page. 57 | 58 | Advanced usage 59 | -------------- 60 | 61 | The more in-depth :doc:`advanced-usage` guide is the place to jtop such a python library and use for your project. 62 | 63 | .. code-block:: python 64 | 65 | from jtop import jtop 66 | 67 | with jtop() as jetson: 68 | # jetson.ok() will provide the proper update frequency 69 | while jetson.ok(): 70 | # Read tegra stats 71 | print(jetson.stats) 72 | 73 | The :doc:`reference/index` documentation provides API-level documentation. 74 | 75 | 76 | Compatibility 77 | ------------- 78 | 79 | jetson-stats is compatible with: 80 | 81 | * NVIDIA Clara AGX (experimental) 82 | * NVIDIA Jetson Orin Series 83 | * NVIDIA Jetson Orin Nano 84 | * NVIDIA Jetson Orin NX 85 | * NVIDIA Jetson AGX Orin 86 | * NVIDIA Jetson IGX Orin (experimental) 87 | * NVIDIA Jetson Xavier Series 88 | * NVIDIA Jetson AGX Xavier Industrial 89 | * NVIDIA Jetson AGX Xavier 90 | * NVIDIA Jetson Xavier NX 91 | * NVIDIA Jetson Nano 92 | * NVIDIA Jetson TX Series 93 | * NVIDIA Jetson TX2 NX 94 | * NVIDIA Jetson TX2i 95 | * NVIDIA Jetson TX2 96 | * NVIDIA Jetson TX1 97 | * NVIDIA Jetson TK1 98 | * Nintendo Switch 99 | 100 | If you need a specific Compatibility open an `issue `_. 101 | 102 | 103 | License 104 | ------- 105 | 106 | jetson-stats is made available under the AGPL-3.0 license. For more details, see `LICENSE `_. 107 | 108 | 109 | Contributing 110 | ------------ 111 | 112 | We happily welcome contributions, please see :doc:`contributing` for details. 113 | -------------------------------------------------------------------------------- /docs/jtop/how_is_it_works.rst: -------------------------------------------------------------------------------- 1 | How is it works 2 | =============== 3 | 4 | jtop is a power monitor that uses a service and a Python client library. 5 | 6 | .. image:: /images/architecture.drawio.png 7 | :align: center 8 | 9 | Like the image above, when jtop service start, load and decode the information on your board. 10 | 11 | Initialization 12 | -------------- 13 | 14 | #. Read NVIDIA Jetson EEPROM to detect which NVIDIA Jetson is running 15 | #. decode jetson_clocks to know if is running and which engines are involved when it starts. 16 | #. decode the NVPmodel to know which model is selected 17 | #. Open the ``/run/jtop.sock`` socket and wait for a jtop client connection 18 | 19 | Loop 20 | ---- 21 | 22 | When jtop is running read all status from your current board and share all this data to all jtop python connections. 23 | 24 | #. Read and estimate the CPU utilization from ``/proc/stat`` 25 | #. Read status from all devices in ``/sys/devices/system`` 26 | #. Read and decode memory status from ``/proc/meminfo`` 27 | #. Decode and read the status from all swaps using ``swapon`` command 28 | #. Check status from jetson_clocks 29 | #. Check which nvpmodel is running 30 | 31 | jtop.sock 32 | --------- 33 | 34 | jtop uses a service to share the data between client (jtop gui or your Python script) and a server. 35 | 36 | This service, called ``jtop.service`` use a socket file. It is located in: 37 | 38 | .. code-block:: console 39 | :class: no-copybutton 40 | 41 | /run/jtop.sock 42 | 43 | This socket is protected by access mode: **660** equivalent to ``srw-rw----`` and by the group. 44 | 45 | Only other users in ``jtop`` **group** have access to this socket 46 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | REM Command file for Sphinx documentation 4 | 5 | if "%SPHINXBUILD%" == "" ( 6 | set SPHINXBUILD=sphinx-build 7 | ) 8 | set BUILDDIR=_build 9 | set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . 10 | if NOT "%PAPER%" == "" ( 11 | set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% 12 | ) 13 | 14 | if "%1" == "" goto help 15 | 16 | if "%1" == "help" ( 17 | :help 18 | echo.Please use `make ^` where ^ is one of 19 | echo. html to make standalone HTML files 20 | echo. dirhtml to make HTML files named index.html in directories 21 | echo. singlehtml to make a single large HTML file 22 | echo. pickle to make pickle files 23 | echo. json to make JSON files 24 | echo. htmlhelp to make HTML files and a HTML help project 25 | echo. qthelp to make HTML files and a qthelp project 26 | echo. devhelp to make HTML files and a Devhelp project 27 | echo. epub to make an epub 28 | echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter 29 | echo. text to make text files 30 | echo. man to make manual pages 31 | echo. changes to make an overview over all changed/added/deprecated items 32 | echo. linkcheck to check all external links for integrity 33 | echo. doctest to run all doctests embedded in the documentation if enabled 34 | goto end 35 | ) 36 | 37 | if "%1" == "clean" ( 38 | for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i 39 | del /q /s %BUILDDIR%\* 40 | goto end 41 | ) 42 | 43 | if "%1" == "html" ( 44 | %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html 45 | if errorlevel 1 exit /b 1 46 | echo. 47 | echo.Build finished. The HTML pages are in %BUILDDIR%/html. 48 | goto end 49 | ) 50 | 51 | if "%1" == "dirhtml" ( 52 | %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml 53 | if errorlevel 1 exit /b 1 54 | echo. 55 | echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. 56 | goto end 57 | ) 58 | 59 | if "%1" == "singlehtml" ( 60 | %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml 61 | if errorlevel 1 exit /b 1 62 | echo. 63 | echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. 64 | goto end 65 | ) 66 | 67 | if "%1" == "pickle" ( 68 | %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle 69 | if errorlevel 1 exit /b 1 70 | echo. 71 | echo.Build finished; now you can process the pickle files. 72 | goto end 73 | ) 74 | 75 | if "%1" == "json" ( 76 | %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json 77 | if errorlevel 1 exit /b 1 78 | echo. 79 | echo.Build finished; now you can process the JSON files. 80 | goto end 81 | ) 82 | 83 | if "%1" == "htmlhelp" ( 84 | %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp 85 | if errorlevel 1 exit /b 1 86 | echo. 87 | echo.Build finished; now you can run HTML Help Workshop with the ^ 88 | .hhp project file in %BUILDDIR%/htmlhelp. 89 | goto end 90 | ) 91 | 92 | if "%1" == "qthelp" ( 93 | %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp 94 | if errorlevel 1 exit /b 1 95 | echo. 96 | echo.Build finished; now you can run "qcollectiongenerator" with the ^ 97 | .qhcp project file in %BUILDDIR%/qthelp, like this: 98 | echo.^> qcollectiongenerator %BUILDDIR%\qthelp\urllib3.qhcp 99 | echo.To view the help file: 100 | echo.^> assistant -collectionFile %BUILDDIR%\qthelp\urllib3.ghc 101 | goto end 102 | ) 103 | 104 | if "%1" == "devhelp" ( 105 | %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp 106 | if errorlevel 1 exit /b 1 107 | echo. 108 | echo.Build finished. 109 | goto end 110 | ) 111 | 112 | if "%1" == "epub" ( 113 | %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub 114 | if errorlevel 1 exit /b 1 115 | echo. 116 | echo.Build finished. The epub file is in %BUILDDIR%/epub. 117 | goto end 118 | ) 119 | 120 | if "%1" == "latex" ( 121 | %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex 122 | if errorlevel 1 exit /b 1 123 | echo. 124 | echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. 125 | goto end 126 | ) 127 | 128 | if "%1" == "text" ( 129 | %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text 130 | if errorlevel 1 exit /b 1 131 | echo. 132 | echo.Build finished. The text files are in %BUILDDIR%/text. 133 | goto end 134 | ) 135 | 136 | if "%1" == "man" ( 137 | %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man 138 | if errorlevel 1 exit /b 1 139 | echo. 140 | echo.Build finished. The manual pages are in %BUILDDIR%/man. 141 | goto end 142 | ) 143 | 144 | if "%1" == "changes" ( 145 | %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes 146 | if errorlevel 1 exit /b 1 147 | echo. 148 | echo.The overview file is in %BUILDDIR%/changes. 149 | goto end 150 | ) 151 | 152 | if "%1" == "linkcheck" ( 153 | %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck 154 | if errorlevel 1 exit /b 1 155 | echo. 156 | echo.Link check complete; look for any errors in the above output ^ 157 | or in %BUILDDIR%/linkcheck/output.txt. 158 | goto end 159 | ) 160 | 161 | if "%1" == "doctest" ( 162 | %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest 163 | if errorlevel 1 exit /b 1 164 | echo. 165 | echo.Testing of doctests in the sources finished, look at the ^ 166 | results in %BUILDDIR%/doctest/output.txt. 167 | goto end 168 | ) 169 | 170 | :end -------------------------------------------------------------------------------- /docs/other-tools/environment_variables.rst: -------------------------------------------------------------------------------- 1 | Environment variables 2 | ===================== 3 | 4 | This script generate the easy environment variables to know which is your 5 | Hardware version of the Jetson and which Jetpack you have already installed. 6 | 7 | .. image:: /images/jetson_env.png 8 | :align: center 9 | 10 | You can read all variables with: 11 | 12 | .. code-block:: bash 13 | 14 | export | grep JETSON -------------------------------------------------------------------------------- /docs/other-tools/index.rst: -------------------------------------------------------------------------------- 1 | 🧰 Other tools 2 | ================ 3 | 4 | When you install jetson-stats there are also other tools included 5 | 6 | .. toctree:: 7 | 8 | jetson_config 9 | jetson_release 10 | jetson_swap 11 | environment_variables -------------------------------------------------------------------------------- /docs/other-tools/jetson_config.rst: -------------------------------------------------------------------------------- 1 | jetson_config 2 | ============= 3 | 4 | Check jetson-stats health, enable/disable desktop, enable/disable jetson_clocks, 5 | improve the performance of your wifi are available only in one click using jetson_config 6 | 7 | .. code-block:: bash 8 | 9 | sudo jetson_config 10 | 11 | .. image:: /images/jetson_config-01-main.png 12 | :align: center 13 | 14 | **jetson_config** have different pages 15 | 16 | - **Health** - Check the status of jetson-stats 17 | - **Update** - Update this tool to the latest version 18 | - **Desktop** - Enable/Disable boot from desktop 19 | - **About** - Information about this configuration tool 20 | 21 | Health 22 | ------ 23 | 24 | This page help to self check this package and automatically fix broken parts, there are these submenus: 25 | 26 | - **jetson-stats** - Fix jetson-stats service 27 | - **Permissions** - Fix permissions for your user 28 | - **variables** - Check if are installed all variables :doc:`environment_variables` 29 | 30 | .. image:: /images/jetson_config-02-jtop.png 31 | :align: center 32 | 33 | Desktop 34 | ------- 35 | 36 | This menu enable and disable the Desktop on your jetson. 37 | 38 | **Remember ssh require a login to work** 39 | 40 | .. image:: /images/jetson_config-03-desktop.png 41 | :align: center 42 | 43 | -------------------------------------------------------------------------------- /docs/other-tools/jetson_release.rst: -------------------------------------------------------------------------------- 1 | jetson_release 2 | ============== 3 | 4 | The command show the status and all information about your NVIDIA Jetson 5 | 6 | .. code-block:: bash 7 | 8 | jetson_release 9 | 10 | .. image:: /images/jetson_release.png 11 | :align: center 12 | 13 | Options available 14 | 15 | .. code-block:: console 16 | :class: no-copybutton 17 | 18 | nvidia@jetson-nano:~$ jetson_release --help 19 | Software part of jetson-stats 4.2.0 - (c) 2023, Raffaello Bonghi 20 | usage: jetson_release [-h] [-v] [-s] 21 | 22 | Show detailed information about this board. Machine, Jetpack, libraries and 23 | other 24 | 25 | optional arguments: 26 | -h, --help show this help message and exit 27 | -v, --verbose Show all variables (default: False) 28 | -s, --serial Show serial number (default: False) 29 | -------------------------------------------------------------------------------- /docs/other-tools/jetson_swap.rst: -------------------------------------------------------------------------------- 1 | jetson_swap 2 | =========== 3 | 4 | Simple manager to switch on and switch off a swapfile in your jetson. 5 | 6 | .. code-block:: bash 7 | 8 | jetson_swap 9 | 10 | All options available 11 | 12 | .. code-block:: console 13 | :class: no-copybutton 14 | 15 | nvidia@jetson-nano:~$ sudo jetson_swap -h 16 | usage: jetson_swap [-h] [-d DIRECTORY] [-n NAME] [-s SIZE] [-a] [-t] [--off] 17 | 18 | Create a swap file and enable on boot (require sudo) 19 | 20 | optional arguments: 21 | -h, --help show this help message and exit 22 | -d DIRECTORY, --dir DIRECTORY 23 | Directory to place swapfile (default: ) 24 | -n NAME, --name NAME Name swap file (default: swapfile) 25 | -s SIZE, --size SIZE Size in Gigabytes (default: 8) 26 | -a, --auto Enable swap on boot (default: False) 27 | -t, --status Check if the swap is currently active (default: False) 28 | --off Switch off the swap (default: False) 29 | -------------------------------------------------------------------------------- /docs/reference/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ========== 3 | 4 | .. autoclass:: jtop.JtopException 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/reference/fan.rst: -------------------------------------------------------------------------------- 1 | Fan 2 | === 3 | 4 | .. autoclass:: jtop.core.fan.Fan 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/reference/gpu.rst: -------------------------------------------------------------------------------- 1 | GPU 2 | === 3 | 4 | .. autoclass:: jtop.core.gpu.GPU 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/reference/index.rst: -------------------------------------------------------------------------------- 1 | 📖 API Reference 2 | ================= 3 | 4 | .. toctree:: 5 | jtop 6 | gpu 7 | memory 8 | fan 9 | jetson_clocks 10 | nvpmodel 11 | exceptions -------------------------------------------------------------------------------- /docs/reference/jetson_clocks.rst: -------------------------------------------------------------------------------- 1 | jetson_clocks 2 | ============= 3 | 4 | .. autoclass:: jtop.core.jetson_clocks.JetsonClocks 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/reference/jtop.rst: -------------------------------------------------------------------------------- 1 | jtop 2 | ==== 3 | 4 | .. autoclass:: jtop.jtop 5 | :members: 6 | :show-inheritance: 7 | 8 | .. automethod:: __init__ 9 | 10 | -------------------------------------------------------------------------------- /docs/reference/memory.rst: -------------------------------------------------------------------------------- 1 | Memory 2 | ====== 3 | 4 | .. autoclass:: jtop.core.memory.Memory 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/reference/nvpmodel.rst: -------------------------------------------------------------------------------- 1 | NVPModel 2 | ======== 3 | 4 | .. autoclass:: jtop.core.nvpmodel.NVPModel 5 | :members: 6 | :show-inheritance: -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx>3.0.0 2 | requests 3 | furo 4 | sphinx-copybutton 5 | sphinx-autobuild 6 | sphinxext-opengraph 7 | numpy>=1.22.2 # not directly required, pinned by Snyk to avoid a vulnerability 8 | tornado>=6.4.2 # not directly required, pinned by Snyk to avoid a vulnerability 9 | pillow>=10.3.0 # not directly required, pinned by Snyk to avoid a vulnerability 10 | fonttools>=4.43.0 # not directly required, pinned by Snyk to avoid a vulnerability 11 | zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability 12 | -------------------------------------------------------------------------------- /docs/sponsors.rst: -------------------------------------------------------------------------------- 1 | 💖 Sponsor 2 | ========== 3 | 4 | Please consider sponsoring jetson-stats development, especially if your company benefits from this library. 5 | 6 | Your contribution will go towards adding new features to jetson-stats and making sure all functionality continues to meet our high quality standards. 7 | 8 | .. important:: 9 | 10 | `Get in contact `_ for additional 11 | details on sponsorship and perks before making a contribution 12 | through `GitHub Sponsors `_ if you have questions. 13 | -------------------------------------------------------------------------------- /docs/troubleshooting.rst: -------------------------------------------------------------------------------- 1 | 🆘 Troubleshooting 2 | ================== 3 | 4 | Let's resolve here the common issues can happening using jetson-stats (jtop). 5 | 6 | Before to start, have you updated jetson-stats to the latest release? 7 | 8 | if not, write: 9 | 10 | .. code-block:: bash 11 | 12 | sudo pip3 install -U jetson-stats 13 | 14 | If nothing changed follow the help below. 15 | 16 | pip3: command not found 17 | ^^^^^^^^^^^^^^^^^^^^^^^ 18 | 19 | When you try to install jetson-stats, you read an output like: 20 | 21 | .. code-block:: console 22 | :class: no-copybutton 23 | 24 | user@board:~$ sudo pip3 install -U jetson-stats 25 | sudo: pip3: command not found 26 | 27 | you can simple install python3-pip 28 | 29 | .. code-block:: bash 30 | 31 | sudo apt install python3-pip 32 | 33 | jtop.service inactive 34 | ^^^^^^^^^^^^^^^^^^^^^ 35 | 36 | When the *jtop.service* (or previously *jetson-stats.service*) is inactive there are few different reasons 37 | 38 | First step, restart jtop service following the command below 39 | 40 | .. code-block:: bash 41 | 42 | sudo systemctl restart jtop.service 43 | 44 | If the error is not fixed run: 45 | 46 | .. code-block:: bash 47 | 48 | journalctl -u jtop.service -n 100 --no-pager 49 | 50 | If you read here an error, open an `issue `_ reporting the output. 51 | 52 | jtop start only with sudo 53 | ^^^^^^^^^^^^^^^^^^^^^^^^^ 54 | 55 | If jtop start only with ``sudo jtop`` and when you run without sudo you read this error: 56 | 57 | .. code-block:: console 58 | :class: no-copybutton 59 | 60 | user@board:~$ jtop 61 | I can't access jtop.service. 62 | Please logout or reboot this board. 63 | 64 | The reason can be your user is not allowed to have access to jtop. 65 | 66 | There are two way to fix it: 67 | 68 | 1. Run the command below and check if is all **OK** if you read **FAIL** on fix permissions, press **Fix all** and logout/login. 69 | 70 | .. code-block:: bash 71 | 72 | sudo jtop --health 73 | 74 | .. image:: images/jtop-fail-user.png 75 | :align: center 76 | 77 | 1. You can manually do writing the command below 78 | 79 | .. code-block:: bash 80 | 81 | sudo usermod -a -G jtop $USER 82 | 83 | remember to logout/login. 84 | 85 | Bad visualization on Putty 86 | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 87 | 88 | If you experiences a bad visualization working with jtop on Putty, like a sequence of "qqqqqwqqqq and xxxx" you can fix following the steps below: 89 | 90 | 1. Window -> Translation 91 | 2. Enable VT100 line drawing even in UTF-8 mode 92 | 93 | Nothing fix my error 94 | ^^^^^^^^^^^^^^^^^^^^ 95 | 96 | Before to open an `issue`_, try to reinstall the latest version with this command 97 | 98 | .. code-block:: bash 99 | 100 | sudo pip3 install --no-cache-dir -v -U jetson-stats 101 | 102 | Save the output somewhere, if this command doesn't fix can be helpful when you one an `issue`_. 103 | 104 | Run this command and save the output. This output help me to understand the reason of this error. 105 | 106 | .. code-block:: bash 107 | 108 | journalctl -u jtop.service -n 100 --no-pager 109 | 110 | Remember also to add other information about your board 111 | 112 | You can find on: 113 | 114 | .. code-block:: bash 115 | 116 | jetson_release -v 117 | 118 | - jetson-stats version: [e.g. 1.8] 119 | 120 | - P-Number: [e.g. pXXXX-XXXX] 121 | - Module: [e.g. NVIDIA Jetson XXX] 122 | 123 | - Jetpack: [e.g. 4.3] 124 | - L4T: [e.g. 5.2.1] 125 | -------------------------------------------------------------------------------- /examples/jtop_callback.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop, JtopException 20 | 21 | 22 | def read_stats(jetson): 23 | """ 24 | This is your callback function where you can read all files when are available. 25 | """ 26 | print(jetson.stats) 27 | 28 | 29 | if __name__ == "__main__": 30 | print("Initialize jtop callback") 31 | # Open the jtop 32 | jetson = jtop() 33 | # Attach a function where you can read the status of your jetson 34 | jetson.attach(read_stats) 35 | 36 | # This try excpet will catch jtop exception 37 | try: 38 | # This loop will manage the jtop status all the time 39 | # This is a blocking a function, if you do not want use you can use as well 40 | # start: jetson.start() 41 | # stop: jetson.stop() 42 | jetson.loop_for_ever() 43 | except JtopException as e: 44 | print(e) 45 | # EOF 46 | -------------------------------------------------------------------------------- /examples/jtop_control.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop, JtopException 20 | 21 | if __name__ == "__main__": 22 | 23 | print("Simple jtop controller") 24 | 25 | try: 26 | # All options are not blocking 27 | with jtop() as jetson: 28 | # Read jetson_clocks status 29 | print(jetson.jetson_clocks) 30 | # Set a new status 31 | jetson.jetson_clocks = True 32 | # Read nvpmodel 33 | if jetson.nvpmodel: 34 | # Read nvpmodel 35 | print(jetson.nvpmodel) 36 | # Set new state 37 | jetson.nvpmodel = 0 # You can write the name of the model as well 38 | # Wait nvpmodel changed 39 | while jetson.ok(): 40 | # deepcode ignore AttributeLoadOnPrimitive: nvpmodel is an object with different attribute. See documentation 41 | if jetson.nvpmodel.id == 0: 42 | break 43 | # You can increase or decrease the nvpmodel using 44 | jetson.nvpmodel += 1 # or jetson.nvpmodel = jetson.nvpmodel + 1 45 | # Wait nvpmodel changed 46 | while jetson.ok(): 47 | # deepcode ignore AttributeLoadOnPrimitive: nvpmodel is an object with different attribute. See documentation 48 | if jetson.nvpmodel.id == 1: 49 | break 50 | # You can control the fan 51 | if jetson.fan: 52 | # read fan status 53 | print(jetson.fan) 54 | # You can change mode and setting 55 | jetson.fan.mode = 'system' 56 | # Wait nvpmodel changed 57 | while jetson.ok(): 58 | if jetson.fan.mode == 'system': 59 | break 60 | # Or you can change the fan speed 61 | jetson.fan.speed = 100 62 | while jetson.ok(): 63 | # Print jetson fan status 64 | print(jetson.fan) 65 | # Leave when fan measure is at 100% 66 | if jetson.fan.speed == 100: 67 | break 68 | except JtopException as e: 69 | print(e) 70 | # EOF 71 | -------------------------------------------------------------------------------- /examples/jtop_cpu.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop cpu reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | if jetson.ok(): 29 | # Print all cpu 30 | for idx, cpu in enumerate(jetson.cpu['cpu']): 31 | print("------ CPU{idx} ------".format(idx=idx)) 32 | for key, value in cpu.items(): 33 | print("{key}: {value}".format(key=key, value=value)) 34 | # read aggregate CPU status 35 | total = jetson.cpu['total'] 36 | print("------ TOTAL ------") 37 | for key, value in total.items(): 38 | print("{key}: {value}".format(key=key, value=value)) 39 | # EOF 40 | -------------------------------------------------------------------------------- /examples/jtop_engines.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop engine reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | while jetson.ok(): 29 | # Read engines list 30 | engines = jetson.engine 31 | # Print all engines 32 | for engine_name in engines: 33 | engine = engines[engine_name] 34 | print("{engine_name} = {engine}".format(engine_name=engine_name, engine=engine)) 35 | # EOF 36 | -------------------------------------------------------------------------------- /examples/jtop_fan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop fan reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | while jetson.ok(): 29 | print("Print fan status") 30 | print(jetson.fan) 31 | # Print for each fan all info 32 | for name in jetson.fan: 33 | speed = jetson.fan.get_speed(name) 34 | profile = jetson.fan.get_profile(name) 35 | print("{name} profile={profile} speed={speed}".format(name=name, profile=profile, speed=speed)) 36 | # EOF 37 | -------------------------------------------------------------------------------- /examples/jtop_flask_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from flask import Flask, Response 20 | from jtop import jtop 21 | import json 22 | 23 | 24 | class WebService: 25 | 26 | def __init__(self): 27 | # Load Service 28 | self._app = Flask(__name__) 29 | # Register update function 30 | self._app.add_url_rule('/status', 'status', self.status) 31 | # Initialize jtop 32 | self._jetson = jtop() 33 | # start service 34 | self.start() 35 | 36 | def status(self): 37 | response = {} 38 | # Get uptime 39 | response['uptime'] = str(self._jetson.uptime) 40 | # Spin jtop 41 | self._jetson.ok(spin=True) 42 | # return data 43 | return Response( 44 | response=json.dumps(response), 45 | status=201, 46 | mimetype="application/json" 47 | ) 48 | 49 | def start(self): 50 | print("Init server ...") 51 | # Start jtop 52 | self._jetson.start() 53 | # Start server 54 | self._app.run(debug=False) 55 | 56 | def stop(self): 57 | print("switch off server") 58 | # Stop jtop 59 | self._jetson.close() 60 | # Stop server 61 | 62 | 63 | if __name__ == "__main__": 64 | # Initialize service 65 | service = WebService() 66 | service.stop() 67 | # EOF 68 | -------------------------------------------------------------------------------- /examples/jtop_hardware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop hardware reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | if jetson.ok(): 29 | # Read hardware, platform and libraries list 30 | # Print all values 31 | for name_category, category in jetson.board.items(): 32 | print("{name}:".format(name=name_category)) 33 | # Print all category 34 | for name, value in category.items(): 35 | print(" - {name}: {value}".format(name=name, value=value)) 36 | # EOF 37 | -------------------------------------------------------------------------------- /examples/jtop_logger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop, JtopException 20 | import csv 21 | import argparse 22 | 23 | 24 | if __name__ == "__main__": 25 | parser = argparse.ArgumentParser(description='Simple jtop logger') 26 | # Standard file to store the logs 27 | parser.add_argument('--file', action="store", dest="file", default="log.csv") 28 | args = parser.parse_args() 29 | 30 | print("Simple jtop logger") 31 | print("Saving log on {file}".format(file=args.file)) 32 | 33 | try: 34 | with jtop() as jetson: 35 | # Make csv file and setup csv 36 | with open(args.file, 'w') as csvfile: 37 | stats = jetson.stats 38 | # Initialize cws writer 39 | writer = csv.DictWriter(csvfile, fieldnames=stats.keys()) 40 | # Write header 41 | writer.writeheader() 42 | # Write first row 43 | writer.writerow(stats) 44 | # Start loop 45 | while jetson.ok(): 46 | stats = jetson.stats 47 | # Write row 48 | writer.writerow(stats) 49 | print("Log at {time}".format(time=stats['time'])) 50 | except JtopException as e: 51 | print(e) 52 | except KeyboardInterrupt: 53 | print("Closed with CTRL-C") 54 | except IOError: 55 | print("I/O error") 56 | # EOF 57 | -------------------------------------------------------------------------------- /examples/jtop_memory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop memory reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | if jetson.ok(): 29 | # Print all cpu 30 | for name, data in jetson.memory.items(): 31 | print("------ {name} ------".format(name=name)) 32 | print(data) 33 | # EOF 34 | -------------------------------------------------------------------------------- /examples/jtop_processes.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop process reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | while jetson.ok(): 29 | # Print all cpu 30 | for process in jetson.processes: 31 | print(process) 32 | # EOF 33 | -------------------------------------------------------------------------------- /examples/jtop_properties.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("All accessible jtop properties") 25 | 26 | with jtop() as jetson: 27 | # boards 28 | print('*** board ***') 29 | print(jetson.board) 30 | # jetson.ok() will provide the proper update frequency 31 | while jetson.ok(): 32 | # CPU 33 | print('*** CPUs ***') 34 | print(jetson.cpu) 35 | # CPU 36 | print('*** Memory ***') 37 | print(jetson.memory) 38 | # GPU 39 | print('*** GPU ***') 40 | print(jetson.gpu) 41 | # Engines 42 | print('*** engine ***') 43 | print(jetson.engine) 44 | # nvpmodel 45 | print('*** NV Power Model ***') 46 | print(jetson.nvpmodel) 47 | # jetson_clocks 48 | print('*** jetson_clocks ***') 49 | print(jetson.jetson_clocks) 50 | # Status disk 51 | print('*** disk ***') 52 | print(jetson.disk) 53 | # Status fans 54 | print('*** fan ***') 55 | print(jetson.fan) 56 | # uptime 57 | print('*** uptime ***') 58 | print(jetson.uptime) 59 | # local interfaces 60 | print('*** local interfaces ***') 61 | print(jetson.local_interfaces) 62 | # Temperature 63 | print('*** temperature ***') 64 | print(jetson.temperature) 65 | # Power 66 | print('*** power ***') 67 | print(jetson.power) 68 | # EOF 69 | -------------------------------------------------------------------------------- /examples/jtop_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | 20 | from jtop import jtop, JtopException 21 | import socket 22 | import json 23 | import argparse 24 | 25 | parser = argparse.ArgumentParser(description='Simple jtop server.') 26 | 27 | # Standard loopback interface address (localhost) 28 | parser.add_argument('--host', action="store", dest="host", default="127.0.0.1") 29 | 30 | # Port to listen on (non-privileged ports are > 1023) 31 | parser.add_argument('--port', action="store", dest="port", type=int, default=65432) 32 | 33 | # Optional argument to return message in a valid HTTP response 34 | parser.add_argument('--http', action="store_true") 35 | 36 | args = parser.parse_args() 37 | 38 | if __name__ == "__main__": 39 | 40 | print("Simple jtop server") 41 | 42 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 43 | sock.bind((args.host, args.port)) 44 | print("Open server jtop to {}:{}".format(args.host, args.port)) 45 | sock.listen(1) 46 | 47 | try: 48 | with jtop() as jetson: 49 | while jetson.ok(): 50 | # Wait socket request 51 | conn, addr = sock.accept() 52 | print("Connected to {}".format(conn)) 53 | # Read and convert in JSON the jetson stats 54 | stats = json.dumps(jetson.stats) 55 | # Send by socket 56 | if args.http: 57 | message = "HTTP/1.1 200 OK\r\nHost: {host}:{port}\r\nContent-Type: application/json\r\nContent-Length: {length}\r\n\r\n{stats}" 58 | conn.send(message.format(host=args.host, port=args.port, length=len(stats), stats=stats.encode())) 59 | else: 60 | conn.send(stats.encode()) 61 | # Close connection 62 | conn.close() 63 | except JtopException as e: 64 | print(e) 65 | except Exception as e: 66 | print(e) 67 | finally: 68 | sock.close() 69 | # EOF 70 | -------------------------------------------------------------------------------- /examples/quick_read.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 4 | # Copyright (c) 2019-2023 Raffaello Bonghi. 5 | # 6 | # This program is free software: you can redistribute it and/or modify 7 | # it under the terms of the GNU Affero General Public License as published by 8 | # the Free Software Foundation, either version 3 of the License, or 9 | # (at your option) any later version. 10 | # 11 | # This program is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU Affero General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU Affero General Public License 17 | # along with this program. If not, see . 18 | 19 | from jtop import jtop 20 | 21 | 22 | if __name__ == "__main__": 23 | 24 | print("Simple jtop reader") 25 | 26 | with jtop() as jetson: 27 | # jetson.ok() will provide the proper update frequency 28 | while jetson.ok(): 29 | # Read tegra stats 30 | print(jetson.stats) 31 | # EOF 32 | -------------------------------------------------------------------------------- /jtop/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # flake8: noqa 19 | 20 | from .core.exceptions import JtopException 21 | from .core.gpu import GPU 22 | from .core.memory import Memory 23 | from .core.fan import Fan 24 | from .core.jetson_clocks import JetsonClocks 25 | from .core.nvpmodel import NVPModel 26 | from .jtop import jtop 27 | 28 | __author__ = "Raffaello Bonghi" 29 | __email__ = "raffaello@rnext.it" 30 | __cr__ = "(c) 2024, RB" 31 | __copyright__ = "(c) 2024, Raffaello Bonghi" 32 | # Version package 33 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#choosing-a-versioning-scheme 34 | __version__ = "4.3.2" 35 | # EOF 36 | -------------------------------------------------------------------------------- /jtop/core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # flake8: noqa 19 | 20 | # EOF 21 | -------------------------------------------------------------------------------- /jtop/core/command.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import os 19 | import sys 20 | import logging 21 | import threading 22 | # Launch command 23 | import subprocess as sp 24 | # Load queue library for python 2 and python 3 25 | try: 26 | import queue 27 | except ImportError: 28 | import Queue as queue # pyright: ignore[reportMissingImports] 29 | # Create logger 30 | logger = logging.getLogger(__name__) 31 | EXTRA_TIMEOUT = 1.0 32 | # Reference: 33 | # https://eli.thegreenplace.net/2017/interacting-with-a-long-running-child-process-in-python/ 34 | # https://stackoverflow.com/questions/37942022/returncode-of-popen-object-is-none-after-the-process-is-terminated/42376107 35 | # https://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python 36 | # https://docs.python.org/3/tutorial/errors.html 37 | # https://stackoverflow.com/questions/10756383/timeout-on-subprocess-readline-in-python 38 | # https://stackoverflow.com/questions/3733270/python-subprocess-timeout 39 | 40 | 41 | class Command(object): 42 | 43 | class CommandException(Exception): 44 | 45 | def __init__(self, message, errno): 46 | self.message = message 47 | self.errno = errno 48 | 49 | def __str__(self): 50 | return "[errno:{errno}] {message}".format(message=self.message, errno=self.errno) 51 | 52 | class TimeoutException(CommandException): 53 | 54 | def __init__(self): 55 | super(Command.TimeoutException, self).__init__("Process does not replied in time", -1) 56 | 57 | @staticmethod 58 | def run_command(command, repeat=5, timeout=2, input=None): 59 | cmd = Command(command, input=input) 60 | for idx in range(repeat): 61 | try: 62 | return cmd(timeout=timeout) 63 | except Command.TimeoutException as error: 64 | logger.error("[{idx}] {error}".format(idx=idx, error=error)) 65 | raise Command.CommandException("Error to start {command}".format(command=command), -2) 66 | 67 | def __init__(self, command, input=None): 68 | self.process = None 69 | self.command = command 70 | self.input = input 71 | 72 | def __call__(self, timeout=None): 73 | def target(out_queue, err_queue): 74 | # Run process 75 | try: 76 | # https://stackoverflow.com/questions/33277452/prevent-unexpected-stdin-reads-and-lock-in-subprocess 77 | if self.input is not None: 78 | self.process = sp.Popen(self.command, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE, preexec_fn=os.setsid) 79 | # Write input 80 | stdout, _ = self.process.communicate(bytes(self.input, 'utf-8')) 81 | self.process.stdin.close() 82 | self.process.wait() 83 | # Read lines output 84 | for line in stdout.splitlines(): 85 | try: 86 | line = line.decode('utf-8') 87 | line = str(line.strip()) 88 | except UnicodeEncodeError: 89 | line = line.encode('ascii', 'ignore').decode('ascii') 90 | out_queue.put(line) 91 | else: 92 | self.process = sp.Popen(self.command, stdout=sp.PIPE, stderr=sp.PIPE, stdin=open(os.devnull), preexec_fn=os.setsid) 93 | # Read lines output 94 | for line in iter(self.process.stdout.readline, b''): 95 | try: 96 | line = line.decode('utf-8') 97 | line = str(line.strip()) 98 | except UnicodeEncodeError: 99 | line = line.encode('ascii', 'ignore').decode('ascii') 100 | out_queue.put(line) 101 | # Close and terminate 102 | self.process.stdout.close() 103 | self.process.wait() 104 | except Exception: 105 | # Store error message 106 | err_queue.put(sys.exc_info()) 107 | # Initialize lists 108 | is_timeout = False 109 | out_queue = queue.Queue() 110 | err_queue = queue.Queue() 111 | thread = threading.Thread(target=target, args=(out_queue, err_queue, )) 112 | thread.start() 113 | # Wait timeout process 114 | thread.join(timeout) 115 | if thread.is_alive(): 116 | logger.error('Terminating process: {command}'.format(command=self.command)) 117 | if self.process is not None: 118 | self.process.terminate() 119 | thread.join(timeout=EXTRA_TIMEOUT) 120 | logger.warning('Process terminated: {command}'.format(command=self.command)) 121 | is_timeout = True 122 | # Read the output 123 | # Extract exception and raise 124 | if not err_queue.empty(): 125 | ex_type, ex_value, tb_str = err_queue.get() 126 | ex_value.__traceback__ = tb_str 127 | raise ex_value 128 | if is_timeout: 129 | raise Command.TimeoutException() 130 | if self.process.returncode != 0: 131 | raise Command.CommandException('Error process:', self.process.returncode) 132 | return list(out_queue.queue) 133 | 134 | def communicate(self, timeout=None): 135 | self.__call__(timeout=timeout) 136 | # EOF 137 | -------------------------------------------------------------------------------- /jtop/core/config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import logging 19 | import os 20 | import json 21 | import sys 22 | import copy 23 | # Create logger 24 | logger = logging.getLogger(__name__) 25 | JTOP_DATA_FOLDER = 'local/jtop' 26 | 27 | 28 | def make_config_service(data_folder=JTOP_DATA_FOLDER): 29 | path = get_config_service(data_folder) 30 | if not os.path.isdir(path): 31 | logger.info("Build service folder in {path}".format(path=path)) 32 | # Make folder directory 33 | os.makedirs(path) 34 | 35 | 36 | def get_config_service(data_folder=JTOP_DATA_FOLDER): 37 | path = sys.prefix 38 | if hasattr(sys, 'real_prefix'): 39 | path = sys.real_prefix 40 | if hasattr(sys, 'base_prefix'): 41 | path = sys.base_prefix 42 | # Return directory folder 43 | return "{path}/{data_folder}".format(path=path, data_folder=data_folder) 44 | 45 | 46 | class Config: 47 | 48 | def __init__(self): 49 | # Build folder if doesn't exists 50 | make_config_service() 51 | # Load configuration path 52 | self.config_file = self.path + '/config.json' 53 | # Load configuration 54 | self._config = self._load() 55 | self._last_config = copy.deepcopy(self._config) 56 | 57 | def set(self, instance, default=None): 58 | # Update configuration 59 | self._config[instance] = default 60 | # Store configuration 61 | if self._last_config != self._config: 62 | self._store() 63 | # Update last configuration 64 | self._last_config = copy.deepcopy(self._config) 65 | 66 | def get(self, instance, default=None): 67 | return self._config.get(instance, default) 68 | 69 | @property 70 | def path(self): 71 | return get_config_service() 72 | 73 | def _load(self): 74 | config = {} 75 | # Load configuration if exist 76 | if not os.path.isfile(self.config_file): 77 | return config 78 | logger.info("Load config from {path}".format(path=self.config_file)) 79 | with open(self.config_file) as json_file: 80 | config = json.load(json_file) 81 | return config 82 | 83 | def _store(self): 84 | logger.info("Store config to {path}".format(path=self.config_file)) 85 | # Write configuration 86 | with open(self.config_file, 'w') as outfile: 87 | json.dump(self._config, outfile, sort_keys=True, indent=4) 88 | 89 | def clear(self): 90 | self._config = {} 91 | self._last_config = {} 92 | if os.path.isfile(self.config_file): 93 | logger.info("Clear config in {path}".format(path=self.config_file)) 94 | # Remove configuration file 95 | os.remove(self.config_file) 96 | return True 97 | return False 98 | 99 | def items(self): 100 | return self._config.items() 101 | 102 | def keys(self): 103 | return self._config.keys() 104 | 105 | def values(self): 106 | return self._config.values() 107 | 108 | def __contains__(self, key): 109 | return key in self._config 110 | 111 | def __repr__(self): 112 | return repr(self._config) 113 | 114 | def __str__(self): 115 | return str(self._config) 116 | # EOF 117 | -------------------------------------------------------------------------------- /jtop/core/engine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import os 19 | # Logging 20 | import logging 21 | # from .exceptions import JtopException 22 | # Create logger 23 | logger = logging.getLogger(__name__) 24 | 25 | 26 | def read_engine(path): 27 | # Read status online 28 | engine = {} 29 | # Check if access to this file 30 | if os.access(path + "/clk_enable_count", os.R_OK): 31 | with open(path + "/clk_enable_count", 'r') as f: 32 | # Write online engine 33 | engine['online'] = int(f.read()) == 1 34 | # Check if access to this file 35 | if os.access(path + "/clk_rate", os.R_OK): 36 | with open(path + "/clk_rate", 'r') as f: 37 | # Write current engine 38 | engine['cur'] = int(f.read()) // 1000 39 | # Decode clock rate 40 | max_value = False 41 | if os.access(path + "/clk_max_rate", os.R_OK): 42 | with open(path + "/clk_max_rate", 'r') as f: 43 | # Write status engine 44 | value = int(f.read()) 45 | # 18446744073709551615 = FFFF FFFF FFFF FFFF = 2 ^ 16 46 | if value != 18446744073709551615: 47 | engine['max'] = value // 1000 48 | max_value = True 49 | if os.access(path + "/clk_min_rate", os.R_OK) and max_value: 50 | with open(path + "/clk_min_rate", 'r') as f: 51 | # Write status engine 52 | engine['min'] = int(f.read()) // 1000 53 | return engine 54 | 55 | 56 | class EngineService(object): 57 | 58 | ENGINES = ['ape', 'dla', 'pva', 'vic', 'nvjpg', 'nvenc', 'nvdec', 'se.', 'cvnas', 'msenc', 'ofa'] 59 | 60 | def __init__(self): 61 | # Sort list before start 62 | EngineService.ENGINES.sort() 63 | self.engines_path = {} 64 | # List all engines available 65 | engine_path = "/sys/kernel/debug/clk" 66 | if os.getenv('JTOP_TESTING', False): 67 | engine_path = "/fake_sys/kernel/debug/clk" 68 | logger.warning("Running in JTOP_TESTING folder={root_dir}".format(root_dir=engine_path)) 69 | list_all_engines = [x[0] for x in os.walk(engine_path)] 70 | # Search all available engines 71 | for name in EngineService.ENGINES: 72 | if name.endswith('.'): 73 | name = name[:-1] 74 | local_path = "{path}/{name}".format(path=engine_path, name=name) 75 | if os.path.isdir(local_path): 76 | self.engines_path[name.upper()] = [local_path] 77 | else: 78 | # https://stackoverflow.com/questions/4843158/how-to-check-if-a-string-is-a-substring-of-items-in-a-list-of-strings 79 | local_path = "{path}/{name}".format(path=engine_path, name=name) 80 | # In this search are removed all engines that have a '.' on their name 81 | # like ape.buffer or nvdec.buf 82 | matching = [s for s in list_all_engines if local_path in s and '.' not in s] 83 | # Add in list all engines 84 | if matching: 85 | # Check if name end with a number, if true collect by number 86 | # dla0 dla1 ... 87 | if os.path.basename(matching[0]).split('_')[0] == "{name}0".format(name=name): 88 | logger.info("Special Engine group found: [{name}X]".format(name=name)) 89 | for num in range(10): 90 | name_engine = "{name}{counter}".format(name=name, counter=num) 91 | new_match = [match for match in matching if name_engine in match] 92 | if new_match: 93 | self.engines_path[name_engine.upper()] = sorted(new_match) 94 | else: 95 | break 96 | else: 97 | self.engines_path[name.upper()] = sorted(matching) 98 | # Print all engines found 99 | if self.engines_path: 100 | engines_string = ' '.join(name for name in self.engines_path) 101 | logger.info("Engines found: [{engines}]".format(engines=engines_string)) 102 | else: 103 | logger.warn("Not engines found!") 104 | 105 | def get_status(self): 106 | status = {} 107 | # Read status from all engines 108 | for engine in self.engines_path: 109 | status[engine] = {} 110 | for local_path in self.engines_path[engine]: 111 | name_engine = os.path.basename(local_path).upper() 112 | logger.debug("Status [{engine}] in {path}".format(engine=name_engine, path=local_path)) 113 | status[engine][name_engine] = read_engine(local_path) 114 | return status 115 | # EOF 116 | -------------------------------------------------------------------------------- /jtop/core/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | class JtopException(Exception): 20 | """ 21 | raise when jtop fail. The message attached show the reason. 22 | """ 23 | 24 | def __init__(self, message, errors=""): 25 | super(JtopException, self).__init__(message, errors) 26 | # Now for your custom code... 27 | self.message = message 28 | self.errors = errors 29 | 30 | def __repr__(self): 31 | return str(self.message) 32 | 33 | def __str__(self): 34 | return str(self.message) 35 | # EOF 36 | -------------------------------------------------------------------------------- /jtop/core/hardware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | from .jetson_variables import get_jetson_variables 19 | from .common import cat 20 | import os 21 | import logging 22 | import platform 23 | # Load distro library from python3 or use platform 24 | try: 25 | import distro 26 | except ImportError: 27 | distro = platform 28 | # Create logger 29 | logger = logging.getLogger(__name__) 30 | 31 | 32 | def get_parameter(path): 33 | if os.path.isfile(path): 34 | return cat(path).strip() 35 | 36 | 37 | def get_platform_variables(): 38 | return { 39 | 'Machine': platform.machine(), 40 | 'System': platform.system(), 41 | 'Distribution': " ".join(distro.linux_distribution()), 42 | 'Release': platform.release(), 43 | 'Python': platform.python_version(), 44 | } 45 | 46 | 47 | def get_x86_64_variables(): 48 | hardware = {} 49 | hardware_path = "/sys/devices/virtual/dmi/id/" 50 | items = os.listdir(hardware_path) 51 | for item in sorted(items): 52 | if item in ['uevent', 'modalias', 'board_serial', 'bios_release', 'product_uuid', 'chassis_type']: 53 | continue 54 | path = os.path.join(hardware_path, item) 55 | output = "" 56 | if os.path.isfile(path): 57 | output = cat(path).strip() 58 | if not output or output == 'Default string': 59 | continue 60 | name = item.replace("_", " ").capitalize() 61 | hardware[name] = output 62 | return hardware 63 | 64 | 65 | def get_hardware(): 66 | # If hardware is ARM check if NVIDIA Jetson 67 | platform_board = platform.machine() 68 | logger.info("Hardware detected {}".format(platform_board)) 69 | if platform_board == 'aarch64': 70 | # Load Jetson data 71 | jetson = get_jetson_variables() 72 | # Print main jetson variables 73 | if '699-level Part Number' in jetson: 74 | logger.info("NVIDIA Jetson 699-level Part Number={}".format(jetson['699-level Part Number'])) 75 | else: 76 | logger.error("NVIDIA Jetson No 699-level Part Number detected!") 77 | if 'Module' in jetson: 78 | logger.info("NVIDIA Jetson Module={}".format(jetson['Module'])) 79 | else: 80 | logger.error("NVIDIA Jetson No Module detected!") 81 | # Check L4T detection 82 | if jetson['L4T']: 83 | logger.info("NVIDIA Jetson detected L4T={}".format(jetson['L4T'])) 84 | else: 85 | logger.error("NVIDIA Jetson L4T not detected!") 86 | return jetson 87 | elif platform_board == 'x86_64': 88 | return get_x86_64_variables() 89 | else: 90 | logger.warning("Unrecognized board {}".format(platform_board)) 91 | return {} 92 | # EOF 93 | -------------------------------------------------------------------------------- /jtop/core/jetson_libraries.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import os 19 | import re 20 | import subprocess 21 | from .common import cat 22 | from .command import Command 23 | # Fix connection refused for python 2.7 24 | try: 25 | FileNotFoundError 26 | except NameError: 27 | FileNotFoundError = IOError 28 | 29 | 30 | MODULES = ['cuDNN', 'TensorRT.', 'VPI'] # 'Visionworks' 31 | CUDA_FILE_RE = re.compile(r'CUDA Version (.*)') 32 | CUDA_NVCC_RE = re.compile(r'V([0-9]+.[0-9]+.[0-9]+)') 33 | 34 | 35 | def get_cuda(): 36 | cuda_version = '' 37 | if os.path.isfile("/usr/local/cuda/version.txt"): 38 | version = cat("/usr/local/cuda/version.txt") 39 | match = re.search(CUDA_FILE_RE, version) 40 | if match: 41 | cuda_version = match.group(1) 42 | elif os.path.isfile("/usr/local/cuda/bin/nvcc"): 43 | cmd = Command(['/usr/local/cuda/bin/nvcc', '--version']) 44 | try: 45 | lines = cmd() 46 | for line in lines: 47 | match = re.search(CUDA_NVCC_RE, line) 48 | if match: 49 | cuda_version = match.group(1) 50 | break 51 | except (OSError, Command.CommandException): 52 | pass 53 | elif subprocess.call(["which", "nvcc"], stdout=subprocess.DEVNULL) == 0: 54 | cmd = Command(['nvcc', '--version']) 55 | try: 56 | lines = cmd() 57 | for line in lines: 58 | match = re.search(CUDA_NVCC_RE, line) 59 | if match: 60 | cuda_version = match.group(1) 61 | break 62 | except (OSError, Command.CommandException): 63 | pass 64 | return cuda_version 65 | 66 | 67 | def get_opencv(): 68 | opencv_version = '' 69 | opencv_cuda = False 70 | cmd = Command(['opencv_version']) 71 | try: 72 | lines = cmd() 73 | # Extract OpenCV version 74 | opencv_version = ''.join(lines) 75 | # Extract if compiled with CUDA 76 | cmd = Command(['opencv_version', '--verbose']) 77 | lines = cmd() 78 | for line in lines: 79 | if "NVIDIA CUDA" in line: 80 | opencv_cuda = True 81 | break 82 | if "Use Cuda" in line: 83 | opencv_cuda = False if "NO" in line else True 84 | break 85 | except (OSError, Command.CommandException): 86 | pass 87 | return opencv_version, opencv_cuda 88 | 89 | 90 | def get_all_modules(): 91 | modules = {} 92 | # Extract all modules in dpkg -l 93 | dpkg = Command(['dpkg', '-l']) 94 | try: 95 | lines = dpkg() 96 | for row in lines: 97 | row = re.sub(r'\n+ +', '\n', row) # remove spaces at the start of lines and empty lines 98 | row = re.sub(r'\s +', '\t', row) # replace two or more spaces with tab 99 | cells = row.split('\t') 100 | if len(cells) > 2: 101 | name = cells[1] 102 | version = cells[2] 103 | if version not in ['arm64', 'amd64']: 104 | modules[name] = version 105 | except (OSError, Command.CommandException): 106 | pass 107 | return modules 108 | 109 | 110 | def get_libraries(): 111 | os_variables = {} 112 | # Find all modules 113 | modules = get_all_modules() 114 | for name in MODULES: 115 | # Fix TensorRT search #462 116 | name_dict = name[:-1] if name.endswith('.') else name 117 | os_variables[name_dict] = '' 118 | # Find version if installed 119 | for module, version in modules.items(): 120 | if name.endswith('.') and name.lower()[:-1] == module: 121 | os_variables[name_dict] = version.split('-')[0] 122 | break 123 | if name.lower() in module: 124 | os_variables[name_dict] = version.split('-')[0] 125 | break 126 | # Get Vulkan output 127 | cmd_vulkaninfo = Command(['which', 'vulkaninfo']) 128 | try: 129 | lines = cmd_vulkaninfo() 130 | # Extract version 131 | if not lines: 132 | raise Command.CommandException("Missing command", -3) 133 | cmd_vulkan = Command(lines) 134 | lines = cmd_vulkan() 135 | for line in lines: 136 | if "Vulkan Instance Version" in line: 137 | os_variables['Vulkan'] = line.lstrip("Vulkan Instance Version: ") 138 | break 139 | except (OSError, Command.CommandException): 140 | pass 141 | return os_variables 142 | 143 | 144 | if __name__ == "__main__": 145 | # Get CUDA 146 | cuda_version = get_cuda() 147 | if cuda_version: 148 | print("CUDA: {version}".format(version=cuda_version)) 149 | else: 150 | print("CUDA not installed!") 151 | # Find OpenCV 152 | opencv_version, opencv_cuda = get_opencv() 153 | if opencv_version: 154 | opencv_cuda_string = "YES" if opencv_cuda else "NO" 155 | print("OPENCV: {opencv_version} - with CUDA: {opencv_cuda}".format(opencv_version=opencv_version, opencv_cuda=opencv_cuda_string)) 156 | else: 157 | print("OPENCV not Installed!") 158 | # Get all libraries 159 | os_variables = get_libraries() 160 | for name, value in os_variables.items(): 161 | print("{name}: {value}".format(name=name, value=value)) 162 | # EOF 163 | -------------------------------------------------------------------------------- /jtop/core/processes.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import re 19 | import os 20 | import pwd 21 | from .common import cat 22 | # Logging 23 | import logging 24 | # Create logger 25 | logger = logging.getLogger(__name__) 26 | 27 | MEM_TABLE_REG = re.compile(r'^(?P\w+)\s+(?P[^ ]+)\s+(?P\d+)\s+(?P\d+)(?P\w)\n') 28 | TOT_TABLE_REG = re.compile(r'total\s+(?P\d+)(?P\w)') 29 | 30 | 31 | def read_process_table(path_table): 32 | """ 33 | This method list all processes working with GPU 34 | 35 | ========== ============ ======== ============= 36 | user process PID size 37 | ========== ============ ======== ============= 38 | user name process number dictionary 39 | ========== ============ ======== ============= 40 | 41 | :return: list of all processes 42 | :type spin: list 43 | """ 44 | table = [] 45 | total = {} 46 | with open(path_table, "r") as fp: 47 | for line in fp: 48 | # Search line 49 | match = re.search(MEM_TABLE_REG, line) 50 | if match: 51 | parsed_line = match.groupdict() 52 | data = [ 53 | parsed_line['PID'], 54 | parsed_line['user'], 55 | parsed_line['process'], 56 | int(parsed_line['size']), 57 | ] 58 | table += [data] 59 | continue 60 | # Find total on table 61 | match = re.search(TOT_TABLE_REG, line) 62 | if match: 63 | parsed_line = match.groupdict() 64 | total = int(parsed_line['size']) 65 | continue 66 | # return total and table 67 | return total, table 68 | 69 | 70 | class ProcessService(object): 71 | 72 | def __init__(self): 73 | self.usernames = {4294967295: "root"} 74 | # board type 75 | self._root_path = "/sys/kernel" 76 | if os.getenv('JTOP_TESTING', False): 77 | self._root_path = "/fake_sys/kernel" 78 | logger.warning("Running in JTOP_TESTING folder={root_dir}".format(root_dir=self._root_path)) 79 | # Check if jetson board 80 | self._isJetson = os.path.isfile(self._root_path + "/debug/nvmap/iovmm/maps") 81 | # Get the clock ticks per second and page size 82 | self._clk_tck = os.sysconf('SC_CLK_TCK') 83 | # self._page_size = os.sysconf('SC_PAGE_SIZE') 84 | # Initialization memory 85 | logger.info("Process service started") 86 | 87 | def get_process_info(self, pid, gpu_mem_usage, process_name, uptime): 88 | # Check if exist folder 89 | if not os.path.isdir(os.path.join('/proc', pid)): 90 | return [] 91 | # https://man7.org/linux/man-pages/man5/proc.5.html 92 | stat = cat(os.path.join('/proc', pid, 'stat')).split() 93 | # Decode uid and find username 94 | uid = int(cat(os.path.join('/proc', pid, 'loginuid'))) 95 | if uid not in self.usernames: 96 | self.usernames[uid] = pwd.getpwuid(uid).pw_name 97 | # Read memory process 98 | # Extract resident set size (VmRSS) (Second field) 99 | # VmRSS is the resident set size of the process, which is the portion of the process's memory 100 | # that is held in RAM and is not swapped out to disk. This is the amount of memory that the process is currently using. 101 | mem_raw = cat(os.path.join('/proc', pid, 'statm')).split() 102 | vm_rss = int(mem_raw[1]) 103 | # CPU percent 104 | # https://stackoverflow.com/questions/16726779/how-do-i-get-the-total-cpu-usage-of-an-application-from-proc-pid-stat 105 | utime = float(stat[13]) 106 | stime = float(stat[14]) 107 | starttime = float(stat[21]) / self._clk_tck 108 | total_time = (utime + stime) / self._clk_tck 109 | proc_uptime = max(1, uptime - starttime) 110 | cpu_percent = 100 * (total_time / proc_uptime) 111 | 112 | process = [ 113 | int(pid), # pid process 114 | self.usernames[uid], # username 115 | "I", # GPU name 116 | "Graphic", # type process 117 | int(stat[17]), # Priority 118 | stat[2], # state 119 | cpu_percent, # CPU percent 120 | vm_rss, # MEM process 121 | gpu_mem_usage, # GPU mem usage 122 | process_name, # Process name 123 | ] 124 | return process 125 | 126 | def get_status(self): 127 | total = {} 128 | table = [] 129 | # Update table 130 | if self._isJetson: 131 | # Use the memory table to measure 132 | total, table = read_process_table(self._root_path + "/debug/nvmap/iovmm/maps") 133 | 134 | uptime = float(open('/proc/uptime', 'r').readline().split()[0]) 135 | 136 | table = [self.get_process_info(prc[0], prc[3], prc[2], uptime) for prc in table] 137 | 138 | return total, table 139 | # EOF 140 | -------------------------------------------------------------------------------- /jtop/core/tegrastats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # Logging 19 | import logging 20 | import sys 21 | # Launch command 22 | import subprocess as sp 23 | # Threading 24 | from threading import Thread, Event 25 | # Tegrastats parser 26 | from .tegra_parse import DATE, VALS, MTS, RAM, SWAP, IRAM, CPUS, TEMPS, WATTS 27 | from .common import locate_commands 28 | # Create logger for tegrastats 29 | logger = logging.getLogger(__name__) 30 | 31 | 32 | class Tegrastats: 33 | """ 34 | - Subprocess read: 35 | https://stackoverflow.com/questions/375427/non-blocking-read-on-a-subprocess-pipe-in-python/4896288#4896288 36 | - Property 37 | https://www.programiz.com/python-programming/property 38 | """ 39 | 40 | def __init__(self, callback, tegrastats_path): 41 | self._running = Event() 42 | # Error message from thread 43 | self._error = None 44 | # Start process tegrastats 45 | self.path = locate_commands("tegrastats", tegrastats_path) 46 | # Define Tegrastats process 47 | self._thread = None 48 | # Initialize callback 49 | self.callback = callback 50 | 51 | def _decode(self, text): 52 | # Remove date from tegrastats string 53 | text = DATE(text) 54 | # Find and parse all single values 55 | stats = VALS(text) 56 | # Parse if exist MTS 57 | mts = MTS(text) 58 | if mts: 59 | stats['MTS'] = mts 60 | # Parse RAM 61 | stats['RAM'] = RAM(text) 62 | # If exists parse SWAP 63 | swap = SWAP(text) 64 | if swap: 65 | stats['SWAP'] = swap 66 | # If exists parse IRAM 67 | iram = IRAM(text) 68 | if iram: 69 | stats['IRAM'] = iram 70 | # Parse CPU status 71 | stats['CPU'] = CPUS(text) 72 | # Parse temperatures 73 | stats['TEMP'] = TEMPS(text) 74 | # Parse Watts 75 | stats['WATT'] = WATTS(text) 76 | return stats 77 | 78 | def _read_tegrastats(self, interval, running): 79 | pts = sp.Popen([self.path, '--interval', str(interval)], stdout=sp.PIPE) 80 | try: 81 | # Reading loop 82 | while running.is_set(): 83 | if pts.poll() is not None: 84 | continue 85 | out = pts.stdout 86 | if out is not None: 87 | # Read line process output 88 | line = out.readline() 89 | # Decode line in UTF-8 90 | tegrastats_data = line.decode("utf-8") 91 | # Decode and store 92 | stats = self._decode(tegrastats_data) 93 | # Launch callback 94 | self.callback(stats) 95 | except AttributeError: 96 | pass 97 | except IOError: 98 | pass 99 | except Exception: 100 | # Write error message 101 | self._error = sys.exc_info() 102 | finally: 103 | # Kill process 104 | try: 105 | pts.kill() 106 | except OSError: 107 | pass 108 | 109 | def open(self, interval=0.5): 110 | if self._thread is not None: 111 | return False 112 | # Set timeout 113 | interval = int(interval * 1000) 114 | # Check if thread or process exist 115 | self._running.set() 116 | # Start thread Service client 117 | self._thread = Thread(target=self._read_tegrastats, args=(interval, self._running, )) 118 | self._thread.start() 119 | return True 120 | 121 | def close(self, timeout=None): 122 | # Catch exception if exist 123 | if self._error: 124 | # Extract exception and raise 125 | ex_type, ex_value, tb_str = self._error 126 | ex_value.__traceback__ = tb_str 127 | raise ex_value 128 | # Check if thread and process are already empty 129 | self._running.clear() 130 | if self._thread is not None: 131 | self._thread.join(timeout) 132 | self._thread = None 133 | return True 134 | # EOF 135 | -------------------------------------------------------------------------------- /jtop/core/timer_reader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import sys 19 | import time 20 | from threading import Thread, Event 21 | # Logging 22 | import logging 23 | # Create logger 24 | logger = logging.getLogger(__name__) 25 | 26 | TIMER_READER_MIN_SLEEP = 0.05 27 | 28 | 29 | class TimerReader: 30 | 31 | def __init__(self, callback): 32 | self._stop_event = Event() 33 | # Initialize callback 34 | self._callback = callback 35 | # Error message from thread 36 | self._error = None 37 | # Define Thread 38 | self._thread = None 39 | 40 | def _timer_callback(self, interval, stop_event): 41 | logger.debug("jtop timer start at {interval}s".format(interval=interval)) 42 | try: 43 | while stop_event.is_set(): 44 | start = time.time() 45 | # Callback function 46 | self._callback() 47 | # Measure timer_callback sleep time 48 | delta = time.time() - start 49 | # Start to sleep 50 | if interval > delta: 51 | time.sleep(interval - delta) 52 | except (KeyboardInterrupt, SystemExit): 53 | logger.info("KeyboardInterrupt or SystemExit, exit timer_reader thread") 54 | except Exception as e: 55 | logger.fatal("Exception in 'timer_reader thread': {}".format(e)) 56 | # Store error message 57 | self._error = sys.exc_info() 58 | logger.debug("jtop timer stopped") 59 | 60 | def open(self, interval=0.5): 61 | # Catch exception if exist 62 | self._error_status() 63 | # Check if not running 64 | if self._thread is not None: 65 | return False 66 | # Check if thread or process exist 67 | self._stop_event.set() 68 | # Start thread Service client 69 | self._thread = Thread(target=self._timer_callback, args=(interval, self._stop_event, )) 70 | self._thread.start() 71 | return True 72 | 73 | def close(self, timeout=None): 74 | # Catch exception if exist 75 | self._error_status() 76 | # Check if thread and process are already empty 77 | self._stop_event.clear() 78 | if self._thread is not None: 79 | self._thread.join(timeout) 80 | self._thread = None 81 | return True 82 | 83 | def _error_status(self): 84 | # Catch exception if exist 85 | if not self._error: 86 | return 87 | # Extract exception and raise 88 | ex_type, ex_value, tb_str = self._error 89 | ex_value.__traceback__ = tb_str 90 | raise ex_value 91 | # EOF 92 | -------------------------------------------------------------------------------- /jtop/gui/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # flake8: noqa 19 | 20 | from .jtopguiconfig import JTOPCONFIG 21 | from .jtopgui import JTOPGUI, Page 22 | from .pall import ALL 23 | from .pcpu import CPU 24 | from .pgpu import GPU 25 | from .pengine import ENGINE, engine_model 26 | from .pmem import MEM 27 | from .pcontrol import CTRL 28 | from .pinfo import INFO 29 | # EOF 30 | -------------------------------------------------------------------------------- /jtop/gui/lib/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # flake8: noqa 19 | -------------------------------------------------------------------------------- /jtop/gui/lib/colors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | 20 | 21 | def init_colorscale_pair(num, fg, bg): 22 | curses.init_pair(num, fg if curses.COLORS >= 256 else curses.COLOR_WHITE, bg if curses.COLORS >= 256 else curses.COLOR_BLACK) 23 | 24 | 25 | class NColors: 26 | 27 | RED = 1 28 | GREEN = 2 29 | YELLOW = 3 30 | BLUE = 4 31 | MAGENTA = 5 32 | CYAN = 6 33 | 34 | iRED = 7 35 | iGREEN = 8 36 | iYELLOW = 9 37 | iBLUE = 10 38 | iMAGENTA = 11 39 | iCYAN = 12 40 | 41 | def __init__(self, color_filter): 42 | # Define pairing colors 43 | curses.init_pair(NColors.RED, curses.COLOR_RED if not color_filter else curses.COLOR_BLUE, curses.COLOR_BLACK) 44 | curses.init_pair(NColors.GREEN, curses.COLOR_GREEN, curses.COLOR_BLACK) 45 | curses.init_pair(NColors.YELLOW, curses.COLOR_YELLOW, curses.COLOR_BLACK) 46 | curses.init_pair(NColors.BLUE, curses.COLOR_BLUE, curses.COLOR_BLACK) 47 | curses.init_pair(NColors.MAGENTA, curses.COLOR_MAGENTA, curses.COLOR_BLACK) 48 | curses.init_pair(NColors.CYAN, curses.COLOR_CYAN, curses.COLOR_BLACK) 49 | # background 50 | curses.init_pair(NColors.iRED, curses.COLOR_WHITE, curses.COLOR_RED if not color_filter else curses.COLOR_BLUE) 51 | curses.init_pair(NColors.iGREEN, curses.COLOR_WHITE, curses.COLOR_GREEN) 52 | curses.init_pair(NColors.iYELLOW, curses.COLOR_BLACK, curses.COLOR_YELLOW) 53 | curses.init_pair(NColors.iBLUE, curses.COLOR_WHITE, curses.COLOR_BLUE) 54 | curses.init_pair(NColors.iMAGENTA, curses.COLOR_WHITE, curses.COLOR_MAGENTA) 55 | curses.init_pair(NColors.iCYAN, curses.COLOR_WHITE, curses.COLOR_CYAN) 56 | 57 | @staticmethod 58 | def init_grey(num): 59 | init_colorscale_pair(num, 240, curses.COLOR_BLACK) 60 | 61 | @staticmethod 62 | def italic(): 63 | # Check if Italic is included 64 | return curses.A_ITALIC if hasattr(curses, 'A_ITALIC') else curses.A_NORMAL 65 | 66 | @staticmethod 67 | def red(): 68 | return curses.color_pair(NColors.RED) 69 | 70 | @staticmethod 71 | def green(): 72 | return curses.color_pair(NColors.GREEN) 73 | 74 | @staticmethod 75 | def yellow(): 76 | return curses.color_pair(NColors.YELLOW) 77 | 78 | @staticmethod 79 | def blue(): 80 | return curses.color_pair(NColors.BLUE) 81 | 82 | @staticmethod 83 | def magenta(): 84 | return curses.color_pair(NColors.MAGENTA) 85 | 86 | @staticmethod 87 | def cyan(): 88 | return curses.color_pair(NColors.CYAN) 89 | 90 | @staticmethod 91 | def ired(): 92 | return curses.color_pair(NColors.iRED) 93 | 94 | @staticmethod 95 | def igreen(): 96 | return curses.color_pair(NColors.iGREEN) 97 | 98 | @staticmethod 99 | def iyellow(): 100 | return curses.color_pair(NColors.iYELLOW) 101 | 102 | @staticmethod 103 | def iblue(): 104 | return curses.color_pair(NColors.iBLUE) 105 | 106 | @staticmethod 107 | def imagenta(): 108 | return curses.color_pair(NColors.iMAGENTA) 109 | 110 | @staticmethod 111 | def icyan(): 112 | return curses.color_pair(NColors.iCYAN) 113 | # EOF 114 | -------------------------------------------------------------------------------- /jtop/gui/lib/dialog_window.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | from .smallbutton import ButtonList 20 | # Gui refresh rate 21 | GUI_REFRESH = 1000 // 20 22 | 23 | 24 | class DialogWindow(object): 25 | def __init__(self, title, text, on_click, buttons, width=44, height=6): 26 | self.dialog_width = width 27 | self.dialog_height = height 28 | self.title = title 29 | self.text = text 30 | self.on_click = on_click 31 | self.buttons = buttons 32 | self.enable_dialog_window = False 33 | self._dialog_win = None 34 | self.info = {} 35 | 36 | def enable(self, title="", info={}): 37 | if title: 38 | self.title = title 39 | self.info = info 40 | self.enable_dialog_window = True 41 | 42 | def disable(self): 43 | self.enable_dialog_window = False 44 | 45 | def show(self, stdscr, key, mouse): 46 | if self.enable_dialog_window: 47 | self._draw(stdscr, key, mouse) 48 | 49 | def _draw(self, stdscr, key, mouse): 50 | # Refresh the window to show the changes 51 | height, width = stdscr.getmaxyx() 52 | dialog_y, dialog_x = (height - self.dialog_height) // 2, (width - self.dialog_width) // 2 53 | self._dialog_win = curses.newwin(self.dialog_height, self.dialog_width, dialog_y, dialog_x) 54 | # Create a list of buttons 55 | self._buttons_profile = ButtonList(self._dialog_win, self._on_click, self.buttons, info=self.info, linear=True) 56 | # Add a border around the window 57 | self._dialog_win.border() 58 | # Add the title and the text 59 | self._dialog_win.addstr(1, 2, self.title, curses.A_BOLD) 60 | self._dialog_win.addstr(2, 2, self.text) 61 | # Add the buttons 62 | align_mouse = (mouse[0] - dialog_x, mouse[1] - dialog_y) if mouse else () 63 | self._buttons_profile.update(4, 2, key, align_mouse, "") 64 | # Refresh the window to show the changes 65 | self._dialog_win.refresh() 66 | self._dialog_win.timeout(GUI_REFRESH) 67 | 68 | def _on_click(self, info, selected): 69 | self.on_click(info, selected) 70 | self.enable_dialog_window = False 71 | # EOF 72 | -------------------------------------------------------------------------------- /jtop/gui/lib/process_table.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | import curses 20 | from .colors import NColors 21 | from .common import size_to_string 22 | 23 | 24 | header = [ 25 | ("PID", {'clm': 7, 'fn': lambda x: str(x)}), 26 | ("USER", {'clm': 9, 'fn': lambda x: x}), 27 | ("GPU", {'clm': 5, 'fn': lambda x: x}), 28 | ("TYPE", {'clm': 6, 'fn': lambda x: x[0]}), 29 | ("PRI", {'clm': 5, 'fn': lambda x: str(x)}), 30 | ("S", {'clm': 4, 'fn': lambda x: x}), 31 | ("CPU%", {'clm': 7, 'fn': lambda x: "{:.1f}".format(x)}), 32 | ("MEM", {'clm': 8, 'fn': lambda x: size_to_string(x, 'k')}), 33 | ("GPU MEM", {'clm': 12, 'fn': lambda x: size_to_string(x, 'k')}), 34 | ("Command", {'clm': 20, 'fn': lambda x: x}), 35 | ] 36 | 37 | 38 | class ProcessTable(object): 39 | 40 | def __init__(self, stdscr, jetson): 41 | self.stdscr = stdscr 42 | self.jetson = jetson 43 | self.line_sort = 8 44 | self.type_reverse = True 45 | 46 | def draw(self, pos_y, pos_x, width, height, key, mouse): 47 | # Plot low bar background line 48 | try: 49 | self.stdscr.addstr(pos_y, 0, " " * width, NColors.igreen()) 50 | except curses.error: 51 | return 0 52 | title_counter = 0 53 | for idx, (title, info) in enumerate(header): 54 | try: 55 | # Check if pressed 56 | if mouse and mouse[1] == pos_y and title_counter <= mouse[0] <= title_counter + info['clm']: 57 | if self.line_sort != idx: 58 | self.line_sort = idx 59 | self.type_reverse = True 60 | else: 61 | self.type_reverse = not self.type_reverse 62 | # Draw title 63 | title = "[{}]".format(title) if idx == self.line_sort else title 64 | self.stdscr.addstr(pos_y, title_counter, title, NColors.igreen() | curses.A_BOLD) 65 | title_counter += info['clm'] 66 | except curses.error: 67 | break 68 | # Sort table for selected line 69 | try: 70 | sorted_processes = self.jetson.processes 71 | sorted_processes = sorted(sorted_processes, key=lambda x: x[self.line_sort], reverse=self.type_reverse) 72 | except IndexError: 73 | pass 74 | # Draw all processes 75 | # Instantiate the number of process variable to avoid an unbound local error if the process table is empty. 76 | nprocess = 0 77 | for nprocess, process in enumerate(sorted_processes): 78 | # Skip unit size process 79 | counter = 0 80 | for (value, (name, info)) in zip(process, header): 81 | # Print all values in a nice view 82 | try: 83 | self.stdscr.addstr(pos_y + nprocess + 1, counter, info['fn'](value), curses.A_NORMAL) 84 | counter += info['clm'] 85 | except curses.error: 86 | break 87 | # Stop loop if table is bigger than height 88 | if nprocess > height - 2: 89 | break 90 | return nprocess 91 | # EOF 92 | -------------------------------------------------------------------------------- /jtop/gui/pcpu.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | # Page class definition 20 | from .jtopgui import Page 21 | from .lib.chart import Chart 22 | from .lib.colors import NColors 23 | from .lib.common import unit_to_string 24 | from .lib.linear_gauge import freq_gauge, basic_gauge 25 | 26 | 27 | def cpu_gauge(stdscr, idx, cpu, pos_y, pos_x, _, size_w): 28 | # online status 29 | online = cpu['online'] if 'online' in cpu else True 30 | # name cpu - workararound for TOTAL cpu 31 | name = cpu['name'] if 'name' in cpu else str(idx + 1) + (" " if idx < 9 else "") 32 | # Plot values 33 | values = [ 34 | (cpu['user'], NColors.green()), 35 | (cpu['nice'], NColors.yellow()), 36 | (cpu['system'], NColors.red()), 37 | ] if online else [] 38 | # Draw gauge 39 | data = { 40 | 'name': name, 41 | 'color': NColors.cyan(), 42 | 'online': online, 43 | 'values': values, 44 | # 'mleft': cpu.get('governor', '').capitalize(), 45 | } 46 | if size_w < 16: 47 | basic_gauge(stdscr, pos_y, pos_x, size_w - 1, data) 48 | return 49 | elif 'freq' in cpu: 50 | # Draw current frequency 51 | curr_string = unit_to_string(cpu['freq']['cur'], 'k', 'Hz') 52 | stdscr.addstr(pos_y, pos_x + size_w - 6, curr_string, NColors.italic()) 53 | # Draw gauge 54 | basic_gauge(stdscr, pos_y, pos_x, size_w - 8, data) 55 | 56 | 57 | def cpu_grid(stdscr, list_cpu, print_cpu, start_y, start_x, size_height=0, size_width=0): 58 | num_cpu = len(list_cpu) 59 | if num_cpu == 0: 60 | return size_height, size_width, 0, 0 61 | size_columns = 4 if num_cpu > 6 else 2 62 | # Measure size rows and columns 63 | size_rows = int(num_cpu / size_columns) + bool((num_cpu / size_columns) % 1) 64 | size_columns = int(num_cpu / size_rows) + bool((num_cpu / size_rows) % 1) 65 | # Measure step height and width 66 | step_height = int(round(size_height / size_rows)) if size_height > 0 else 1 67 | step_width = int(size_width / size_columns) if size_width > 0 else 1 68 | # Build Grid 69 | idx_row = 0 70 | idx_column = 0 71 | for idx, cpu in enumerate(list_cpu): 72 | # Check row index 73 | if idx_row >= size_rows: 74 | idx_row = 0 75 | idx_column += 1 76 | # Get CPU in grid 77 | try: 78 | print_cpu(stdscr, idx, cpu, start_y + idx_row * step_height, start_x + idx_column * step_width, step_height - 1, step_width - 1) 79 | except curses.error: 80 | pass 81 | idx_row += 1 82 | # return matrix 83 | return step_height, step_width, size_columns, size_rows 84 | 85 | 86 | def compact_cpus(stdscr, pos_y, pos_x, width, jetson): 87 | _, _, _, size_rows = cpu_grid(stdscr, jetson.cpu['cpu'], cpu_gauge, pos_y, pos_x + 1, size_width=width - 2) 88 | return size_rows 89 | 90 | 91 | class CPU(Page): 92 | 93 | def __init__(self, stdscr, jetson): 94 | super(CPU, self).__init__("CPU", stdscr, jetson) 95 | # List all chart CPU 96 | size_cpu = len(jetson.cpu['cpu']) 97 | self._chart_cpus = [Chart(jetson, str(idx + 1), self.update_chart, color_text=curses.COLOR_BLUE, 98 | color_chart=[curses.COLOR_BLUE]) for idx in range(size_cpu)] 99 | 100 | def update_chart(self, jetson, name): 101 | cpu = jetson.cpu['cpu'][int(name) - 1] 102 | return { 103 | 'active': cpu['online'], 104 | 'value': [100 - cpu.get("idle", 100)], 105 | } 106 | 107 | def print_cpu(self, stdscr, idx, cpu, pos_y, pos_x, size_h, size_w): 108 | # Print status CPU 109 | governor = cpu.get('governor', '').capitalize() 110 | label_chart_cpu = "{percent: >3.0f}% {governor}".format(percent=100 - cpu.get('idle', 100), governor=governor) 111 | # Print chart 112 | chart = self._chart_cpus[idx] 113 | chart.draw(stdscr, [pos_x, pos_x + size_w], [pos_y, pos_y + size_h - 2], label=label_chart_cpu, y_label=False) 114 | # Print model 115 | model = cpu['model'] if 'model' in cpu else '' 116 | model = model[:size_w] 117 | try: 118 | stdscr.addstr(pos_y + size_h - 1, pos_x, model, curses.A_NORMAL) 119 | except curses.error: 120 | pass 121 | # Print info 122 | if 'freq' in cpu: 123 | freq = cpu['freq'] 124 | freq['online'] = cpu['online'] 125 | freq['name'] = "Frq" 126 | try: 127 | freq_gauge(stdscr, pos_y + size_h, pos_x, size_w, cpu['freq']) 128 | except curses.error: 129 | pass 130 | 131 | def draw(self, key, mouse): 132 | # Screen size 133 | height, width, first = self.size_page() 134 | # Print gauge all CPU 135 | total = self.jetson.cpu['total'] 136 | total['name'] = 'ALL' 137 | cpu_gauge(self.stdscr, 0, total, first + 1, 1, '', width) 138 | # Print all GRID CPU 139 | step_height, step_width, size_columns, size_rows = cpu_grid( 140 | self.stdscr, self.jetson.cpu['cpu'], self.print_cpu, first + 2, 1, size_height=height - 4, size_width=width - 8) 141 | # Print CPU Y axis 142 | chart = self._chart_cpus[0] 143 | for i in range(size_rows): 144 | chart.draw_y_axis(self.stdscr, first + 2 + i * step_height, 1 + step_width * size_columns, step_height - 3) 145 | # EOF 146 | -------------------------------------------------------------------------------- /jtop/jetson_swap.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import re 19 | import os 20 | import sys 21 | import argparse 22 | from .terminal_colors import bcolors 23 | from .core.memory import MemoryService, read_swapon 24 | from .core.common import get_var 25 | # Version match 26 | VERSION_RE = re.compile(r""".*__version__ = ["'](.*?)['"]""", re.S) 27 | COPYRIGHT_RE = re.compile(r""".*__copyright__ = ["'](.*?)['"]""", re.S) 28 | 29 | 30 | def main(): 31 | parser = argparse.ArgumentParser( 32 | description='Create a swap file and enable on boot (require sudo)', 33 | formatter_class=argparse.ArgumentDefaultsHelpFormatter) 34 | parser.add_argument('-d', '--dir', dest="directory", help='Directory to place swapfile', type=str, default='') 35 | parser.add_argument('-n', '--name', dest="name", help='Name swap file', type=str, default='swapfile') 36 | parser.add_argument('-s', '--size', dest="size", help='Size in Gigabytes', type=int, default='8') 37 | parser.add_argument('-a', '--auto', dest="auto", help='Enable swap on boot', action="store_true", default=False) 38 | parser.add_argument('-t', '--status', dest="status", help='Check if the swap is currently active', action="store_true", default=False) 39 | parser.add_argument('--off', dest="off", help='Switch off the swap', action="store_true", default=False) 40 | # Parse arguments 41 | args = parser.parse_args() 42 | # Copyrights 43 | print("Software part of jetson-stats {version} - {copyright}".format(version=get_var(VERSION_RE), copyright=get_var(COPYRIGHT_RE))) 44 | # Status swap 45 | if args.status: 46 | # Print all swap 47 | swap_table = read_swapon() 48 | print("NAME\t\tTYPE\t\tPRIO\t\tSIZE\t\tUSED") 49 | for name, swap in swap_table.items(): 50 | print("{name}\t{type}\t\t{prio}\t\t{size}{unit}\t{used}{unit}".format( 51 | name=name, 52 | type=swap['type'], 53 | prio=swap['prio'], 54 | size=swap['size'], 55 | used=swap['used'], 56 | unit='k')) 57 | sys.exit(0) 58 | # Check if running a root 59 | if os.getuid() != 0: 60 | # Quit with error 61 | print(bcolors.fail("Please run with sudo")) 62 | parser.print_help(sys.stderr) 63 | sys.exit(1) 64 | # Define Memory Service 65 | memory_service = MemoryService 66 | # Path swap 67 | size = args.size 68 | auto = args.auto 69 | path_swap = "{directory}/{name}".format(directory=args.directory, name=args.name) 70 | if args.off: 71 | print("Switch off swap {path_swap}".format(path_swap=path_swap)) 72 | memory_service.swap_deactivate(path_swap) 73 | sys.exit(0) 74 | # Create Swap 75 | print("Create swap {path_swap} [{size}GB] - on boot={auto}".format(path_swap=path_swap, size=size, auto=auto)) 76 | # Create swap 77 | memory_service.swap_set(size, path_swap, auto) 78 | 79 | 80 | if __name__ == "__main__": 81 | main() 82 | # EOF 83 | -------------------------------------------------------------------------------- /jtop/terminal_colors.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | 19 | class bcolors: 20 | HEADER = '\033[95m' 21 | OKBLUE = '\033[94m' 22 | OKGREEN = '\033[92m' 23 | WARNING = '\033[93m' 24 | FAIL = '\033[91m' 25 | ENDC = '\033[0m' 26 | BOLD = '\033[1m' 27 | UNDERLINE = '\033[4m' 28 | 29 | @staticmethod 30 | def ok(message="OK"): 31 | return bcolors.OKGREEN + str(message) + bcolors.ENDC 32 | 33 | @staticmethod 34 | def warning(message="WARN"): 35 | return bcolors.WARNING + str(message) + bcolors.ENDC 36 | 37 | @staticmethod 38 | def fail(message="ERR"): 39 | return bcolors.FAIL + str(message) + bcolors.ENDC 40 | 41 | @staticmethod 42 | def bold(message): 43 | return bcolors.BOLD + str(message) + bcolors.ENDC 44 | # EOF 45 | -------------------------------------------------------------------------------- /jtop/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /jtop/tests/marco_functions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import warnings 19 | # Max count to wait 20 | MAX_COUNT = 50 21 | # TEST NVP MODELS: 22 | # - [0] MAXTEST (DEFAULT) 23 | # - [1] TEST 24 | # - [2] MINTEST 25 | # - [3] MIN_MAX_TEST 26 | 27 | 28 | def set_fan_profile(jetson, new_profile): 29 | # Set new speed 30 | if jetson.ok(): 31 | jetson.fan.profile = new_profile 32 | # Wait jetson_clocks on 33 | counter = 0 34 | while jetson.ok(): 35 | if jetson.fan.profile == new_profile or counter == MAX_COUNT: 36 | break 37 | counter += 1 38 | if counter == MAX_COUNT: 39 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 40 | # Check if is true 41 | assert jetson.fan.profile == new_profile 42 | 43 | 44 | def set_fan_speed(jetson, new_speed): 45 | # Set new speed 46 | if jetson.ok(): 47 | jetson.fan.speed = new_speed 48 | # Wait jetson_clocks on 49 | counter = 0 50 | while jetson.ok(): 51 | if jetson.fan.speed == new_speed or counter == MAX_COUNT: 52 | break 53 | counter += 1 54 | if counter == MAX_COUNT: 55 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 56 | # Check if is true 57 | assert jetson.fan.speed == new_speed 58 | 59 | 60 | def set_jetson_clocks(jetson, status): 61 | if jetson.jetson_clocks is None: 62 | warnings.warn("jetson_clocks does not exists, please check file", UserWarning) 63 | return 64 | # Check if status is different 65 | if jetson.jetson_clocks != status: 66 | # Set true jetson_clocks 67 | if jetson.ok(): 68 | jetson.jetson_clocks = status 69 | # Wait jetson_clocks on 70 | counter = 0 71 | while jetson.ok(): 72 | if jetson.jetson_clocks == status or counter == MAX_COUNT: 73 | break 74 | counter += 1 75 | if counter == MAX_COUNT: 76 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 77 | # Check if is true 78 | assert jetson.jetson_clocks == status 79 | 80 | 81 | def set_jetson_clocks_boot(jetson, status): 82 | if jetson.jetson_clocks is None: 83 | warnings.warn("jetson_clocks does not exists, please check file", UserWarning) 84 | return 85 | # Set true jetson_clocks 86 | if jetson.ok(): 87 | jetson.jetson_clocks.boot = status 88 | # Wait jetson_clocks on 89 | counter = 0 90 | while jetson.ok(): 91 | if jetson.jetson_clocks.boot == status or counter == MAX_COUNT: 92 | break 93 | counter += 1 94 | if counter == MAX_COUNT: 95 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 96 | # Check if is true 97 | assert jetson.jetson_clocks.boot == status 98 | 99 | 100 | def set_nvp_mode(jetson, mode): 101 | # Check if status is different 102 | print("NVP-MODE: BEFORE assert str(jetson.nvpmodel)={nvp} - mode={mode}".format(nvp=str(jetson.nvpmodel), mode=mode)) 103 | if str(jetson.nvpmodel) != mode: 104 | # Check status nvpmodel 105 | if jetson.ok(): 106 | jetson.nvpmodel = mode 107 | # Wait change nvpmodel 108 | counter = 0 109 | while jetson.ok(): 110 | if str(jetson.nvpmodel) == mode or counter == MAX_COUNT: 111 | break 112 | counter += 1 113 | if counter == MAX_COUNT: 114 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 115 | # Check if is same model 116 | print("NVP-MODE: assert jetson.nvpmodel={nvp} - mode={mode}".format(nvp=str(jetson.nvpmodel), mode=mode)) 117 | assert str(jetson.nvpmodel) == mode 118 | # Check name variable 119 | assert jetson.nvpmodel.name == mode 120 | # EOF 121 | -------------------------------------------------------------------------------- /jtop/tests/test_00_service.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import time 19 | from jtop import jtop, JtopException 20 | from ..service import JtopServer 21 | from .conftest import reset_environment, emulate_device 22 | 23 | 24 | def test_service(): 25 | device = 'orin' 26 | emulate_device(device) 27 | # Start jtop Server 28 | jtop_server = JtopServer() 29 | jtop_server.start() 30 | # Check if is alive 31 | assert jtop_server.is_alive() 32 | # Init and open jtop 33 | jetson = jtop() 34 | jetson.start() 35 | # Wait 36 | time.sleep(0.5) 37 | # Close service 38 | jtop_server.close() 39 | # Close jetson 40 | try: 41 | jetson.close() 42 | except JtopException: 43 | pass 44 | # Check if service is off 45 | assert not jtop_server.is_alive() 46 | # Clear configuration 47 | jtop_server.config_clear() 48 | # Reset environment 49 | reset_environment(device) 50 | # EOF 51 | -------------------------------------------------------------------------------- /jtop/tests/test_01_outputs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | import os 20 | from datetime import timedelta 21 | from multiprocessing.pool import Pool 22 | from jtop import jtop, Memory, Fan, GPU 23 | from .conftest import emulate_all_devices 24 | NUM_PROCESSES = 20 25 | 26 | 27 | def check_attributes(jetson): 28 | # Jetson stats 29 | assert isinstance(jetson.stats, dict) 30 | # Jetson json 31 | assert isinstance(jetson.json(), str) 32 | assert isinstance(jetson.json(stats=True), str) 33 | # uptime 34 | assert isinstance(jetson.uptime, timedelta) 35 | # CPU 36 | assert isinstance(jetson.cpu, dict) 37 | # GPU 38 | assert isinstance(jetson.gpu, GPU) 39 | # Processes 40 | assert isinstance(jetson.processes, list) 41 | # Memory 42 | assert isinstance(jetson.memory, Memory) 43 | # Fan 44 | assert isinstance(jetson.fan, Fan) 45 | # Engines 46 | assert isinstance(jetson.engine, dict) 47 | # Status disk 48 | assert isinstance(jetson.disk, dict) 49 | # local interfaces 50 | assert isinstance(jetson.local_interfaces, dict) 51 | # Check power 52 | assert isinstance(jetson.power, dict) 53 | # Check temperature 54 | assert isinstance(jetson.temperature, dict) 55 | 56 | 57 | def test_hardware(setup_jtop_server): 58 | with jtop() as jetson: 59 | # Check contain hardware variables 60 | assert len(jetson.board['hardware']) > 0 61 | # Check contain Libraries information 62 | assert len(jetson.board['libraries']) > 0 63 | # Check contain platform variables 64 | assert len(jetson.board['platform']) > 0 65 | 66 | 67 | def test_open(setup_jtop_server): 68 | with jtop() as jetson: 69 | # Check status OK 70 | assert jetson.ok() 71 | # Check all attributes 72 | check_attributes(jetson) 73 | 74 | 75 | def jtop_callback(jetson): 76 | # Check all attributes 77 | check_attributes(jetson) 78 | # Close connection 79 | jetson.close() 80 | 81 | 82 | def test_open_callback(setup_jtop_server): 83 | # Initialize object 84 | jetson = jtop() 85 | # Attach callback 86 | jetson.attach(jtop_callback) 87 | # start jtop 88 | jetson.start() 89 | 90 | 91 | def jtop_worker(x): 92 | with jtop() as jetson: 93 | print("[{x}] jtop started on PID {pid}".format(x=x, pid=os.getpid())) 94 | # Check status OK 95 | for _ in range(10): 96 | assert jetson.ok() 97 | return True 98 | 99 | 100 | def test_multiple_run(setup_jtop_server): 101 | p = Pool(NUM_PROCESSES) 102 | pool_output = p.map(jtop_worker, range(NUM_PROCESSES)) 103 | # Check all return true 104 | assert all(pool_output) 105 | # Close pool 106 | p.close() 107 | p.join() 108 | 109 | 110 | test_hardware = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_hardware) 111 | test_open = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_open) 112 | test_open_callback = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_open_callback) 113 | test_multiple_run = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_multiple_run) 114 | # EOF 115 | -------------------------------------------------------------------------------- /jtop/tests/test_02_fan.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | from jtop import jtop 20 | from .marco_functions import set_fan_profile, set_fan_speed 21 | from .conftest import emulate_all_devices 22 | 23 | 24 | def test_fan(setup_jtop_server): 25 | device, jtop_server = setup_jtop_server 26 | # Run test 27 | with jtop() as jetson: 28 | print("Running test with parameter:", device) 29 | if jetson.ok(): 30 | # Read fan status 31 | fan = jetson.fan 32 | # Status fan 33 | print("Fan output: {fan}".format(fan=fan)) 34 | # Check depend of parameter 35 | if device in ['simple', 'tk']: 36 | assert len(fan) == 0 37 | else: 38 | assert len(fan) > 0 39 | 40 | 41 | def test_fan_set_profile(setup_jtop_server): 42 | device, jtop_server = setup_jtop_server 43 | with jtop() as jetson: 44 | # Detect which emulation is running and select a test profile 45 | if device in ['tx', 'nano']: 46 | set_fan_profile(jetson, 'temp_control') 47 | else: 48 | set_fan_profile(jetson, 'quiet') 49 | 50 | 51 | def test_fan_set_speed(setup_jtop_server): 52 | with jtop() as jetson: 53 | # Set a new fan speed 54 | set_fan_speed(jetson, 100) 55 | 56 | 57 | test_fan = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_fan) 58 | test_fan_set_profile = pytest.mark.parametrize( 59 | "setup_jtop_server", ['tx', 'nano', 'xavier', 'orin'], indirect=True)(test_fan_set_profile) 60 | test_fan_set_speed = pytest.mark.parametrize( 61 | "setup_jtop_server", ['tx', 'nano', 'xavier', 'orin'], indirect=True)(test_fan_set_speed) 62 | # EOF 63 | -------------------------------------------------------------------------------- /jtop/tests/test_03_jetson_clocks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | from jtop import jtop, JetsonClocks 20 | from .conftest import emulate_all_devices 21 | from .marco_functions import set_jetson_clocks_boot 22 | # test functions 23 | MAX_COUNT = 10 24 | 25 | 26 | def test_jetson_clocks_output(setup_jtop_server): 27 | device, jtop_server = setup_jtop_server 28 | with jtop() as jetson: 29 | print("Running test with parameter:", device) 30 | if jetson.ok(): 31 | # Read jetson_clocks status 32 | jetson_clocks = jetson.jetson_clocks 33 | # Status jetson_clocks 34 | print("jetson_clocks output: {jetson_clocks}".format(jetson_clocks=jetson_clocks)) 35 | # Check depend of parameter 36 | if device in ['simple', 'tk']: 37 | assert jetson_clocks is None 38 | else: 39 | assert isinstance(jetson_clocks, JetsonClocks) 40 | 41 | 42 | def test_set_true_false(setup_jtop_server): 43 | with jtop() as jetson: 44 | # Check jetson_clocks status 45 | assert jetson.jetson_clocks.status == 'inactive' 46 | # check status is false 47 | assert not jetson.jetson_clocks 48 | # Set true jetson_clocks 49 | jetson.jetson_clocks = True 50 | # Wait jetson_clocks on 51 | counter = 0 52 | while jetson.ok(): 53 | if jetson.jetson_clocks or counter == MAX_COUNT: 54 | break 55 | counter += 1 56 | # Check jetson_clocks status 57 | assert jetson.jetson_clocks.status == 'running' 58 | # Check if is true 59 | assert jetson.jetson_clocks 60 | # Switch off jetson_clocks 61 | jetson.jetson_clocks = False 62 | # Wait jetson_clocks on 63 | counter = 0 64 | while jetson.ok(): 65 | if not jetson.jetson_clocks or counter == MAX_COUNT: 66 | break 67 | counter += 1 68 | # Check jetson_clocks status 69 | assert jetson.jetson_clocks.status == 'inactive' 70 | # Set to false jetson_clocks 71 | assert not jetson.jetson_clocks 72 | 73 | 74 | def test_set_boot(setup_jtop_server): 75 | device, jtop_server = setup_jtop_server 76 | with jtop() as jetson: 77 | # Set to false jetson_clocks 78 | assert not jetson.jetson_clocks 79 | # Enable on boot 80 | set_jetson_clocks_boot(jetson, True) 81 | # Disable on boot 82 | assert jetson.jetson_clocks.boot 83 | 84 | 85 | test_jetson_clocks_output = pytest.mark.parametrize( 86 | "setup_jtop_server", emulate_all_devices(), indirect=True)(test_jetson_clocks_output) 87 | test_set_true_false = pytest.mark.parametrize( 88 | "setup_jtop_server", ['tx', 'nano', 'xavier', 'orin'], indirect=True)(test_set_true_false) 89 | test_set_boot = pytest.mark.parametrize( 90 | "setup_jtop_server", ['tx', 'nano', 'xavier', 'orin'], indirect=True)(test_set_boot) 91 | # EOF 92 | -------------------------------------------------------------------------------- /jtop/tests/test_04_nvpmodel.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | import warnings 20 | from jtop import jtop, NVPModel 21 | from .marco_functions import set_jetson_clocks, set_nvp_mode 22 | from .conftest import emulate_all_devices 23 | # Max count to wait 24 | MAX_COUNT = 50 25 | 26 | 27 | def test_nvpmodel_output(setup_jtop_server): 28 | device, jtop_server = setup_jtop_server 29 | with jtop() as jetson: 30 | print("Running test with parameter:", device) 31 | if jetson.ok(): 32 | # Read nvpmodel status 33 | nvpmodel = jetson.nvpmodel 34 | # Status nvpmodel 35 | print("nvpmodel output: {nvpmodel}".format(nvpmodel=nvpmodel)) 36 | # Check depend of parameter 37 | if device in ['simple', 'tk', 'tx']: 38 | assert nvpmodel is None 39 | else: 40 | assert isinstance(nvpmodel, NVPModel) 41 | 42 | 43 | def test_nvpmodel(setup_jtop_server): 44 | with jtop() as jetson: 45 | # Check status nvpmodel 46 | set_nvp_mode(jetson, "MIN_MAX_TEST") 47 | 48 | 49 | def test_nvpmodel_fail(setup_jtop_server): 50 | with jtop() as jetson: 51 | # Check status nvpmodel 52 | if jetson.ok(): 53 | jetson.nvpmodel = "MINTEST" 54 | # Check if is same model 55 | assert str(jetson.nvpmodel) != "MINTEST" 56 | # Check name variable 57 | assert jetson.nvpmodel.name != "MINTEST" 58 | 59 | 60 | def test_nvpmodel_increment_decrement(setup_jtop_server): 61 | with jtop() as jetson: 62 | # Save nvp ID 63 | nvp_id = jetson.nvpmodel.id 64 | # Set new NVP mode 65 | jetson.nvpmodel += 1 66 | # Wait change nvpmodel 67 | counter = 0 68 | while jetson.ok(): 69 | if jetson.nvpmodel.id == nvp_id + 1 or counter == MAX_COUNT: 70 | break 71 | counter += 1 72 | if counter == MAX_COUNT: 73 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 74 | # Check if is same model 75 | assert jetson.nvpmodel.id == nvp_id + 1 76 | # Save nvp ID 77 | nvp_id = jetson.nvpmodel.id 78 | # Set new NVP mode 79 | jetson.nvpmodel = jetson.nvpmodel - 1 80 | # Wait change nvpmodel 81 | counter = 0 82 | while jetson.ok(): 83 | if jetson.nvpmodel.id == nvp_id - 1 or counter == MAX_COUNT: 84 | break 85 | counter += 1 86 | if counter == MAX_COUNT: 87 | warnings.warn("Max time counter {counter}".format(counter=MAX_COUNT), UserWarning) 88 | # Check if is same model 89 | assert jetson.nvpmodel.id == nvp_id - 1 90 | 91 | 92 | def test_nvpmodel_jetson_clocks(setup_jtop_server): 93 | with jtop() as jetson: 94 | # Enable jetson_clocks 95 | set_jetson_clocks(jetson, True) 96 | # Check status nvpmodel 97 | set_nvp_mode(jetson, "TEST") 98 | # Disable jetson_clocks 99 | set_jetson_clocks(jetson, False) 100 | # Check status nvpmodel 101 | set_nvp_mode(jetson, "MIN_MAX_TEST") 102 | 103 | 104 | test_nvpmodel_output = pytest.mark.parametrize( 105 | "setup_jtop_server", emulate_all_devices(), indirect=True)(test_nvpmodel_output) 106 | test_nvpmodel = pytest.mark.parametrize( 107 | "setup_jtop_server", ['nano', 'xavier', 'orin'], indirect=True)(test_nvpmodel) 108 | test_nvpmodel_fail = pytest.mark.parametrize( 109 | "setup_jtop_server", ['nano', 'xavier', 'orin'], indirect=True)(test_nvpmodel_fail) 110 | test_nvpmodel_increment_decrement = pytest.mark.parametrize( 111 | "setup_jtop_server", ['nano', 'xavier', 'orin'], indirect=True)(test_nvpmodel_increment_decrement) 112 | test_nvpmodel_jetson_clocks = pytest.mark.parametrize( 113 | "setup_jtop_server", ['nano', 'xavier', 'orin'], indirect=True)(test_nvpmodel_jetson_clocks) 114 | # EOF 115 | -------------------------------------------------------------------------------- /jtop/tests/test_05_gui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import pytest 19 | import curses 20 | from jtop import jtop 21 | from .conftest import emulate_all_devices 22 | # Import gui test 23 | from ..gui.lib.chart import Chart 24 | from ..gui import JTOPGUI, ALL, GPU, CPU, MEM, ENGINE, CTRL, INFO 25 | 26 | 27 | @pytest.fixture 28 | def reset_chart(): 29 | Chart.reset_color_counter() 30 | 31 | 32 | def openGUI(stdscr, jetson): 33 | # Initialization Menu 34 | pages = [ALL] 35 | if jetson.gpu: 36 | pages += [GPU] 37 | pages += [CPU, MEM] 38 | if jetson.engine: 39 | pages += [ENGINE] 40 | pages += [CTRL, INFO] 41 | pages = JTOPGUI(stdscr, jetson, pages, start=False) 42 | return pages 43 | 44 | 45 | def test_openGUI(setup_jtop_server, reset_chart): 46 | # Load command line controller 47 | stdscr = curses.initscr() 48 | # Initialize colors 49 | curses.start_color() 50 | # Run jtop 51 | with jtop() as jetson: 52 | if jetson.ok(): 53 | # Reset counter charts 54 | Chart.reset_color_counter() 55 | assert Chart.COLOR_COUNTER == 0 56 | # Open JTOPGUI 57 | pages = openGUI(stdscr, jetson) 58 | # Start with selected page 59 | pages.set(0) 60 | assert True 61 | 62 | 63 | test_openGUI = pytest.mark.parametrize("setup_jtop_server", emulate_all_devices(), indirect=True)(test_openGUI) 64 | # EOF 65 | -------------------------------------------------------------------------------- /jtop/tests_gui/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | -------------------------------------------------------------------------------- /jtop/tests_gui/test_chart.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | # GUI jtop interface 20 | from ..gui import JTOPGUI, Page 21 | from ..gui.lib.chart import Chart 22 | # jtop client 23 | from ..jtop import jtop 24 | 25 | # How to run this test package 26 | # python3 -m jtop.tests_gui.test_chart 27 | 28 | 29 | class TestPage(Page): 30 | 31 | def __init__(self, stdscr, jetson): 32 | super(TestPage, self).__init__("Test", stdscr, jetson) 33 | # Create a chart 34 | self.chart = Chart(jetson, "Chart Test", self.update_chart) 35 | 36 | def update_chart(self, jetson, name): 37 | return {'value': [50]} 38 | 39 | def draw(self, key, mouse): 40 | # Screen size 41 | height, width, first = self.size_page() 42 | # Full size page 43 | size_x = [1, width - 1] 44 | size_y = [first + 1, height - 2] 45 | # Draw chart 46 | self.chart.draw(self.stdscr, size_x, size_y) 47 | 48 | 49 | def main(): 50 | 51 | with jtop() as jetson: 52 | curses.wrapper(JTOPGUI, jetson, [TestPage]) 53 | 54 | 55 | if __name__ == "__main__": 56 | main() 57 | # EOF 58 | -------------------------------------------------------------------------------- /jtop/tests_gui/test_gui_page.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | # GUI jtop interface 20 | from ..gui import JTOPGUI, Page 21 | # jtop client 22 | from ..jtop import jtop 23 | 24 | # How to run 25 | # python3 -m jtop.tests_gui.test_gui_page 26 | 27 | 28 | class TestPage(Page): 29 | 30 | def __init__(self, stdscr, jetson): 31 | super(TestPage, self).__init__("Test", stdscr, jetson) 32 | 33 | def draw(self, key, mouse): 34 | return super().draw(key, mouse) 35 | 36 | 37 | def main(): 38 | 39 | with jtop() as jetson: 40 | curses.wrapper(JTOPGUI, jetson, [TestPage]) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | # EOF 46 | -------------------------------------------------------------------------------- /jtop/tests_gui/test_linear_gauge.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | # GUI jtop interface 20 | from ..gui import JTOPGUI, Page 21 | from ..gui.lib.linear_gauge import linear_gauge 22 | # jtop client 23 | from ..jtop import jtop 24 | 25 | # How to run 26 | # python3 -m jtop.tests_gui.test_linear_gauge 27 | 28 | 29 | class TestPage(Page): 30 | 31 | def __init__(self, stdscr, jetson): 32 | super(TestPage, self).__init__("Test", stdscr, jetson) 33 | 34 | def draw(self, key, mouse): 35 | # Screen size 36 | height, width, first = self.size_page() 37 | # Print a linear gauge 38 | for idx in range(21): 39 | linear_gauge(self.stdscr, offset=first + 1 + idx, start=0, size=width, value=idx * 5) 40 | 41 | 42 | def main(): 43 | 44 | with jtop() as jetson: 45 | curses.wrapper(JTOPGUI, jetson, [TestPage]) 46 | 47 | 48 | if __name__ == "__main__": 49 | main() 50 | # EOF 51 | -------------------------------------------------------------------------------- /jtop/tests_gui/test_process_table.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | import curses 19 | # GUI jtop interface 20 | from ..gui import JTOPGUI, Page 21 | from ..gui.lib.process_table import ProcessTable 22 | # jtop client 23 | from ..jtop import jtop 24 | 25 | # How to run 26 | # python3 -m jtop.tests_gui.test_process_table 27 | 28 | 29 | class TestJtop(jtop): 30 | def __init__(self): 31 | super(TestJtop, self).__init__() 32 | 33 | @property 34 | def processes(self): 35 | ''' Return an empty list of processes for testing. ''' 36 | return [] 37 | 38 | 39 | class TestPage(Page): 40 | 41 | def __init__(self, stdscr, jetson): 42 | super(TestPage, self).__init__("Test", stdscr, jetson) 43 | 44 | self.process_table = ProcessTable(stdscr, jetson) 45 | 46 | def draw(self, key, mouse): 47 | # Screen size 48 | height, width, first = self.size_page() 49 | line_counter = first + 1 50 | 51 | # Draw process table 52 | line_counter += self.process_table.draw(line_counter, 0, width, height, key, mouse) 53 | 54 | 55 | def main(): 56 | 57 | with TestJtop() as jetson: 58 | curses.wrapper(JTOPGUI, jetson, [TestPage]) 59 | 60 | 61 | if __name__ == "__main__": 62 | main() 63 | # EOF 64 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | smbus2 2 | distro -------------------------------------------------------------------------------- /scripts/jtop_env.sh: -------------------------------------------------------------------------------- 1 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 2 | # Copyright (c) 2019-2023 Raffaello Bonghi. 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | # THIS SCRIPT MUST HAVE .SH ! 18 | 19 | # Load JETSON environment variables 20 | # Export variables to be loaded on bash script 21 | # https://blog.tintoy.io/2017/06/exporting-environment-variables-from-python-to-bash/ 22 | JETSON_VARIABLE="" 23 | JETSON_PYTHON_NAME="" 24 | if type -P python3 >/dev/null 2>&1 ; then 25 | JETSON_VARIABLE=$(python3 -c "import jtop; print(jtop.__path__[0])" 2> /dev/null) 26 | JETSON_PYTHON_NAME="python3" 27 | fi 28 | if type -P python >/dev/null 2>&1 && [ -z $JETSON_VARIABLE ] ; then 29 | JETSON_VARIABLE=$(python -c "import jtop; print(jtop.__path__[0])" 2> /dev/null) 30 | JETSON_PYTHON_NAME="python" 31 | fi 32 | 33 | # Load variables only if not empty the variable 34 | if [ ! -z $JETSON_VARIABLE ] ; then 35 | eval $($JETSON_PYTHON_NAME -m jtop.core.jetson_variables) 36 | fi 37 | # EOF 38 | -------------------------------------------------------------------------------- /services/jtop.service: -------------------------------------------------------------------------------- 1 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 2 | # Copyright (c) 2019-2023 Raffaello Bonghi. 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | [Unit] 17 | Description=jtop service 18 | After=multi-user.target 19 | 20 | [Service] 21 | # Type=exec 22 | Environment="JTOP_SERVICE=True" 23 | ExecStart=/usr/local/bin/jtop --force 24 | Restart=on-failure 25 | RestartSec=10s 26 | TimeoutStartSec=30s 27 | TimeoutStopSec=30s 28 | 29 | [Install] 30 | WantedBy=multi-user.target 31 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | # This includes the license file(s) in the wheel. 3 | # https://wheel.readthedocs.io/en/stable/user_guide.html#including-license-files-in-the-generated-wheel-file 4 | license_files = LICENSE 5 | 6 | [bdist_wheel] 7 | # This flag says to generate wheels that support both Python 2 and Python 8 | # 3. If your code will not run unchanged on both Python 2 and 3, you will 9 | # need to generate separate wheels for each Python version that you 10 | # support. Removing this line (or setting universal to 0) will prevent 11 | # bdist_wheel from trying to make a universal wheel. For more see: 12 | # https://packaging.python.org/guides/distributing-packages-using-setuptools/#wheels 13 | # universal=0 14 | -------------------------------------------------------------------------------- /tests/Dockerfile.sphinx: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | ARG PYTHON_VERSION=3.11 19 | FROM python:${PYTHON_VERSION} 20 | 21 | RUN apt-get update && \ 22 | apt-get install -y sudo bc make gcc g++ make && \ 23 | sudo -H python -m pip install --upgrade pip && \ 24 | rm -rf /var/lib/apt/lists/* 25 | 26 | COPY . /jetson_stats 27 | 28 | WORKDIR /jetson_stats 29 | 30 | # Install jtop 31 | RUN sudo pip3 install -v -e . 32 | # Install sphinx requirements 33 | RUN pip install -r docs/requirements.txt 34 | 35 | WORKDIR /jetson_stats/docs 36 | 37 | RUN sphinx-build -b html -W . _build/html 38 | 39 | CMD ["bash"] -------------------------------------------------------------------------------- /tests/Dockerfile.tox: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | ARG PYTHON_VERSION=3.12.0a3-slim-bullseye 19 | FROM python:${PYTHON_VERSION} 20 | 21 | RUN apt-get update && \ 22 | apt-get install -y sudo bc systemctl && \ 23 | rm -rf /var/lib/apt/lists/* 24 | 25 | COPY . /jetson_stats 26 | 27 | WORKDIR /jetson_stats 28 | 29 | RUN sudo groupadd jtop && \ 30 | sudo -H python -m pip install --upgrade pip && \ 31 | sudo -H pip install tox 32 | 33 | # Run tox 34 | RUN sudo tox -e py${PYTHON_VERSION%.*} 35 | 36 | CMD ["bash"] -------------------------------------------------------------------------------- /tests/local_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | bold=`tput bold` 19 | red=`tput setaf 1` 20 | green=`tput setaf 2` 21 | yellow=`tput setaf 3` 22 | blue=`tput setaf 4` 23 | reset=`tput sgr0` 24 | 25 | 26 | usage() 27 | { 28 | if [ "$1" != "" ]; then 29 | echo "${red}$1${reset}" 30 | fi 31 | 32 | echo "Jetson_stats tox local test. USE ONLY IN A TEST DESKTOP MACHINE!" 33 | echo "Usage:" 34 | echo "$0 [options]" 35 | echo "options," 36 | echo " -h|--help | This help" 37 | echo " --debug | Run image" 38 | echo " -py|--python [PYHTON] | Set a specific python version, example PYTHON=3.9" 39 | echo " --doc | Run and build ONLY the documentation" 40 | 41 | } 42 | 43 | main() 44 | { 45 | local DOCKER_BUILD=true 46 | local DOCUMENTATION_BUILD=true 47 | local PYTHON_LIST="2.7 3.6 3.8 3.9 3.10 3.11" 48 | local PYTHON_DEBUG=false 49 | local DOCUMENTATION=false 50 | 51 | # Decode all information from startup 52 | while [ -n "$1" ]; do 53 | case "$1" in 54 | -h|--help) 55 | # Load help 56 | usage 57 | exit 0 58 | ;; 59 | --doc) 60 | DOCKER_BUILD=false 61 | DOCUMENTATION=true 62 | ;; 63 | --debug) 64 | PYTHON_DEBUG=true 65 | DOCUMENTATION_BUILD=false 66 | DOCKER_BUILD=false 67 | ;; 68 | -py|--python) 69 | PYTHON_LIST=$2 70 | DOCUMENTATION_BUILD=false 71 | shift 1 72 | ;; 73 | *) 74 | usage "[ERROR] Unknown option: $1" 75 | exit 1 76 | ;; 77 | esac 78 | shift 1 79 | done 80 | 81 | 82 | if $DOCUMENTATION_BUILD ; then 83 | echo "- ${green}Build and compile jetson-stats documentation with sphinx${reset}" 84 | docker build -t rbonghi/jetson-stats:doc -f tests/Dockerfile.sphinx . || { echo "${red}docker build failure!${reset}"; exit 1; } 85 | fi 86 | 87 | if $DOCKER_BUILD ; then 88 | # Build all images 89 | for PYTHON_VERSION in $PYTHON_LIST; do 90 | echo "- ${green}Build and test image with python:${bold}$PYTHON_VERSION${reset}" 91 | docker build -t rbonghi/jetson-stats:tox-py$PYTHON_VERSION --build-arg "PYTHON_VERSION=$PYTHON_VERSION" -f tests/Dockerfile.tox . || { echo "${red}docker build failure!${reset}"; exit 1; } 92 | done 93 | fi 94 | 95 | if $PYTHON_DEBUG ; then 96 | if $DOCUMENTATION ; then 97 | echo "- ${yellow}Debug documentation image${reset}" 98 | docker run -v $(pwd):/jetson_stats -it --rm rbonghi/jetson-stats:doc 99 | else 100 | PYTHON_VERSION=$PYTHON_LIST 101 | echo "- ${yellow}Debug Image with python:${bold}$PYTHON_VERSION${reset}" 102 | docker run -v $(pwd):/jetson_stats -it --rm rbonghi/jetson-stats:tox-py$PYTHON_VERSION 103 | fi 104 | fi 105 | 106 | } 107 | 108 | main $@ 109 | exit 0 110 | 111 | #EOF 112 | -------------------------------------------------------------------------------- /tests/nvfancontrol: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 3 | # Copyright (c) 2019-2023 Raffaello Bonghi. 4 | # 5 | # This program is free software: you can redistribute it and/or modify 6 | # it under the terms of the GNU Affero General Public License as published by 7 | # the Free Software Foundation, either version 3 of the License, or 8 | # (at your option) any later version. 9 | # 10 | # This program is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU Affero General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU Affero General Public License 16 | # along with this program. If not, see . 17 | 18 | # Emulation of 19 | # https://docs.nvidia.com/jetson/archives/r34.1/DeveloperGuide/text/SD/PlatformPowerAndPerformance/JetsonOrinNxSeriesAndJetsonAgxOrinSeries.html 20 | 21 | usage() 22 | { 23 | if [ "$1" != "" ]; then 24 | tput setaf 1 25 | echo "$1" 26 | tput sgr0 27 | fi 28 | 29 | echo "FAKE Nvidia Fan Control Userspace Daemon" 30 | echo 31 | echo "Usage:" 32 | echo "nvfancontrol [-h | --help] [--verbose] [-q | --query] [-f | --file]" 33 | echo " -h, --help: Print this help info." 34 | echo " -f, --file: nvfancontrol conf file path." 35 | echo " -q, --query: print the current fan control status info." 36 | echo " --verbose: Enable verbose log." 37 | } 38 | 39 | 40 | query() 41 | { 42 | # Input file to be read 43 | input_file="/etc/nvfancontrol.conf" 44 | 45 | local fan_profile="" 46 | local fan_governor="" 47 | local fan_control="" 48 | # Loop through each line of the file 49 | while read line; do 50 | if [[ "$line" == *"FAN_DEFAULT_PROFILE"* ]]; then 51 | # Extract the second word from the line 52 | fan_profile=$(echo "$line" | awk '{print $2}') 53 | fi 54 | if [[ "$line" == *"FAN_DEFAULT_GOVERNOR"* ]]; then 55 | # Extract the second word from the line 56 | fan_governor=$(echo "$line" | awk '{print $2}') 57 | fi 58 | if [[ "$line" == *"FAN_DEFAULT_CONTROL"* ]]; then 59 | # Extract the second word from the line 60 | fan_control=$(echo "$line" | awk '{print $2}') 61 | fi 62 | done < "$input_file" 63 | 64 | echo "FAN1:FAN_PROFILE:$fan_profile" 65 | echo "FAN1:FAN_GOVERNOR:$fan_governor" 66 | echo "FAN1:FAN_CONTROL:$fan_control" 67 | 68 | mkdir -p /var/lib/nvfancontrol 69 | 70 | echo "FAN1:FAN_PROFILE:$fan_profile" > /var/lib/nvfancontrol/status 71 | echo "FAN1:FAN_GOVERNOR:$fan_governor" >> /var/lib/nvfancontrol/status 72 | echo "FAN1:FAN_CONTROL:$fan_control" >> /var/lib/nvfancontrol/status 73 | } 74 | 75 | 76 | main() 77 | { 78 | local VERBOSE=false 79 | 80 | if [ $(id -u) -ne 0 ] ; then 81 | echo "NVFAN ERROR: Sudo permissions are required to execute nvfancontrol user daemon" 82 | exit 1 83 | fi 84 | 85 | # Decode all information from startup 86 | while [ -n "$1" ]; do 87 | case "$1" in 88 | -q) 89 | query 90 | exit 0 91 | ;; 92 | --verbose) 93 | VERBOSE=true 94 | ;; 95 | -h|--help) 96 | usage 97 | exit 0 98 | ;; 99 | *) 100 | usage "[ERROR] Unknown option: $1" 101 | exit 1 102 | ;; 103 | esac 104 | shift 1 105 | done 106 | } 107 | 108 | main $@ 109 | exit 0 110 | 111 | # EOF 112 | -------------------------------------------------------------------------------- /tests/nvfancontrol.conf: -------------------------------------------------------------------------------- 1 | POLLING_INTERVAL 2 2 | 3 | TMARGIN ENABLED 4 | FAN_GOVERNOR pid { 5 | STEP_SIZE 10 6 | } 7 | FAN_PROFILE cool { 8 | #TEMP HYST PWM RPM 9 | 0 0 255 2900 10 | 18 9 255 2900 11 | 30 11 202 2300 12 | 45 11 149 1700 13 | 60 14 88 1000 14 | 105 0 0 0 15 | } 16 | FAN_PROFILE quiet { 17 | #TEMP HYST PWM RPM 18 | 0 0 202 2300 19 | 18 9 202 2300 20 | 30 11 158 1800 21 | 45 11 114 1300 22 | 60 14 62 700 23 | 105 0 0 0 24 | } 25 | THERMAL_GROUP 0 { 26 | GROUP_MAX_TEMP 105 27 | #Thermal-Zone Coeffs Max-Temp 28 | CPU-therm 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0 29 | GPU-therm 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0 30 | SOC0-therm 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0 31 | SOC1-therm 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0 32 | SOC2-therm 20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 0 33 | } 34 | FAN_DEFAULT_CONTROL open_loop 35 | FAN_DEFAULT_PROFILE cool 36 | FAN_DEFAULT_GOVERNOR pid 37 | -------------------------------------------------------------------------------- /tests/nvfancontrol.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Fake nvfanservice control 3 | 4 | [Service] 5 | ExecStart=/usr/bin/touch /tmp/nvfancontrol_tmp 6 | ExecStop=/bin/rm /tmp/nvfancontrol_tmp 7 | RemainAfterExit=yes 8 | 9 | [Install] 10 | WantedBy=multi-user.target -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # This file is part of the jetson_stats package (https://github.com/rbonghi/jetson_stats or http://rnext.it). 2 | # Copyright (c) 2019-2023 Raffaello Bonghi. 3 | # 4 | # This program is free software: you can redistribute it and/or modify 5 | # it under the terms of the GNU Affero General Public License as published by 6 | # the Free Software Foundation, either version 3 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU Affero General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU Affero General Public License 15 | # along with this program. If not, see . 16 | 17 | 18 | [tox] 19 | envlist = py{2.7,3.6,3.8,3.9,3.10,3.11,3.12} 20 | skip_missing_interpreters = true 21 | 22 | [testenv] 23 | setenv = 24 | TERM=linux 25 | TERMINFO=/etc/terminfo 26 | JTOP_TESTING=true 27 | basepython = 28 | py2.7: python2.7 29 | py3.6: python3.6 30 | py3.8: python3.8 31 | py3.9: python3.9 32 | py3.10: python3.10 33 | py3.11: python3.11 34 | py3.12: python3.12 35 | deps = 36 | # Reference https://github.com/mgedmin/check-manifest/issues/98 37 | check-manifest==0.41 38 | flake8 39 | pytest 40 | # pytest-cov 41 | # coveralls 42 | commands = 43 | # List of enviroments variables 44 | # https://tox.readthedocs.io/en/latest/config.html#substitutions-for-virtualenv-related-sections 45 | check-manifest --ignore tox.ini,jtop/tests*,docs*,examples* 46 | python setup.py check -m -s 47 | flake8 . 48 | # Run tests 49 | py.test -v 50 | 51 | [flake8] 52 | max-line-length = 160 53 | exclude = 54 | .git, 55 | .tox, 56 | .venv*, 57 | *.egg, 58 | build, 59 | test.*, 60 | data 61 | select = E,W,F 62 | 63 | [pytest] 64 | # log_cli = true 65 | # addopts = --capture=no --ignore=jtop/tests_gui 66 | addopts = --ignore=jtop/tests_gui 67 | log_cli_level = DEBUG 68 | log_format = %(asctime)s [%(levelname)s] %(name)s - %(message)s 69 | log_date_format = %Y-%m-%d %H:%M:%S 70 | filterwarnings = ignore:.* is deprecated:DeprecationWarning 71 | --------------------------------------------------------------------------------