@@ -0,0 +1,65 @@
|
||||
parse:
|
||||
additional_commands: {}
|
||||
override_spec: {}
|
||||
vartags: []
|
||||
proptags: []
|
||||
format:
|
||||
disable: false
|
||||
line_width: 90
|
||||
tab_size: 4
|
||||
use_tabchars: false
|
||||
fractional_tab_policy: use-space
|
||||
max_subgroups_hwrap: 2
|
||||
max_pargs_hwrap: 6
|
||||
max_rows_cmdline: 2
|
||||
separate_ctrl_name_with_space: false
|
||||
separate_fn_name_with_space: false
|
||||
dangle_parens: false
|
||||
dangle_align: child
|
||||
min_prefix_chars: 4
|
||||
max_prefix_chars: 10
|
||||
max_lines_hwrap: 2
|
||||
line_ending: unix
|
||||
command_case: lower
|
||||
keyword_case: upper
|
||||
always_wrap: []
|
||||
enable_sort: true
|
||||
autosort: false
|
||||
require_valid_layout: false
|
||||
layout_passes: {}
|
||||
markup:
|
||||
bullet_char: '-'
|
||||
enum_char: '*'
|
||||
first_comment_is_literal: true
|
||||
literal_comment_pattern: ^#
|
||||
fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$
|
||||
ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$
|
||||
explicit_trailing_pattern: '#<'
|
||||
hashruler_min_length: 10
|
||||
canonicalize_hashrulers: true
|
||||
enable_markup: true
|
||||
lint:
|
||||
disabled_codes: []
|
||||
function_pattern: '[0-9a-z_]+'
|
||||
macro_pattern: '[0-9A-Z_]+'
|
||||
global_var_pattern: '[A-Z][0-9A-Z_]+'
|
||||
internal_var_pattern: _[A-Z][0-9A-Z_]+
|
||||
local_var_pattern: '[a-z][a-z0-9_]+'
|
||||
private_var_pattern: _[0-9a-z_]+
|
||||
public_var_pattern: '[A-Z][0-9A-Z_]+'
|
||||
argument_var_pattern: '[a-z][a-z0-9_]+'
|
||||
keyword_pattern: '[A-Z][0-9A-Z_]+'
|
||||
max_conditionals_custom_parser: 2
|
||||
min_statement_spacing: 1
|
||||
max_statement_spacing: 2
|
||||
max_returns: 6
|
||||
max_branches: 12
|
||||
max_arguments: 5
|
||||
max_localvars: 15
|
||||
max_statements: 50
|
||||
encode:
|
||||
emit_byteorder_mark: false
|
||||
input_encoding: utf-8
|
||||
output_encoding: utf-8
|
||||
misc:
|
||||
per_command: {}
|
||||
@@ -0,0 +1,8 @@
|
||||
/dashboards
|
||||
/mongodb_connector
|
||||
/perfmon
|
||||
/sample
|
||||
/soc_params
|
||||
/utils
|
||||
/wave_parser
|
||||
/TestArea
|
||||
@@ -0,0 +1,60 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: analyze commands
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
# Cancel any previous runs
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install Rocm
|
||||
run: |
|
||||
sudo ./cmake/rocm_install.sh
|
||||
sudo apt-get update
|
||||
sudo apt-get dist-upgrade -y
|
||||
sudo apt-get autoclean
|
||||
- name: Install python prereqs
|
||||
run: |
|
||||
python3 -m pip install -r requirements.txt
|
||||
python3 -m pip install pyinstaller pytest pytest-cov mock
|
||||
|
||||
- name: build and install
|
||||
run: |
|
||||
ls
|
||||
ls src
|
||||
ls src/mibench
|
||||
ls src/mibench/roofline
|
||||
cmake -B build-omniperf -DCMAKE_INSTALL_PREFIX=/opt/omniperf -DROCM_VERSIONS="4.3.1" "4.5.2" "5.0.2" "5.1.3" "5.2.3"
|
||||
cmake --build build-omniperf --target all
|
||||
cmake --build build-omniperf --target install
|
||||
- name: run ctest
|
||||
run: |
|
||||
cd build-omniperf
|
||||
ctest --verbose -R test_analyze_commands
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: analyze workloads
|
||||
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
# Cancel any previous runs
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install Rocm
|
||||
run: |
|
||||
sudo ./cmake/rocm_install.sh
|
||||
sudo apt-get update
|
||||
sudo apt-get dist-upgrade -y
|
||||
sudo apt-get autoclean
|
||||
- name: Install python prereqs
|
||||
run: |
|
||||
python3 -m pip install -r requirements.txt
|
||||
python3 -m pip install pyinstaller pytest pytest-cov
|
||||
|
||||
- name: build and install
|
||||
run: |
|
||||
ls
|
||||
ls src
|
||||
ls src/mibench
|
||||
ls src/mibench/roofline
|
||||
cmake -B build-omniperf -DCMAKE_INSTALL_PREFIX=/opt/omniperf -DROCM_VERSIONS="4.3.1" "4.5.2" "5.0.2" "5.1.3" "5.2.3"
|
||||
cmake --build build-omniperf --target all
|
||||
cmake --build build-omniperf --target install
|
||||
- name: run ctest
|
||||
run: |
|
||||
cd build-omniperf
|
||||
ctest --verbose -R test_analyze_workloads test_saved_analysis
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
name: formatting
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, dev ]
|
||||
pull_request:
|
||||
branches: [ main, dev ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
python:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.8, 3.9]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
# Cancel any previous runs
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install black
|
||||
if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
|
||||
- name: black format
|
||||
run: |
|
||||
black --diff --check .
|
||||
|
||||
cmake:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y python3-pip
|
||||
python3 -m pip install cmake-format
|
||||
- name: cmake-format
|
||||
run: |
|
||||
set +e
|
||||
cmake-format -i $(find . -type f | egrep 'CMakeLists.txt|\.cmake$')
|
||||
if [ $(git diff | wc -l) -gt 0 ]; then
|
||||
echo -e "\nError! CMake code not formatted. Run cmake-format...\n"
|
||||
echo -e "\nFiles:\n"
|
||||
git diff --name-only
|
||||
echo -e "\nFull diff:\n"
|
||||
git diff
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python-bytecode:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: find-bytecode
|
||||
run: |
|
||||
set +e
|
||||
FILES=$(find . -type f | egrep '__pycache__|\.pyc$')
|
||||
if [ -n "${FILES}" ]; then
|
||||
echo -e "\nError! Python bytecode included in commit\n"
|
||||
echo -e "### FILES: ###"
|
||||
echo -e "${FILES}"
|
||||
echo -e "##############"
|
||||
exit 1
|
||||
fi
|
||||
@@ -0,0 +1,42 @@
|
||||
name: Packaging
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v[1-9].[0-9]+.[0-9]+*"
|
||||
|
||||
jobs:
|
||||
distbuild:
|
||||
runs-on: ubuntu-latest
|
||||
name: Create release distribution
|
||||
env:
|
||||
INSTALL_DIR: /tmp
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
- name: Verify VERSION file consistent with tag
|
||||
run: utils/ver_check.py --tag ${{github.ref_name}}
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Python dependency installs
|
||||
run: python3 -m pip install -t${INSTALL_DIR}/python-libs -r requirements.txt
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DPYTHON_DEPS=${INSTALL_DIR}/python-libs ..
|
||||
- name: Release tarball
|
||||
run: |
|
||||
cd build
|
||||
make package_source
|
||||
- name: Rename tarball
|
||||
run: mv build/omniperf-*.tar.gz build/omniperf-${{github.ref_name}}.tar.gz
|
||||
- name: Archive tarball
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: omniperf-${{github.ref_name}}.tar.gz
|
||||
path: build/omniperf-${{github.ref_name}}.tar.gz
|
||||
@@ -0,0 +1,64 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Packaging
|
||||
|
||||
# Controls when the workflow will run
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*.*.*"
|
||||
|
||||
# Allows you to run this workflow manually from the Actions tab
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
# Cancel any previous runs
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Install Rocm
|
||||
run: |
|
||||
sudo ./cmake/rocm_install.sh
|
||||
sudo apt-get update
|
||||
sudo apt-get dist-upgrade -y
|
||||
sudo apt-get autoclean
|
||||
- name: Install python prereqs
|
||||
run: |
|
||||
python3 -m pip install -r requirements.txt
|
||||
python3 -m pip install pyinstaller
|
||||
|
||||
- name: build and install
|
||||
run: |
|
||||
cmake -B build-omniperf -DCMAKE_INSTALL_PREFIX=/opt/omniperf -DROCM_VERSIONS="4.3.1" "4.5.2" "5.0.2" "5.1.3" "5.2.3"
|
||||
cmake --build build-omniperf --target all
|
||||
cmake --build build-omniperf --target install
|
||||
|
||||
- name: Read VERSION
|
||||
id: VERSION
|
||||
uses: juliangruber/read-file-action@v1
|
||||
with:
|
||||
path: ./VERSION
|
||||
|
||||
- name: generate packaging
|
||||
run: |
|
||||
cd build-omniperf
|
||||
cpack -G STGZ
|
||||
cpack -G DEB -D CPACK_PACKAGING_INSTALL_PREFIX=/opt/omniperf
|
||||
cpack -G RPM -D CPACK_PACKAGING_INSTALL_PREFIX=/opt/omniperf
|
||||
ls
|
||||
@@ -0,0 +1,92 @@
|
||||
name: Tarball
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
distbuild:
|
||||
runs-on: ubuntu-latest
|
||||
name: Create distribution tarball
|
||||
env:
|
||||
INSTALL_DIR: /tmp/foo1
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Cancel Previous Runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.8'
|
||||
- name: Python dependency installs
|
||||
run: python3 -m pip install -t${INSTALL_DIR}/python-libs -r requirements.txt
|
||||
- name: Configure
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DPYTHON_DEPS=${INSTALL_DIR}/python-libs ..
|
||||
- name: Release tarball
|
||||
run: |
|
||||
cd build
|
||||
make package_source
|
||||
- name: Archive tarball
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: tarball-testing
|
||||
path: build/omniperf-*.tar.gz
|
||||
retention-days: 3
|
||||
disttest:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [distbuild]
|
||||
name: Tarball tests
|
||||
env:
|
||||
INSTALL_DIR: /tmp/foo2
|
||||
steps:
|
||||
- name: Cancel previous runs
|
||||
uses: styfle/cancel-workflow-action@0.11.0
|
||||
- name: Access tarball
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: tarball-testing
|
||||
- name: Expand
|
||||
run: tar xfz omniperf-*.tar.gz; rm omniperf-*.tar.gz
|
||||
- name: Python dependency installs
|
||||
run: |
|
||||
cd omniperf-*
|
||||
python3 -m pip install -t${INSTALL_DIR}/python-libs -r requirements.txt
|
||||
- name: Configure
|
||||
run: |
|
||||
cd omniperf-*
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}/omniperf \
|
||||
-DPYTHON_DEPS=${INSTALL_DIR}/python-libs \
|
||||
-DMOD_INSTALL_PATH=${INSTALL_DIR}/modulefiles ..
|
||||
- name: Install
|
||||
run: |
|
||||
cd omniperf-*
|
||||
cd build
|
||||
make install
|
||||
- name: Verify expected paths
|
||||
run: |
|
||||
test -d $INSTALL_DIR/omniperf
|
||||
test -d $INSTALL_DIR/omniperf/bin
|
||||
test -x $INSTALL_DIR/omniperf/bin/omniperf
|
||||
test -d $INSTALL_DIR/modulefiles/omniperf
|
||||
- name: Query version (setting PYTHONPATH by hand)
|
||||
run: |
|
||||
export PYTHONPATH=${INSTALL_DIR}/python-libs:$PYTHONPATH
|
||||
$INSTALL_DIR/omniperf/bin/omniperf --version
|
||||
- name: Install Lmod
|
||||
run: sudo apt-get install -y lmod
|
||||
- name: Access omniperf using modulefile
|
||||
run: |
|
||||
. /etc/profile.d/lmod.sh
|
||||
module use $INSTALL_DIR/modulefiles
|
||||
module load omniperf
|
||||
module list
|
||||
omniperf --version
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# mongodb_connector files
|
||||
__pycache__
|
||||
|
||||
# edit files
|
||||
*~
|
||||
|
||||
# generated files/folders
|
||||
/dist
|
||||
/omniperf.spec
|
||||
/build*
|
||||
/.vscode
|
||||
/.cache
|
||||
/workloads
|
||||
.coverage
|
||||
saved_analysis
|
||||
pmc_kernel_top.csv
|
||||
|
||||
# temp files
|
||||
/tests/Testing
|
||||
@@ -0,0 +1,10 @@
|
||||
[submodule "src/mibench"]
|
||||
path = src/mibench
|
||||
url = git@github.com:AARInternal/mibench.git
|
||||
branch = main
|
||||
[submodule "src/waveparser"]
|
||||
path = src/waveparser
|
||||
url = git@github.com:AARInternal/waveparser.git
|
||||
[submodule "src/multevent"]
|
||||
path = src/multevent
|
||||
url = git@github.com:AARInternal/multevent.git
|
||||
@@ -0,0 +1,10 @@
|
||||
# This is the list of Omniperf's significant contributors.
|
||||
#
|
||||
# This does not necessarily list everyone who has contributed code,
|
||||
# especially since many employees of one corporation may be contributing.
|
||||
# To see the full list of contributors, see the revision history in
|
||||
# source control.
|
||||
Xiaomin Lu
|
||||
Cole Ramos
|
||||
Fei Zheng
|
||||
Jose Santos
|
||||
@@ -0,0 +1,277 @@
|
||||
cmake_minimum_required(VERSION 3.19 FATAL_ERROR)
|
||||
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR AND CMAKE_CURRENT_SOURCE_DIR STREQUAL
|
||||
CMAKE_SOURCE_DIR)
|
||||
set(MSG "")
|
||||
message(STATUS "Warning! Building from the source directory is not recommended")
|
||||
message(STATUS "If unintended, please remove 'CMakeCache.txt' and 'CMakeFiles'")
|
||||
message(STATUS "and build from a separate directory")
|
||||
message(FATAL_ERROR "In-source build")
|
||||
endif()
|
||||
|
||||
# System info
|
||||
cmake_host_system_information(RESULT LOCALHOST QUERY FQDN)
|
||||
message(STATUS "Hostname: ${LOCALHOST}")
|
||||
|
||||
# Versioning info derived from file
|
||||
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" FULL_VERSION_STRING LIMIT_COUNT 1)
|
||||
string(REGEX REPLACE "(\n|\r)" "" FULL_VERSION_STRING "${FULL_VERSION_STRING}")
|
||||
string(REGEX REPLACE "([0-9]+)\.([0-9]+)\.([0-9]+)(.*)" "\\1.\\2.\\3" OMNIPERF_VERSION
|
||||
"${FULL_VERSION_STRING}")
|
||||
|
||||
string(REGEX REPLACE "(${OMNIPERF_VERSION})(.*)" "\\2" OMNIPERF_VERSION_TWEAK
|
||||
"${FULL_VERSION_STRING}")
|
||||
string(REGEX REPLACE "^\\." "" OMNIPERF_VERSION_TWEAK "${OMNIPERF_VERSION_TWEAK}")
|
||||
|
||||
project(
|
||||
omniperf
|
||||
VERSION ${OMNIPERF_VERSION}
|
||||
LANGUAGES C
|
||||
DESCRIPTION "OmniPerf"
|
||||
HOMEPAGE_URL "https://github.com/AMDResearch/omniperf")
|
||||
|
||||
include(ExternalProject)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||||
set(CMAKE_INSTALL_PREFIX
|
||||
"/opt/apps/omniperf"
|
||||
CACHE PATH "default install path" FORCE)
|
||||
endif()
|
||||
message(STATUS "Installation path: ${CMAKE_INSTALL_PREFIX}")
|
||||
|
||||
# Python 3 is required
|
||||
message(STATUS "Detecting Python interpreter...")
|
||||
find_package(
|
||||
Python3 3.7
|
||||
COMPONENTS Interpreter
|
||||
REQUIRED)
|
||||
|
||||
# Allow user-provided python search path
|
||||
if(DEFINED PYTHON_DEPS)
|
||||
set(ENV{PYTHONPATH} "${PYTHON_DEPS}")
|
||||
message(STATUS "Optional PYTHON_DEPS provided:")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
message(STATUS "including ${PYTHON_DEPS} in search path")
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
endif()
|
||||
|
||||
# Check required Python packages
|
||||
set(pythonDeps
|
||||
"astunparse"
|
||||
"colorlover"
|
||||
"dash"
|
||||
"matplotlib"
|
||||
"numpy"
|
||||
"pandas"
|
||||
"pymongo"
|
||||
"yaml"
|
||||
"tabulate"
|
||||
"tqdm"
|
||||
"dash_svg"
|
||||
"dash_bootstrap_components")
|
||||
|
||||
message(STATUS "Checking for required Python package dependencies...")
|
||||
set_property(GLOBAL PROPERTY pythonDepsFlag "groovy")
|
||||
|
||||
function(checkPythonPackage [package])
|
||||
execute_process(
|
||||
COMMAND ${Python3_EXECUTABLE} -c "import ${ARGV0}"
|
||||
OUTPUT_QUIET ERROR_QUIET
|
||||
RESULT_VARIABLE EXIT_CODE)
|
||||
if(${EXIT_CODE} EQUAL 0)
|
||||
message(STATUS "${ARGV0} = yes")
|
||||
else()
|
||||
message(STATUS "${ARGV0} = missing")
|
||||
set_property(GLOBAL PROPERTY pythonDepsFlag "missing")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
foreach(package IN LISTS pythonDeps)
|
||||
checkpythonpackage(${package})
|
||||
endforeach()
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
|
||||
get_property(pythonDepsInstalled GLOBAL PROPERTY pythonDepsFlag)
|
||||
if(${pythonDepsInstalled} STREQUAL "groovy")
|
||||
message(STATUS "OK: Python dependencies available in current environment.")
|
||||
else()
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"\nNecessary Python package dependencies not found. Please install required dependencies "
|
||||
"above using your favorite package manager. If using pip, consider running:\n"
|
||||
"python3 -m pip install -r requirements.txt\n"
|
||||
"at the top-level of this repository. If preparing a shared installation for "
|
||||
"multiple users, consider adding the -t <target-dir> option to install necessary dependencies "
|
||||
"into a shared directory, e.g.\n"
|
||||
"python3 -m pip install -t <shared-install-path> -r requirements.txt\n"
|
||||
"Note that the -DPYTHON_DEPS=<shared-install-path> can be used to provide an "
|
||||
"additional search path to cmake for python packages.")
|
||||
endif()
|
||||
|
||||
# ----------------------
|
||||
# modulefile creation
|
||||
# ----------------------
|
||||
|
||||
set(MOD_INSTALL_PATH
|
||||
"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATAROOTDIR}/modulefiles"
|
||||
CACHE STRING "Install path for modulefile")
|
||||
message(STATUS "Modulefile install path: ${MOD_INSTALL_PATH}")
|
||||
|
||||
set(moduleFileTemplate "omniperf.lua.in")
|
||||
|
||||
configure_file(
|
||||
${PROJECT_SOURCE_DIR}/cmake/${moduleFileTemplate}
|
||||
${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/modulefiles/${PROJECT_NAME}/${PROJECT_VERSION}.lua
|
||||
@ONLY)
|
||||
|
||||
# Crusher mods
|
||||
if(LOCALHOST MATCHES ".*\.crusher\.olcf\.ornl\.gov")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
message(STATUS "Using crusher-specific modulefile modification")
|
||||
file(READ ${PROJECT_SOURCE_DIR}/cmake/modfile.crusher.mod mod_additions)
|
||||
file(
|
||||
APPEND
|
||||
${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/modulefiles/${PROJECT_NAME}/${PROJECT_VERSION}.lua
|
||||
${mod_additions})
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
endif()
|
||||
|
||||
# Thera mods
|
||||
if(LOCALHOST MATCHES ".*\.thera\.amd\.com")
|
||||
list(APPEND CMAKE_MESSAGE_INDENT " ")
|
||||
message(STATUS "Using thera-specific modulefile modification")
|
||||
file(READ ${PROJECT_SOURCE_DIR}/cmake/modfile.thera.mod mod_additions)
|
||||
file(
|
||||
APPEND
|
||||
${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/modulefiles/${PROJECT_NAME}/${PROJECT_VERSION}.lua
|
||||
${mod_additions})
|
||||
list(POP_BACK CMAKE_MESSAGE_INDENT)
|
||||
endif()
|
||||
|
||||
enable_testing()
|
||||
|
||||
add_test(
|
||||
NAME test_analyze_commands
|
||||
COMMAND ${Python3_EXECUTABLE} -m pytest
|
||||
${PROJECT_SOURCE_DIR}/tests/test_analyze_commands.py
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
add_test(
|
||||
NAME test_analyze_workloads
|
||||
COMMAND ${Python3_EXECUTABLE} -m pytest
|
||||
${PROJECT_SOURCE_DIR}/tests/test_analyze_workloads.py
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
add_test(
|
||||
NAME test_saved_analysis
|
||||
COMMAND ${Python3_EXECUTABLE} -m pytest
|
||||
${PROJECT_SOURCE_DIR}/tests/test_saved_analysis.py
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR})
|
||||
|
||||
# if(EXISTS ${PROJECT_SOURCE_DIR}/src/mibench/roofline/roofline.cpp) message(STATUS
|
||||
# "Enabling optional roofline binaries..") foreach(ROCM_VERSION ${ROCM_VERSIONS})
|
||||
# externalproject_add( roofline-rocm-${ROCM_VERSION} PREFIX
|
||||
# ${PROJECT_BINARY_DIR}/rocm-${ROCM_VERSION}/roofline SOURCE_DIR
|
||||
# ${PROJECT_SOURCE_DIR}/src/mibench/roofline BINARY_DIR
|
||||
# ${PROJECT_BINARY_DIR}/rocm-${ROCM_VERSION}/roofline/build BUILD_BYPRODUCTS
|
||||
# <BINARY_DIR>/roofline CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
|
||||
# HIP_PATH=/opt/rocm-${ROCM_VERSION} ${CMAKE_COMMAND}
|
||||
# -DCMAKE_CXX_COMPILER=/opt/rocm-${ROCM_VERSION}/hip/bin/hipcc
|
||||
# -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -B <BINARY_DIR> <SOURCE_DIR> BUILD_COMMAND
|
||||
# ${CMAKE_COMMAND} --build <BINARY_DIR> --target all INSTALL_COMMAND "") #
|
||||
# add_dependencies(roofline-rocm-${ROCM_VERSION} run-pyinstaller) string(REGEX REPLACE
|
||||
# "([0-9]+)\.([0-9]+)\.([0-9]+)(.*)" "\\1.\\2" ROCM_BASIC_VERSION "${ROCM_VERSION}")
|
||||
# install( PROGRAMS ${PROJECT_BINARY_DIR}/rocm-${ROCM_VERSION}/roofline/build/roofline
|
||||
# DESTINATION ${CMAKE_INSTALL_BINDIR}/utils RENAME roofline-${ROCM_BASIC_VERSION})
|
||||
# endforeach() else() message(STATUS "Optional roofline package not found") endif()
|
||||
|
||||
# ---------
|
||||
# Install
|
||||
# ---------
|
||||
|
||||
# top-level driver and associated .py files
|
||||
install(PROGRAMS src/omniperf TYPE BIN)
|
||||
install(FILES src/parser.py TYPE BIN)
|
||||
install(FILES src/common.py TYPE BIN)
|
||||
install(FILES VERSION DESTINATION ${CMAKE_INSTALL_PREFIX})
|
||||
# src/omniperf_cli
|
||||
install(
|
||||
DIRECTORY src/omniperf_cli
|
||||
TYPE BIN
|
||||
PATTERN src/omniperf_cli/tests EXCLUDE
|
||||
PATTERN "__pycache__" EXCLUDE)
|
||||
# src/utils
|
||||
install(
|
||||
DIRECTORY src/utils
|
||||
TYPE BIN
|
||||
PATTERN "rooflines*" EXCLUDE)
|
||||
# src/utils/rooflines
|
||||
install(PROGRAMS src/utils/rooflines/roofline-rhel8-mi200-rocm5
|
||||
src/utils/rooflines/roofline-sle15sp3-mi200-rocm5
|
||||
DESTINATION ${CMAKE_INSTALL_BINDIR}/utils/rooflines)
|
||||
# src/perfmon_pub
|
||||
install(
|
||||
DIRECTORY src/perfmon_pub
|
||||
TYPE BIN
|
||||
PATTERN "__pycache__" EXCLUDE)
|
||||
|
||||
# src/soc_params
|
||||
install(
|
||||
DIRECTORY src/soc_params
|
||||
TYPE BIN
|
||||
PATTERN "__pycache__" EXCLUDE)
|
||||
|
||||
# samples
|
||||
install(DIRECTORY sample TYPE DATA)
|
||||
|
||||
# modulefile
|
||||
install(
|
||||
FILES
|
||||
${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_DATAROOTDIR}/modulefiles/${PROJECT_NAME}/${PROJECT_VERSION}.lua
|
||||
DESTINATION ${MOD_INSTALL_PATH}/${PROJECT_NAME})
|
||||
|
||||
# install(PROGRAMS src/omniperf.py DESTINATION ${OMNIPERF_PYINSTALLER_PATH})
|
||||
# set(OMNIPERF_BUILD_ARGS "-y --workpath=${PROJECT_BINARY_DIR}/pyinstaller
|
||||
# --distpath=${PROJECT_BINARY_DIR}/dist" CACHE STRING "Arguments for build.sh" FORCE)
|
||||
#
|
||||
# string(REPLACE " " ";" _OMNIPERF_BUILD_ARGS "${OMNIPERF_BUILD_ARGS}")
|
||||
#
|
||||
# add_custom_target( run-pyinstaller COMMAND ${PROJECT_SOURCE_DIR}/build.sh
|
||||
# ${_OMNIPERF_BUILD_ARGS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} BYPRODUCTS
|
||||
# ${PROJECT_BINARY_DIR}/dist COMMENT "Running pyinstaller")
|
||||
#
|
||||
# set(OMNIPERF_PYINSTALLER_PATH
|
||||
# "${CMAKE_INSTALL_LIBDIR}/python${PYTHON_VERSION}/site-packages/omniperf" CACHE STRING
|
||||
# "Relative installation directory for omniperf")
|
||||
#
|
||||
# install( DIRECTORY ${PROJECT_BINARY_DIR}/dist/omniperf/ DESTINATION
|
||||
# ${OMNIPERF_PYINSTALLER_PATH} USE_SOURCE_PERMISSIONS COMPONENT pyinstaller PATTERN ".a"
|
||||
# EXCLUDE PATTERN ".git" EXCLUDE PATTERN "utils/roofline" EXCLUDE)
|
||||
#
|
||||
# install(FILES ${PROJECT_SOURCE_DIR}/VERSION DESTINATION
|
||||
# ${OMNIPERF_PYINSTALLER_PATH}/../)
|
||||
#
|
||||
# file(RELATIVE_PATH OMNIPERF_RELATIVE_PATH
|
||||
# "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}"
|
||||
# "${CMAKE_INSTALL_PREFIX}/${OMNIPERF_PYINSTALLER_PATH}")
|
||||
#
|
||||
# configure_file(${PROJECT_SOURCE_DIR}/cmake/omniperf.sh.in
|
||||
# ${PROJECT_BINARY_DIR}/omniperf-sh @ONLY) install( PROGRAMS
|
||||
# ${PROJECT_BINARY_DIR}/omniperf-sh DESTINATION ${CMAKE_INSTALL_BINDIR} RENAME omniperf)
|
||||
|
||||
# Packaging directives set(CPACK_GENERATOR "DEB" "RPM" "TGZ") set(CPACK_PACKAGE_NAME
|
||||
# "omniperf") set(CPACK_PACKAGE_VENDOR "AMD") set(CPACK_PACKAGE_VERSION_MAJOR
|
||||
# ${PROJECT_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||
# set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}${OMNIPERF_VERSION_TWEAK})
|
||||
# set(CPACK_PACKAGE_CONTACT "Advanced Micro Devices Inc.")
|
||||
# set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "omniperf") set(CPACK_RESOURCE_FILE_LICENSE
|
||||
# "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
|
||||
|
||||
# Source tarball
|
||||
set(CPACK_SOURCE_GENERATOR "TGZ")
|
||||
set(CPACK_SOURCE_PACKAGE_FILE_NAME ${CMAKE_PROJECT_NAME}-${PROJECT_VERSION})
|
||||
set(CPACK_SOURCE_IGNORE_FILES ".*~$" \.git/ \.github /tests /build)
|
||||
include(CPack)
|
||||
@@ -0,0 +1,59 @@
|
||||
## How to fork from us
|
||||
|
||||
To keep our development fast and conflict free, we recommend you to [fork](https://github.com/AMDResearch/omniperf/fork) our repository and start your work from our `develop` branch in your private repository.
|
||||
|
||||
Afterwards, git clone your repository to your local machine. But that is not it! To keep track of the original develop repository, add it as another remote.
|
||||
|
||||
```
|
||||
git remote add mainline https://github.com/AMDResearch/omniperf.git
|
||||
git checkout develop
|
||||
```
|
||||
|
||||
As always in git, start a new branch with
|
||||
|
||||
```
|
||||
git checkout -b topic-<yourFeatureName>
|
||||
```
|
||||
|
||||
and apply your changes there.
|
||||
|
||||
## How to contribute to Omniperf
|
||||
|
||||
### Did you find a bug?
|
||||
|
||||
- Ensure the bug was not already reported by searching on GitHub under [Issues](https://github.com/AMDResearch/omniperf/issues).
|
||||
|
||||
- If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/AMDResearch/omniperf/issues/new).
|
||||
|
||||
### Did you write a patch that fixes a bug?
|
||||
|
||||
- Open a new GitHub [pull request](https://github.com/AMDResearch/omniperf/compare) with the patch.
|
||||
|
||||
- Ensure the PR description clearly describes the problem and solution. If there is an existing GitHub issue open describing this bug, please include it in the description so we can close it.
|
||||
|
||||
- Ensure the PR is based on the `develop` branch of the Omniperf GitHub repository.
|
||||
|
||||
- Omniperf requires new commits to include a "Signed-off-by" token in the commit message (typically enabled via the `git commit -s` option), indicating your agreement to the projects's [Developer's Certificate of Origin](https://developercertificate.org/) and compatability with the project [LICENSE](https://github.com/AMDResearch/omniperf/blob/main/LICENSE):
|
||||
|
||||
|
||||
> (a) The contribution was created in whole or in part by me and I
|
||||
> have the right to submit it under the open source license
|
||||
> indicated in the file; or
|
||||
>
|
||||
> (b) The contribution is based upon previous work that, to the best
|
||||
> of my knowledge, is covered under an appropriate open source
|
||||
> license and I have the right under that license to submit that
|
||||
> work with modifications, whether created in whole or in part
|
||||
> by me, under the same open source license (unless I am
|
||||
> permitted to submit under a different license), as indicated
|
||||
> in the file; or
|
||||
>
|
||||
> (c) The contribution was provided directly to me by some other
|
||||
> person who certified (a), (b) or (c) and I have not modified
|
||||
> it.
|
||||
>
|
||||
> (d) I understand and agree that this project and the contribution
|
||||
> are public and that a record of the contribution (including all
|
||||
> personal information I submit with it, including my sign-off) is
|
||||
> maintained indefinitely and may be redistributed consistent with
|
||||
> this project or the open source license(s) involved.
|
||||
@@ -0,0 +1,67 @@
|
||||
# -----------------------------------------------------------------------
|
||||
# NOTE:
|
||||
# Dependencies are not included as part of Omniperf.
|
||||
# It's the user's responsibility to accept any licensing implications
|
||||
# before building the project
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
FROM ubuntu:20.04
|
||||
WORKDIR /app
|
||||
|
||||
USER root
|
||||
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
ENV TZ "US/Chicago"
|
||||
|
||||
ADD grafana_plugins/svg_plugin /var/lib/grafana/plugins/custom-svg
|
||||
ADD grafana_plugins/omniperfData_plugin /var/lib/grafana/plugins/omniperfData_plugin
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y apt-transport-https software-properties-common adduser libfontconfig1 wget curl && \
|
||||
wget https://dl.grafana.com/enterprise/release/grafana-enterprise_8.3.4_amd64.deb &&\
|
||||
dpkg -i grafana-enterprise_8.3.4_amd64.deb &&\
|
||||
echo "deb https://packages.grafana.com/enterprise/deb stable main" | tee -a /etc/apt/sources.list.d/grafana.list && \
|
||||
echo "deb [signed-by=/usr/share/keyrings/yarnkey.gpg] https://dl.yarnpkg.com/debian stable main" | tee /etc/apt/sources.list.d/yarn.list && \
|
||||
apt-get install gnupg && \
|
||||
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc -O server-5.0.asc &&\
|
||||
apt-key add server-5.0.asc && \
|
||||
echo "deb [trusted=yes arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org.list && \
|
||||
wget -q -O - https://packages.grafana.com/gpg.key | apt-key add - && \
|
||||
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | gpg --dearmor | tee /usr/share/keyrings/yarnkey.gpg > /dev/null && \
|
||||
apt-get update && \
|
||||
apt-get install -y mongodb-org && \
|
||||
apt-get install -y tzdata systemd apt-utils npm vim net-tools && \
|
||||
mkdir -p /nonexistent && \
|
||||
/usr/sbin/grafana-cli plugins install michaeldmoore-multistat-panel && \
|
||||
/usr/sbin/grafana-cli plugins install ae3e-plotly-panel && \
|
||||
/usr/sbin/grafana-cli plugins install natel-plotly-panel && \
|
||||
/usr/sbin/grafana-cli plugins install grafana-image-renderer && \
|
||||
curl -fsSL https://deb.nodesource.com/setup_16.x | bash - && \
|
||||
apt-get install -y yarn nodejs && \
|
||||
chown root:grafana /etc/grafana && \
|
||||
cd /var/lib/grafana/plugins/omniperfData_plugin && \
|
||||
npm install && \
|
||||
npm run build && \
|
||||
apt-get autoremove -y && \
|
||||
apt-get autoclean -y && \
|
||||
cd /var/lib/grafana/plugins/custom-svg && \
|
||||
yarn install && \
|
||||
yarn build && \
|
||||
yarn autoclean && \
|
||||
sed -i "s/ bindIp.*/ bindIp: 0.0.0.0/" /etc/mongod.conf && \
|
||||
mkdir -p /var/lib/grafana && \
|
||||
touch /var/lib/grafana/grafana.lib && \
|
||||
chown grafana:grafana /var/lib/grafana/grafana.lib && \
|
||||
rm /app/grafana-enterprise_8.3.4_amd64.deb /app/server-5.0.asc
|
||||
|
||||
# Overwrite grafana ini file
|
||||
COPY docker/grafana.ini /etc/grafana
|
||||
|
||||
# switch Grafana port to 4000
|
||||
RUN sed -i "s/^;http_port = 3000/http_port = 4000/" /etc/grafana/grafana.ini && \
|
||||
sed -i "s/^http_port = 3000/http_port = 4000/" /usr/share/grafana/conf/defaults.ini
|
||||
|
||||
# starts mongo and grafana-server at startup
|
||||
COPY docker/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
|
||||
ENTRYPOINT [ "/docker-entrypoint.sh" ]
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Advanced Micro Devices, Inc. All Rights Reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,36 @@
|
||||
<!-- [](https://amdresearch.github.io/omniperf/) -->
|
||||
|
||||
<!-- [](https://github.com/AMDResearch/omniperf/compare/main...dev) -->
|
||||
|
||||
# Omniperf
|
||||
|
||||
## General
|
||||
Omniperf is a system performance profiling tool for machine
|
||||
learning/HPC workloads running on AMD MI GPUs. The tool presently
|
||||
targets usage on MI100 and MI200 accelerators.
|
||||
|
||||
* For more information on available features, installation steps, and
|
||||
workload profiling and analysis, please refer to the online
|
||||
[documentation](https://amdresearch.github.io/omniperf).
|
||||
|
||||
* Omniperf is an AMD open source research project and is not supported
|
||||
as part of the ROCm software stack. We welcome contributions and
|
||||
feedback from the community. Please see the
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) file for additional details on our
|
||||
contribution process.
|
||||
|
||||
* Licensing information can be found in the [LICENSE](LICENSE) file.
|
||||
|
||||
## Development
|
||||
|
||||
Omniperf follows a
|
||||
[main-dev](https://nvie.com/posts/a-successful-git-branching-model/)
|
||||
branching model. As a result, our latest stable release is shipped
|
||||
from the `main` branch, while new features are developed in our
|
||||
`dev` branch.
|
||||
|
||||
Before publishing a new release, we'll open a new `release-*` branch
|
||||
from `dev` with `*` being the version number of the upcoming
|
||||
release. This branch will only receive bug fixes and users may
|
||||
checkout to preview upcoming features.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1.0.3
|
||||
@@ -0,0 +1,66 @@
|
||||
################################################################################
|
||||
# Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
################################################################################
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
USER root
|
||||
|
||||
COPY rocm_install.sh /omniperf/rocm_install.sh
|
||||
|
||||
ENV PATH="/omniperf:${PATH}"
|
||||
ENV TZ="US/Chicago"
|
||||
ENV DEBIAN_FRONTEND noninteractive
|
||||
|
||||
#pyenv dependencies
|
||||
RUN apt update && \
|
||||
apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev xz-utils tk-dev libffi-dev liblzma-dev python-openssl
|
||||
ENV HOME="/omniperf"
|
||||
WORKDIR $HOME
|
||||
ENV PYENV_ROOT="$HOME/.pyenv"
|
||||
ENV PATH="$PYENV_ROOT/bin:$PATH"
|
||||
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone && \
|
||||
apt update && \
|
||||
apt-get install -y cmake wget git python3-dev rpm python3-venv software-properties-common &&\
|
||||
add-apt-repository ppa:deadsnakes/ppa -y &&\
|
||||
apt install python3.7 -y libpython3.7-dev python3.7-venv libnuma-dev &&\
|
||||
curl https://pyenv.run | bash
|
||||
|
||||
RUN echo "export PATH=$HOME/.pyenv/bin:$PATH" >> ~/.bashrc &&\
|
||||
echo eval "$(pyenv init -)" >> ~/.bashrc &&\
|
||||
echo eval "$(pyenv virtualenv-init -)" >> ~/.bashrc &&\
|
||||
CPPFLAGS=-I/usr/bin/openssl \
|
||||
LDFLAGS=-L/usr/lib64 \
|
||||
CONFIGURE_OPTS=--enable-shared pyenv install -v 3.8.12 &&\
|
||||
pyenv global 3.8.12 &&\
|
||||
apt-get install -y python3-pip
|
||||
#clang?
|
||||
|
||||
RUN python3 -m pip install astunparse==1.6.2 colorlover dash matplotlib numpy pandas pymongo pyyaml tabulate tqdm dash-svg pyinstaller dash-bootstrap-components &&\
|
||||
python3 -m pip install 'cmake==3.21.4' && \
|
||||
./rocm_install.sh &&\
|
||||
#wget -q -O - https://repo.radeon.com/rocm/rocm.gpg.key | apt-key add - && \
|
||||
#echo "deb [arch=amd64] https://repo.radeon.com/rocm/apt/${ROCM_REPO_VERSION}/ ${ROCM_REPO_DIST} main" | tee /etc/apt/sources.list.d/rocm.list && \
|
||||
apt-get update && \
|
||||
apt-get dist-upgrade -y && \
|
||||
#apt-get install -y rocm-dev rocm-utils rocm-smi-lib roctracer-dev rocprofiler-dev rccl-dev hip-base hsa-amd-aqlprofile hsa-rocr-dev hsakmt-roct-dev ${EXTRA_PACKAGES} && \
|
||||
apt-get autoclean
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
################################################################################
|
||||
# Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
################################################################################
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR=$(realpath $(dirname ${BASH_SOURCE[0]}))
|
||||
|
||||
OMNIPERF_DIR=$(realpath ${SCRIPT_DIR}/@OMNIPERF_RELATIVE_PATH@)
|
||||
|
||||
if [ ! -f ${OMNIPERF_DIR}/omniperf ]; then
|
||||
echo -e "Error! Expected omniperf installation in ${OMNIPERF_DIR}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
eval ${OMNIPERF_DIR}/omniperf "${@}"
|
||||
@@ -0,0 +1,4 @@
|
||||
-- Crusher-specific additions
|
||||
depends_on "cray-python"
|
||||
depends_on "rocm"
|
||||
prereq(atleast("rocm","5.1.0"))
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Thera-specific additions
|
||||
depends_on "python"
|
||||
depends_on "rocm"
|
||||
prereq(atleast("rocm","5.1.0"))
|
||||
local home = os.getenv("HOME")
|
||||
setenv("MPLCONFIGDIR",pathJoin(home,".matplotlib"))
|
||||
@@ -0,0 +1,32 @@
|
||||
local help_message = [[
|
||||
|
||||
Omniperf is an open-source performance analysis tool for profiling
|
||||
machine learning/HPC workloads running on AMD MI GPUs.
|
||||
|
||||
Version @PROJECT_VERSION@
|
||||
]]
|
||||
|
||||
help(help_message,"\n")
|
||||
|
||||
whatis("Name: omniperf")
|
||||
whatis("Version: @PROJECT_VERSION@")
|
||||
whatis("Keywords: Profiling, Performance, GPU")
|
||||
whatis("Description: tool for GPU performance profiling")
|
||||
whatis("URL: https://github.com/AMDResearch/omniperf")
|
||||
|
||||
-- Export environmental variables
|
||||
local topDir="@CMAKE_INSTALL_PREFIX@"
|
||||
local binDir="@CMAKE_INSTALL_FULL_BINDIR@"
|
||||
local shareDir="@CMAKE_INSTALL_FULL_DATADIR@"
|
||||
local pythonDeps="@PYTHON_DEPS@"
|
||||
|
||||
setenv("OMNIPERF_DIR",topDir)
|
||||
setenv("OMNIPERF_BIN",binDir)
|
||||
setenv("OMNIPERF_SHARE",shareDir)
|
||||
|
||||
-- Update relevant PATH variables
|
||||
prepend_path("PATH",binDir)
|
||||
if ( pythonDeps ~= "" ) then
|
||||
prepend_path("PYTHONPATH",pythonDeps)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
################################################################################
|
||||
# Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
################################################################################
|
||||
|
||||
declare -a rocm_versions=("4.3.1" "4.5.2" "5.0.2" "5.1.3" "5.2.3")
|
||||
wget https://repo.radeon.com/amdgpu-install/22.10/ubuntu/focal/amdgpu-install_22.10.50100-1_all.deb
|
||||
apt-get install -y ./amdgpu-install_22.10.50100-1_all.deb
|
||||
for rocm_version in ${rocm_versions[@]}; do
|
||||
echo "deb [arch=amd64] https://repo.radeon.com/rocm/apt/$rocm_version ubuntu main" | tee /etc/apt/sources.list.d/rocm.list
|
||||
apt update
|
||||
amdgpu-install -y --usecase=rocm --rocmrelease=$rocm_version --no-dkms
|
||||
done
|
||||
@@ -0,0 +1,39 @@
|
||||
# -----------------------------------------------------------------------
|
||||
# NOTE:
|
||||
# Dependencies are not included as part of Omniperf.
|
||||
# It's the user's responsibility to accept any licensing implications
|
||||
# before building the project
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
version: "3.3"
|
||||
|
||||
services:
|
||||
web:
|
||||
image: omniperf-grafana-v1.0
|
||||
container_name: omniperf-grafana-v1.0
|
||||
restart: always
|
||||
build: .
|
||||
ports:
|
||||
- "14000:4000"
|
||||
volumes:
|
||||
- grafana-storage:/var/lib/grafana
|
||||
stdin_open: true
|
||||
tty: true
|
||||
db_mongo:
|
||||
container_name: mongo
|
||||
image: mongo
|
||||
restart: always
|
||||
environment:
|
||||
MONGO_INITDB_ROOT_USERNAME: amd
|
||||
MONGO_INITDB_ROOT_PASSWORD: amd123
|
||||
volumes:
|
||||
- grafana-mongo-db:/data/db
|
||||
ports:
|
||||
- "27018:27017"
|
||||
command: mongod --bind_ip 0.0.0.0
|
||||
|
||||
volumes:
|
||||
grafana-mongo-db:
|
||||
external: true
|
||||
grafana-storage:
|
||||
external: true
|
||||
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
|
||||
################################################################################
|
||||
# Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
################################################################################
|
||||
|
||||
pushd /var/lib/grafana/plugins/omniperfData_plugin
|
||||
npm run server &
|
||||
popd
|
||||
|
||||
service grafana-server start
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
exec bash
|
||||
else
|
||||
eval $@
|
||||
fi
|
||||
@@ -0,0 +1,7 @@
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"esnext": true,
|
||||
"disallowImplicitTypeConversion": ["string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"requireSpacesInFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"disallowSpacesInsideArrayBrackets": true,
|
||||
"disallowSpacesInsideParentheses": true,
|
||||
"validateIndentation": 2
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"useTabs": true
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 (Unreleased)
|
||||
|
||||
Initial release.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 JamesOsgood
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Omniperf Data Source Plugin
|
||||
|
||||
This plugin allows users of Omniperf to connect their MongoDB database to for visualization in Grafana
|
||||
|
||||
## Info
|
||||
|
||||
This backend exposes the endpoints Grafana requires to create a custom data source.
|
||||
|
||||
An express server, located in the `server` folder exposes these endpoints and makes the proxy to the MongoDB.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Node.js
|
||||
|
||||
`sudo apt-get install -y npm`
|
||||
|
||||
## Useage
|
||||
|
||||
<img src="src/img/sample.PNG" alt="Sample Data Source" style="width: 500px;"/>
|
||||
@@ -0,0 +1,22 @@
|
||||
const { src, dest, series } = require('gulp');
|
||||
const babel = require('gulp-babel');
|
||||
|
||||
function script() {
|
||||
return src('src/**/*.js')
|
||||
.pipe(
|
||||
babel({
|
||||
presets: ['es2015'],
|
||||
plugins: [
|
||||
'transform-es2015-modules-systemjs',
|
||||
'transform-es2015-for-of',
|
||||
],
|
||||
})
|
||||
)
|
||||
.pipe(dest('dist'));
|
||||
}
|
||||
|
||||
function srcToDist() {
|
||||
return src(['src/**/*', '!src/**/*.js']).pipe(dest('dist'));
|
||||
}
|
||||
|
||||
exports.default = series(script, srcToDist);
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "amd-omniperf-data-plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "server/mongo-proxy.js",
|
||||
"scripts": {
|
||||
"build": "gulp",
|
||||
"test": "echo 'No test found'",
|
||||
"server": "cd server && node mongo-proxy.js"
|
||||
},
|
||||
"author": "Audacious Software Group",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-plugin-transform-es2015-for-of": "^6.23.0",
|
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.24.1",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"chai": "~4.2.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-babel": "^7.0.1",
|
||||
"jsdom": "~16.2.2",
|
||||
"prunk": "^1.3.1",
|
||||
"q": "^1.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"body-parser": "^1.19.0",
|
||||
"config": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
"fs": "0.0.1-security",
|
||||
"lodash": "^4.17.15",
|
||||
"mocha": "^7.1.2",
|
||||
"moment": "^2.24.0",
|
||||
"mongodb": "^3.5.7",
|
||||
"statman-stopwatch": "^2.11.1"
|
||||
},
|
||||
"_comments": "Dependencies are not included as part of Omniperf. It's the user's responsibility to accept any licensing implications before building the project."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"server":
|
||||
{
|
||||
"port": 3333,
|
||||
"logRequests": false,
|
||||
"logQueries": false,
|
||||
"logTimings": false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,647 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2018 JamesOsgood
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
// #
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
var _ = require('lodash');
|
||||
var app = express();
|
||||
const MongoClient = require('mongodb').MongoClient;
|
||||
const assert = require('assert');
|
||||
var config = require('config');
|
||||
var Stopwatch = require("statman-stopwatch");
|
||||
var moment = require('moment')
|
||||
const serverConfig = require('./config/default.json').server;
|
||||
|
||||
app.use(bodyParser.json({ limit: '5mb' }));
|
||||
|
||||
// Called via required 'testDataSource'
|
||||
app.all('/', function(req, res, next)
|
||||
{
|
||||
console.log("Entered /")
|
||||
logRequest(req.body, "/")
|
||||
setCORSHeaders(res);
|
||||
|
||||
MongoClient.connect(req.body.db.url, function(err, client)
|
||||
{
|
||||
if ( err != null )
|
||||
{
|
||||
res.send({ status : "error",
|
||||
display_status : "Error",
|
||||
message : 'MongoDB Connection Error: ' + err.message });
|
||||
}
|
||||
else
|
||||
{
|
||||
res.send( { status : "success",
|
||||
display_status : "Success",
|
||||
message : 'MongoDB Connection test OK' });
|
||||
}
|
||||
next()
|
||||
})
|
||||
});
|
||||
|
||||
// Called by template functions and to look up variables
|
||||
app.all('/search', function(req, res, next)
|
||||
{
|
||||
console.log("Entered /search")
|
||||
logRequest(req.body, "/search")
|
||||
setCORSHeaders(res);
|
||||
|
||||
// Generate an id to track requests
|
||||
const requestId = ++requestIdCounter
|
||||
// Add state for the queries in this request
|
||||
var queryStates = []
|
||||
requestsPending[requestId] = queryStates
|
||||
// Parse query string in target
|
||||
queryArgs = parseQuery(req.body.target, {})
|
||||
if (queryArgs.err != null)
|
||||
{
|
||||
queryError(requestId, queryArgs.err, next)
|
||||
}
|
||||
else
|
||||
{
|
||||
doTemplateQuery(requestId, queryArgs, req.body.db, res, next);
|
||||
}
|
||||
});
|
||||
|
||||
// State for queries in flight. As results come it, acts as a semaphore and sends the results back
|
||||
var requestIdCounter = 0
|
||||
// Map of request id -> array of results. Results is
|
||||
// { query, err, output }
|
||||
var requestsPending = {}
|
||||
|
||||
// Called when a query finishes with an error
|
||||
function queryError(requestId, err, next)
|
||||
{
|
||||
// We only 1 return error per query so it may have been removed from the list
|
||||
if ( requestId in requestsPending )
|
||||
{
|
||||
// Remove request
|
||||
delete requestsPending[requestId]
|
||||
// Send back error
|
||||
next(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Called when query finished
|
||||
function queryFinished(requestId, queryId, results, res, next)
|
||||
{
|
||||
// We only 1 return error per query so it may have been removed from the list
|
||||
if ( requestId in requestsPending )
|
||||
{
|
||||
var queryStatus = requestsPending[requestId]
|
||||
// Mark this as finished
|
||||
queryStatus[queryId].pending = false
|
||||
queryStatus[queryId].results = results
|
||||
|
||||
// See if we're all done
|
||||
var done = true
|
||||
for ( var i = 0; i < queryStatus.length; i++)
|
||||
{
|
||||
if (queryStatus[i].pending == true )
|
||||
{
|
||||
done = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If query done, send back results
|
||||
if (done)
|
||||
{
|
||||
// Concatenate results
|
||||
output = []
|
||||
for ( var i = 0; i < queryStatus.length; i++)
|
||||
{
|
||||
var queryResults = queryStatus[i].results
|
||||
var keys = Object.keys(queryResults)
|
||||
for (var k = 0; k < keys.length; k++)
|
||||
{
|
||||
var tg = keys[k]
|
||||
output.push(queryResults[tg])
|
||||
}
|
||||
}
|
||||
res.json(output);
|
||||
next()
|
||||
// Remove request
|
||||
delete requestsPending[requestId]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called to get graph points
|
||||
app.all('/query', function(req, res, next)
|
||||
{
|
||||
console.log("Entered /query")
|
||||
logRequest(req.body, "/query")
|
||||
setCORSHeaders(res);
|
||||
|
||||
// Parse query string in target
|
||||
substitutions = { "$from" : new Date(req.body.range.from),
|
||||
"$to" : new Date(req.body.range.to),
|
||||
"$dateBucketCount" : getBucketCount(req.body.range.from, req.body.range.to, req.body.intervalMs)
|
||||
}
|
||||
|
||||
// Generate an id to track requests
|
||||
const requestId = ++requestIdCounter
|
||||
// Add state for the queries in this request
|
||||
var queryStates = []
|
||||
requestsPending[requestId] = queryStates
|
||||
var error = false
|
||||
|
||||
for ( var queryId = 0; queryId < req.body.targets.length && !error; queryId++)
|
||||
{
|
||||
tg = req.body.targets[queryId]
|
||||
queryArgs = parseQuery(tg.target, substitutions)
|
||||
queryArgs.type = tg.type
|
||||
// If we picked up any errors while parsing query
|
||||
if (queryArgs.err != null)
|
||||
{
|
||||
queryError(requestId, queryArgs.err, next)
|
||||
error = true
|
||||
}
|
||||
else
|
||||
{
|
||||
// Add to the state
|
||||
queryStates.push( { pending : true } )
|
||||
|
||||
// Run the query
|
||||
runAggregateQuery( requestId, queryId, req.body, queryArgs, res, next)
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
app.use(function(error, req, res, next)
|
||||
{
|
||||
// Any request to this server will get here, and will send an HTTP
|
||||
// response with the error message
|
||||
res.status(500).json({ message: error.message });
|
||||
});
|
||||
|
||||
app.listen(serverConfig.port);
|
||||
|
||||
console.log("Server is listening on port " + serverConfig.port);
|
||||
|
||||
function setCORSHeaders(res)
|
||||
{
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "POST");
|
||||
res.setHeader("Access-Control-Allow-Headers", "accept, content-type");
|
||||
}
|
||||
|
||||
function forIn(obj, processFunc)
|
||||
{
|
||||
var key;
|
||||
for (key in obj)
|
||||
{
|
||||
var value = obj[key]
|
||||
processFunc(obj, key, value)
|
||||
if ( value != null && typeof(value) == "object")
|
||||
{
|
||||
forIn(value, processFunc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseQuery(query, substitutions)
|
||||
{
|
||||
doc = {}
|
||||
docs = []
|
||||
queryErrors = []
|
||||
|
||||
chosenDB = null
|
||||
|
||||
query = query.trim() // Gets rid of any leading or trailing space
|
||||
// TODO: Add a case to catch custom db name
|
||||
// if (query.substring(0,3) != "db.")
|
||||
// {
|
||||
// queryErrors.push("Query must start with db.")
|
||||
// return null
|
||||
// }
|
||||
var firstDotIndex = query.indexOf('.')
|
||||
if (query.substring(0,3) != "db.")
|
||||
{
|
||||
if(firstDotIndex == -1){
|
||||
queryErrors.push("Invalid database name format")
|
||||
return null
|
||||
}
|
||||
chosenDB = query.substring(0, firstDotIndex)
|
||||
}
|
||||
doc.db = chosenDB
|
||||
|
||||
|
||||
// Query is of the form db.<collection>.aggregate or db.<collection>.find
|
||||
// Split on the first ( after db.
|
||||
var openBracketIndex = query.indexOf('(', firstDotIndex)
|
||||
|
||||
if (openBracketIndex == -1)
|
||||
{
|
||||
queryErrors.push("Can't find opening bracket")
|
||||
}
|
||||
else
|
||||
{
|
||||
// Split the first bit - it's the collection name and operation ( must be aggregate )
|
||||
var parts = query.substring(firstDotIndex+1, openBracketIndex).split('.')
|
||||
|
||||
// Collection names can have .s so last part is operation, rest is the collection name
|
||||
if (parts.length >= 2)
|
||||
{
|
||||
doc.operation = parts.pop().trim()
|
||||
doc.collection = parts.join('.')
|
||||
}
|
||||
else
|
||||
{
|
||||
queryErrors.push("Invalid collection and operation syntax")
|
||||
}
|
||||
|
||||
// Args is the rest up to the last bracket
|
||||
const potentialPipelineEnding = ['])', '],'];
|
||||
|
||||
const pipelineEnd = potentialPipelineEnding.find(end => query.indexOf(end, openBracketIndex) !== -1);
|
||||
const pipelineEndIndex = query.indexOf(pipelineEnd, openBracketIndex)
|
||||
|
||||
if (!pipelineEnd)
|
||||
{
|
||||
queryErrors.push("Can't find last bracket")
|
||||
}
|
||||
else
|
||||
{
|
||||
var args = query.substring(openBracketIndex + 1, pipelineEndIndex)
|
||||
if ( doc.operation == 'aggregate')
|
||||
{
|
||||
// Wrap args in array syntax so we can check for optional options arg
|
||||
args = '[' + args + ']]'
|
||||
docs = ((raw) => {
|
||||
try {
|
||||
const data = JSON.parse(raw);
|
||||
return data;
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
})(args);
|
||||
|
||||
if(!docs) {
|
||||
queryErrors.push("Invalid query syntax");
|
||||
}
|
||||
|
||||
// First Arg is pipeline
|
||||
doc.pipeline = docs[0]
|
||||
// If we have 2 top level args, second is agg options
|
||||
if ( docs.length == 2 )
|
||||
{
|
||||
doc.agg_options = docs[1]
|
||||
}
|
||||
// Replace with substitutions
|
||||
for ( var i = 0; i < doc.pipeline.length; i++)
|
||||
{
|
||||
var stage = doc.pipeline[i]
|
||||
forIn(stage, function (obj, key, value)
|
||||
{
|
||||
if ( typeof(value) == "string" )
|
||||
{
|
||||
if ( value.startsWith("&") )
|
||||
{
|
||||
obj[key] = "$"+value.substring(1) // ie "$from" --> new Date(req.body.range.from)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryErrors.push("Unknown operation " + doc.operation + ", only aggregate supported")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (queryErrors.length > 0 )
|
||||
{
|
||||
doc.err = new Error('Failed to parse query - ' + queryErrors.join(':'))
|
||||
}
|
||||
|
||||
// Checker for debugging
|
||||
if (chosenDB == null){
|
||||
console.log("Chosen DB is DEFAULT");
|
||||
}
|
||||
else{
|
||||
console.log("chosenDB is " + doc.db);
|
||||
}
|
||||
console.log("Collection is "+ doc.collection);
|
||||
|
||||
return doc
|
||||
}
|
||||
|
||||
// Run an aggregate query. Must return documents of the form
|
||||
// { value : 0.34334, ts : <epoch time in seconds> }
|
||||
|
||||
function runAggregateQuery( requestId, queryId, body, queryArgs, res, next )
|
||||
{
|
||||
MongoClient.connect(body.db.url, function(err, client)
|
||||
{
|
||||
if ( err != null )
|
||||
{
|
||||
queryError(requestId, err, next)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// TODO: Use the other db name if it was provided
|
||||
// var assignedName = null
|
||||
// if(queryArgs.db == null){
|
||||
// assignedName = body.db.db
|
||||
// }
|
||||
// else{
|
||||
// assignedName = queryArgs.db
|
||||
// }
|
||||
// console.log("Assigned DB name is " + assignedName)
|
||||
// const db = client.db(assignedName);
|
||||
var secondDb = client.db(queryArgs.db)
|
||||
|
||||
// Get the documents collection
|
||||
const collection = secondDb.collection(queryArgs.collection);
|
||||
logQuery(queryArgs.pipeline, queryArgs.agg_options)
|
||||
// Track how long it takes to complete query
|
||||
var stopwatch = new Stopwatch(true)
|
||||
|
||||
collection.aggregate(queryArgs.pipeline, {allowDiskUse:true}).toArray(function(err, docs)
|
||||
{
|
||||
if ( err != null )
|
||||
{
|
||||
client.close();
|
||||
queryError(requestId, err, next)
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var results = {}
|
||||
if ( queryArgs.type == 'timeserie' )
|
||||
{
|
||||
results = getTimeseriesResults(docs)
|
||||
}
|
||||
|
||||
// This is where omniperf will go for most results
|
||||
else
|
||||
{
|
||||
results = getTableResults(docs)
|
||||
}
|
||||
|
||||
client.close();
|
||||
var elapsedTimeMs = stopwatch.stop()
|
||||
logTiming(body, elapsedTimeMs)
|
||||
// Mark query as finished - will send back results when all queries finished
|
||||
queryFinished(requestId, queryId, results, res, next)
|
||||
}
|
||||
catch(err)
|
||||
{
|
||||
queryError(requestId, err, next)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function getTableResults(docs)
|
||||
{
|
||||
var columns = {}
|
||||
|
||||
// Build superset of columns
|
||||
for ( var i = 0; i < docs.length; i++)
|
||||
{
|
||||
var doc = docs[i]
|
||||
// Go through all properties
|
||||
for (var propName in doc )
|
||||
{
|
||||
// See if we need to add a new column
|
||||
if ( !(propName in columns) )
|
||||
{
|
||||
columns[propName] =
|
||||
{
|
||||
text : propName,
|
||||
type : "text"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build return rows
|
||||
rows = []
|
||||
for ( var i = 0; i < docs.length; i++)
|
||||
{
|
||||
var doc = docs[i]
|
||||
row = []
|
||||
// All cols
|
||||
for ( var colName in columns )
|
||||
{
|
||||
var col = columns[colName]
|
||||
if ( col.text in doc )
|
||||
{
|
||||
row.push(doc[col.text])
|
||||
}
|
||||
else
|
||||
{
|
||||
row.push(null)
|
||||
}
|
||||
}
|
||||
rows.push(row)
|
||||
}
|
||||
|
||||
var results = {}
|
||||
results["table"] = {
|
||||
columns : Object.values(columns),
|
||||
rows : rows,
|
||||
type : "table"
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
function getTimeseriesResults(docs)
|
||||
{
|
||||
var results = {}
|
||||
for ( var i = 0; i < docs.length; i++)
|
||||
{
|
||||
var doc = docs[i]
|
||||
var tg = doc.name
|
||||
var dp = null
|
||||
if (tg in results)
|
||||
{
|
||||
dp = results[tg]
|
||||
}
|
||||
else
|
||||
{
|
||||
dp = { 'target' : tg, 'datapoints' : [] }
|
||||
results[tg] = dp
|
||||
}
|
||||
|
||||
results[tg].datapoints.push([doc['value'], doc['ts'].getTime()])
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// Runs a query to support templates. Must return documents of the form
|
||||
// { _id : <id> }
|
||||
function doTemplateQuery(requestId, queryArgs, db, res, next)
|
||||
{
|
||||
if ( queryArgs.err == null)
|
||||
{
|
||||
if(queryArgs.db != null){ //We're looking at a custom dbName.aggregate
|
||||
|
||||
// Use connect method to connect to the server
|
||||
MongoClient.connect(db.url, function(err, client)
|
||||
{
|
||||
if ( err != null )
|
||||
{
|
||||
queryError(requestId, err, next )
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove request from list
|
||||
if ( requestId in requestsPending )
|
||||
{
|
||||
delete requestsPending[requestId]
|
||||
}
|
||||
var secondDb = client.db(queryArgs.db);
|
||||
// Get the documents collection
|
||||
const collection = secondDb.collection(queryArgs.collection);
|
||||
|
||||
collection.aggregate(queryArgs.pipeline).toArray(function(err, result)
|
||||
{
|
||||
assert.equal(err, null)
|
||||
|
||||
output = []
|
||||
for ( var i = 0; i < result.length; i++)
|
||||
{
|
||||
var doc = result[i]
|
||||
output.push(doc["_id"])
|
||||
}
|
||||
res.json(output);
|
||||
client.close()
|
||||
next()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
else{ //Then we're just looking at the default db.aggregate
|
||||
// Database Name
|
||||
const dbName = db.db
|
||||
|
||||
// Use connect method to connect to the server
|
||||
MongoClient.connect(db.url, function(err, client)
|
||||
{
|
||||
if ( err != null )
|
||||
{
|
||||
queryError(requestId, err, next )
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove request from list
|
||||
if ( requestId in requestsPending )
|
||||
{
|
||||
delete requestsPending[requestId]
|
||||
}
|
||||
const db = client.db(dbName);
|
||||
// Get the documents collection
|
||||
const collection = db.collection(queryArgs.collection);
|
||||
|
||||
collection.aggregate(queryArgs.pipeline).toArray(function(err, result)
|
||||
{
|
||||
assert.equal(err, null)
|
||||
|
||||
output = []
|
||||
for ( var i = 0; i < result.length; i++)
|
||||
{
|
||||
var doc = result[i]
|
||||
output.push(doc["_id"])
|
||||
}
|
||||
res.json(output);
|
||||
client.close()
|
||||
next()
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
next(queryArgs.err)
|
||||
}
|
||||
}
|
||||
|
||||
function logRequest(body, type)
|
||||
{
|
||||
if (serverConfig.logRequests)
|
||||
{
|
||||
console.log("REQUEST: " + type + ":\n" + JSON.stringify(body,null,2))
|
||||
}
|
||||
}
|
||||
|
||||
function logQuery(query, options)
|
||||
{
|
||||
if (serverConfig.logQueries)
|
||||
{
|
||||
console.log("Query:")
|
||||
console.log(JSON.stringify(query,null,2))
|
||||
if ( options != null )
|
||||
{
|
||||
console.log("Query Options:")
|
||||
console.log(JSON.stringify(options,null,2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function logTiming(body, elapsedTimeMs)
|
||||
{
|
||||
if (serverConfig.logTimings)
|
||||
{
|
||||
var range = new Date(body.range.to) - new Date(body.range.from)
|
||||
var diff = moment.duration(range)
|
||||
|
||||
console.log("Request: " + intervalCount(diff, body.interval, body.intervalMs) + " - Returned in " + elapsedTimeMs.toFixed(2) + "ms")
|
||||
}
|
||||
}
|
||||
|
||||
// Take a range as a moment.duration and a grafana interval like 30s, 1m etc
|
||||
// And return the number of intervals that represents
|
||||
function intervalCount(range, intervalString, intervalMs)
|
||||
{
|
||||
// Convert everything to seconds
|
||||
var rangeSeconds = range.asSeconds()
|
||||
var intervalsInRange = rangeSeconds / (intervalMs / 1000)
|
||||
|
||||
var output = intervalsInRange.toFixed(0) + ' ' + intervalString + ' intervals'
|
||||
return output
|
||||
}
|
||||
|
||||
function getBucketCount(from, to, intervalMs)
|
||||
{
|
||||
var boundaries = []
|
||||
var current = new Date(from).getTime()
|
||||
var toMs = new Date(to).getTime()
|
||||
var count = 0
|
||||
while ( current < toMs )
|
||||
{
|
||||
current += intervalMs
|
||||
count++
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>mongodb-grafana-proxy</string>
|
||||
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PATH</key>
|
||||
<string>/usr/local/bin/:$PATH</string>
|
||||
</dict>
|
||||
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server</string>
|
||||
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/lib/npm-packages/bin/forever</string>
|
||||
<string>-l</string>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server/mongodb-proxy-forever.log</string>
|
||||
<string>-o</string>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server/mongodb-proxy.log</string>
|
||||
<string>-e</string>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server/mongodb-proxy-err.log</string>
|
||||
<string>--workingDir</string>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server</string>
|
||||
<string>/usr/local/var/lib/grafana/plugins/mongodb-grafana/dist/server/mongodb-proxy.js</string>
|
||||
</array>
|
||||
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
|
||||
<key>KeepAlive</key>
|
||||
<false/>
|
||||
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,3 @@
|
||||
.generic-datasource-query-row .query-keyword {
|
||||
width: 75px;
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2018 JamesOsgood
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
// #
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import _ from "lodash";
|
||||
|
||||
export class GenericDatasource {
|
||||
|
||||
constructor(instanceSettings, $q, backendSrv, templateSrv) {
|
||||
this.type = instanceSettings.type;
|
||||
this.url = instanceSettings.url;
|
||||
this.name = instanceSettings.name;
|
||||
this.db = { 'url' : instanceSettings.jsonData.mongodb_url, 'db' : instanceSettings.jsonData.mongodb_db }
|
||||
this.q = $q;
|
||||
this.backendSrv = backendSrv;
|
||||
this.templateSrv = templateSrv;
|
||||
this.withCredentials = instanceSettings.withCredentials;
|
||||
this.headers = {'Content-Type': 'application/json'};
|
||||
// Assert valid sign in parameter
|
||||
if (typeof instanceSettings.basicAuth === 'string' && instanceSettings.basicAuth.length > 0) {
|
||||
this.headers['Authorization'] = instanceSettings.basicAuth;
|
||||
}
|
||||
}
|
||||
|
||||
query(options) {
|
||||
var query = this.buildQueryParameters(options);
|
||||
query.targets = query.targets.filter(t => !t.hide);
|
||||
query.db = this.db
|
||||
|
||||
if (query.targets.length <= 0) {
|
||||
return this.q.when({data: []});
|
||||
}
|
||||
|
||||
return this.doRequest({
|
||||
url: this.url + '/query',
|
||||
data: query,
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
// Testing whether connection is valid
|
||||
testDatasource() {
|
||||
return this.doRequest({
|
||||
url: this.url + '/',
|
||||
data : { db : this.db },
|
||||
method: 'POST',
|
||||
}).then(response => {
|
||||
if (response.status === 200) {
|
||||
return { status: response.data.status, message: response.data.message, title: response.data.display_status };
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
annotationQuery(options) {
|
||||
var query = this.templateSrv.replace(options.annotation.query, {}, 'glob');
|
||||
var annotationQuery = {
|
||||
range: options.range,
|
||||
annotation: {
|
||||
name: options.annotation.name,
|
||||
datasource: options.annotation.datasource,
|
||||
enable: options.annotation.enable,
|
||||
iconColor: options.annotation.iconColor,
|
||||
query: query
|
||||
},
|
||||
rangeRaw: options.rangeRaw
|
||||
};
|
||||
|
||||
return this.doRequest({
|
||||
url: this.url + '/annotations',
|
||||
method: 'POST',
|
||||
data: annotationQuery
|
||||
}).then(result => {
|
||||
response.data.$$status = result.status;
|
||||
response.data.$$config = result.config;
|
||||
return result.data;
|
||||
});
|
||||
}
|
||||
|
||||
metricFindQuery(query) {
|
||||
var interpolated = {
|
||||
target: this.templateSrv.replace(query, null, '')
|
||||
};
|
||||
interpolated.db = this.db
|
||||
|
||||
return this.doRequest({
|
||||
url: this.url + '/search',
|
||||
data: interpolated,
|
||||
method: 'POST',
|
||||
}).then(this.mapToTextValue);
|
||||
}
|
||||
|
||||
mapToTextValue(result) {
|
||||
return _.map(result.data, (d, i) => {
|
||||
if (d && d.text && d.value) {
|
||||
return { text: d.text, value: d.value };
|
||||
} else if (_.isObject(d)) {
|
||||
return { text: d, value: i};
|
||||
}
|
||||
return { text: d, value: d };
|
||||
});
|
||||
}
|
||||
|
||||
doRequest(options) {
|
||||
options.withCredentials = this.withCredentials;
|
||||
options.headers = this.headers;
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
}
|
||||
|
||||
buildQueryParameters(options) {
|
||||
//remove place holder targets
|
||||
options.targets = _.filter(options.targets, target => {
|
||||
return target.target !== 'select metric';
|
||||
});
|
||||
|
||||
var targets = _.map(options.targets, target => {
|
||||
return {
|
||||
target: this.templateSrv.replace(target.target, options.scopedVars, ''),
|
||||
refId: target.refId,
|
||||
hide: target.hide,
|
||||
type: target.type || 'timeserie'
|
||||
};
|
||||
});
|
||||
|
||||
options.targets = targets;
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
<!-- ################################################################################
|
||||
# Copyright (c) 2018 JamesOsgood
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
################################################################################ -->
|
||||
|
||||
<h5 class="section-heading">Query</h5>
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form">
|
||||
<input type="text" class="gf-form-input" ng-model='ctrl.annotation.query' placeholder=""></input>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
<!-- ################################################################################
|
||||
# Copyright (c) 2018 JamesOsgood
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
################################################################################ -->
|
||||
|
||||
<datasource-http-settings current="ctrl.current">
|
||||
</datasource-http-settings>
|
||||
|
||||
<h3 class="page-heading">MongoDB details</h3>
|
||||
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label width-10">MongoDB URL</span>
|
||||
<input class="gf-form-input width-30" type="text"
|
||||
ng-model='ctrl.current.jsonData.mongodb_url'
|
||||
placeholder="mongodb://localhost:27017" required>
|
||||
</input>
|
||||
</div>
|
||||
|
||||
<div class="gf-form">
|
||||
<!--TODO: Make this compatible with $db Grafana variable-->
|
||||
<span class="gf-form-label width-10">Database Name</span>
|
||||
<input class="gf-form-input width-30" type="text"
|
||||
ng-model='ctrl.current.jsonData.mongodb_db'
|
||||
placeholder="myDatabase" required>
|
||||
</input>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<!-- ################################################################################
|
||||
# Copyright (c) 2018 JamesOsgood
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
################################################################################ -->
|
||||
|
||||
<query-editor-row query-ctrl="ctrl" class="generic-datasource-query-row" has-text-edit-mode="true">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form max-width-8">
|
||||
<!--Drop down menu for selecting query type-->
|
||||
<select class="gf-form-input" ng-model="ctrl.target.type" ng-options="f as f for f in ['table', 'timeseries']"></select>
|
||||
</div>
|
||||
|
||||
<div class="gf-form" ng-if="ctrl.target.rawQuery">
|
||||
<textarea class="gf-form-input" rows="5" cols="150" ng-model="ctrl.target.target" spellcheck="false" ng-blur="ctrl.onChangeInternal()" />
|
||||
</div>
|
||||
|
||||
<div ng-if="!ctrl.target.rawQuery">
|
||||
<div class="gf-form">
|
||||
<gf-form-dropdown model="ctrl.target.target"
|
||||
allow-custom="true"
|
||||
lookup-text="true"
|
||||
get-options="ctrl.getOptions($query)"
|
||||
on-change="ctrl.onChangeInternal()">
|
||||
</gf-form-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gf-form gf-form--auto">
|
||||
<div class="gf-form-label gf-form-label--auto"></div>
|
||||
</div>
|
||||
</div>
|
||||
</query-editor-row>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<!-- ################################################################################
|
||||
# Copyright (c) 2018 JamesOsgood
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
################################################################################ -->
|
||||
|
||||
<section class="grafana-metric-options" >
|
||||
<div class="gf-form">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
After Width: | Height: | Size: 67 KiB |
|
After Width: | Height: | Size: 47 KiB |
@@ -0,0 +1,41 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2018 JamesOsgood
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
// #
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import {GenericDatasource} from './datasource';
|
||||
import {GenericDatasourceQueryCtrl} from './query_ctrl';
|
||||
|
||||
class GenericConfigCtrl {}
|
||||
GenericConfigCtrl.templateUrl = 'html/config.html';
|
||||
|
||||
class GenericQueryOptionsCtrl {}
|
||||
GenericQueryOptionsCtrl.templateUrl = 'html/query.options.html';
|
||||
|
||||
class GenericAnnotationsQueryCtrl {}
|
||||
GenericAnnotationsQueryCtrl.templateUrl = 'html/annotations.editor.html'
|
||||
|
||||
export {
|
||||
GenericDatasource as Datasource,
|
||||
GenericDatasourceQueryCtrl as QueryCtrl,
|
||||
GenericConfigCtrl as ConfigCtrl,
|
||||
GenericQueryOptionsCtrl as QueryOptionsCtrl,
|
||||
GenericAnnotationsQueryCtrl as AnnotationsQueryCtrl
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "Omniperf Data",
|
||||
"id": "amd-omniperf-data-plugin",
|
||||
"type": "datasource",
|
||||
"backend": true,
|
||||
"partials": {
|
||||
"config": "public/app/plugins/datasource/simplejson/partials/config.html"
|
||||
},
|
||||
"metrics": true,
|
||||
"annotations": false,
|
||||
"info": {
|
||||
"description": "An Omniperf datasource build for MongoDB",
|
||||
"author": {
|
||||
"name": "Audacious Software Group",
|
||||
"url": ""
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/omniperf_circle.png",
|
||||
"large": "img/omniperf_circle.png"
|
||||
},
|
||||
"version": "%VERSION%",
|
||||
"updated": "%TODAY%"
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaVersion": ">=7.0.0",
|
||||
"plugins": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2018 JamesOsgood
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
// #
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import {QueryCtrl} from 'app/plugins/sdk';
|
||||
import './css/query-editor.css!'
|
||||
|
||||
export class GenericDatasourceQueryCtrl extends QueryCtrl {
|
||||
|
||||
constructor($scope, $injector) {
|
||||
super($scope, $injector);
|
||||
|
||||
this.scope = $scope;
|
||||
this.target.target = this.target.target || 'select metric';
|
||||
this.target.type = this.target.type || 'timeserie';
|
||||
this.target.rawQuery = true;
|
||||
}
|
||||
|
||||
getOptions(query) {
|
||||
return this.datasource.metricFindQuery(query || '');
|
||||
}
|
||||
|
||||
toggleEditorMode() {
|
||||
this.target.rawQuery = !this.target.rawQuery;
|
||||
}
|
||||
|
||||
onChangeInternal() {
|
||||
this.panelCtrl.refresh(); // Asks the panel to refresh data.
|
||||
}
|
||||
}
|
||||
|
||||
GenericDatasourceQueryCtrl.templateUrl = 'html/query.editor.html';
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
node_modules/
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
dist/
|
||||
artifacts/
|
||||
work/
|
||||
ci/
|
||||
e2e-results/
|
||||
|
||||
# Editors
|
||||
.idea
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
...require("./node_modules/@grafana/toolkit/src/config/prettier.plugin.config.json"),
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0 (Unreleased)
|
||||
|
||||
Initial release.
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Grafana Panel Plugin Template
|
||||
|
||||
[](https://github.com/grafana/grafana-starter-panel/actions?query=workflow%3A%22CI%22)
|
||||
|
||||
This template is a starting point for building Grafana Panel Plugins in Grafana 7.0+
|
||||
|
||||
## What is Grafana Panel Plugin?
|
||||
|
||||
Panels are the building blocks of Grafana. They allow you to visualize data in different ways. While Grafana has several types of panels already built-in, you can also build your own panel, to add support for other visualizations.
|
||||
|
||||
For more information about panels, refer to the documentation on [Panels](https://grafana.com/docs/grafana/latest/features/panels/panels/)
|
||||
|
||||
## Getting started
|
||||
|
||||
1. Install dependencies
|
||||
|
||||
```bash
|
||||
yarn install
|
||||
```
|
||||
|
||||
2. Build plugin in development mode or run in watch mode
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
yarn watch
|
||||
```
|
||||
|
||||
3. Build plugin in production mode
|
||||
|
||||
```bash
|
||||
yarn build
|
||||
```
|
||||
|
||||
## Learn more
|
||||
|
||||
- [Build a panel plugin tutorial](https://grafana.com/tutorials/build-a-panel-plugin)
|
||||
- [Grafana documentation](https://grafana.com/docs/)
|
||||
- [Grafana Tutorials](https://grafana.com/tutorials/) - Grafana Tutorials are step-by-step guides that help you make the most of Grafana
|
||||
- [Grafana UI Library](https://developers.grafana.com/ui) - UI components to help you build interfaces using Grafana Design System
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "amd-custom-svg",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "grafana-toolkit plugin:build",
|
||||
"test": "grafana-toolkit plugin:test",
|
||||
"dev": "grafana-toolkit plugin:dev",
|
||||
"watch": "grafana-toolkit plugin:dev --watch",
|
||||
"sign": "grafana-toolkit plugin:sign",
|
||||
"start": "yarn watch"
|
||||
},
|
||||
"author": "Audacious Software Group",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@grafana/data": "latest",
|
||||
"@grafana/toolkit": "latest",
|
||||
"@grafana/ui": "latest",
|
||||
"emotion": "10.0.27"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grafana/runtime": "^8.1.1",
|
||||
"@svgdotjs/svg.js": "^3.1.1",
|
||||
"react-monaco-editor": "^0.44.0",
|
||||
"tslib": "^2.3.1"
|
||||
},
|
||||
"_comments": "Dependencies are not included as part of Omniperf. It's the user's responsibility to accept any licensing implications before building the project."
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { stylesFactory } from '@grafana/ui';
|
||||
import { SVG, Element as SVGElement, Dom as SVGDom, extend as SVGExtend, Runner as SVGRunner } from '@svgdotjs/svg.js';
|
||||
import { SVGOptions, SVGIDMapping } from 'types';
|
||||
|
||||
import { css } from 'emotion';
|
||||
|
||||
interface MappedElements {
|
||||
[key: string]: SVGElement | SVGDom;
|
||||
}
|
||||
interface Props extends PanelProps<SVGOptions> {}
|
||||
interface PanelState {
|
||||
addAllIDs: boolean;
|
||||
svgNode: SVGElement | SVGDom | null;
|
||||
svgSource: string | null;
|
||||
mappedElements: MappedElements | null;
|
||||
svgMappings: SVGIDMapping[];
|
||||
initFunctionSource: string;
|
||||
initFunction: Function | null;
|
||||
eventFunctionSource: string;
|
||||
eventFunction: Function | null;
|
||||
initialized: boolean;
|
||||
}
|
||||
interface TextMappedElement extends SVGElement {
|
||||
textElement: Element;
|
||||
}
|
||||
|
||||
SVGExtend(SVGElement, {
|
||||
openOnClick: function (this: SVGElement, url: string) {
|
||||
return window.open(url);
|
||||
},
|
||||
animateContRotate: function (this: SVGElement, speed: number) {
|
||||
return (
|
||||
this.animate(speed)
|
||||
//@ts-ignore
|
||||
.ease('-')
|
||||
//@ts-ignore
|
||||
.rotate(360)
|
||||
.loop()
|
||||
);
|
||||
},
|
||||
showOn: function (this: SVGElement, on: boolean) {
|
||||
if (on) {
|
||||
this.show();
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
animateOn: function (this: SVGElement, speed: number, on: boolean, animation: Function) {
|
||||
if (on) {
|
||||
//@ts-ignore
|
||||
if (this.timeline()._runners.length === 0) {
|
||||
animation(this.animate(speed));
|
||||
} else {
|
||||
this.timeline().play();
|
||||
}
|
||||
} else {
|
||||
this.timeline().stop();
|
||||
}
|
||||
},
|
||||
stopAnimation: function (this: SVGRunner) {
|
||||
this.timeline().stop();
|
||||
},
|
||||
getParentNode: function (this: SVGElement) {
|
||||
return this.node.parentNode;
|
||||
},
|
||||
getTopNode: function (this: SVGElement) {
|
||||
let currentNode: Element = this.node as Element;
|
||||
while (true) {
|
||||
if (currentNode.parentNode && !currentNode.className.includes('svg-object')) {
|
||||
currentNode = currentNode.parentNode as Element;
|
||||
} else {
|
||||
return currentNode;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
SVGExtend(SVGDom, {
|
||||
updateXHTMLFontText: function (this: SVGDom, newText: string) {
|
||||
let currentElement: Element | TextMappedElement = this.node;
|
||||
let i = 0;
|
||||
while (currentElement.localName !== 'xhtml:font') {
|
||||
if (currentElement.firstElementChild && i < 10) {
|
||||
currentElement = currentElement.firstElementChild;
|
||||
i++;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentElement.innerHTML = newText;
|
||||
},
|
||||
});
|
||||
|
||||
export class SVGPanel extends PureComponent<Props, PanelState> {
|
||||
constructor(props: any) {
|
||||
super(props);
|
||||
this.state = {
|
||||
addAllIDs: false,
|
||||
svgNode: null,
|
||||
svgSource: null,
|
||||
svgMappings: [],
|
||||
mappedElements: null,
|
||||
initFunctionSource: '',
|
||||
initFunction: null,
|
||||
eventFunctionSource: '',
|
||||
eventFunction: null,
|
||||
initialized: false,
|
||||
};
|
||||
}
|
||||
|
||||
initializeMappings(svgNode: SVGElement | SVGDom) {
|
||||
const svgMappings = this.props.options.svgMappings;
|
||||
let currentElements: MappedElements = { ...this.state.mappedElements };
|
||||
currentElements = {};
|
||||
console.log('Current length is ', svgMappings.length);
|
||||
for (let i = 0; i < svgMappings.length; i++) {
|
||||
if (svgMappings[i].mappedName !== '') {
|
||||
currentElements[this.props.options.svgMappings[i].mappedName] = svgNode.findOne(
|
||||
`#${this.props.options.svgMappings[i].svgId}`
|
||||
)!;
|
||||
}
|
||||
}
|
||||
this.setState({ mappedElements: currentElements });
|
||||
}
|
||||
mapAllIDs(svgNode: SVGDom) {
|
||||
let svgMappings: SVGIDMapping[] = [...this.props.options.svgMappings];
|
||||
let nodeFilterID: NodeFilter = {
|
||||
acceptNode: (node: Element) => {
|
||||
if (node.id) {
|
||||
if (node.id !== '') {
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
}
|
||||
}
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
},
|
||||
};
|
||||
let svgWalker = document.createTreeWalker(svgNode.node, NodeFilter.SHOW_ALL, nodeFilterID);
|
||||
let currentNode: Element | null = svgWalker.currentNode as Element;
|
||||
while (currentNode) {
|
||||
if (currentNode && currentNode.id) {
|
||||
if (svgMappings.filter((mapping) => (currentNode ? mapping.svgId === currentNode.id : false)).length === 0) {
|
||||
svgMappings.push({ svgId: currentNode.id, mappedName: '' });
|
||||
}
|
||||
}
|
||||
currentNode = svgWalker.nextNode() as Element;
|
||||
}
|
||||
this.setState({ svgMappings: [...svgMappings], initialized: false });
|
||||
this.props.options.svgMappings = [...svgMappings];
|
||||
this.props.onOptionsChange(this.props.options);
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
mappingClickHandler(event: React.MouseEvent<HTMLElement, MouseEvent>) {
|
||||
if (event.target) {
|
||||
let clicked = event.target as Element;
|
||||
let loopCount = 0;
|
||||
let svgMappings: SVGIDMapping[] = [...this.props.options.svgMappings];
|
||||
if (clicked.id) {
|
||||
while (clicked.id === '') {
|
||||
loopCount++;
|
||||
if (loopCount > 20) {
|
||||
return;
|
||||
}
|
||||
clicked = clicked.parentNode as Element;
|
||||
}
|
||||
for (let i = 0; i < svgMappings.length; i++) {
|
||||
if (svgMappings[i].svgId === clicked.id) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
svgMappings.push({ svgId: clicked.id, mappedName: '' });
|
||||
this.setState({ svgMappings: [...svgMappings], initialized: false });
|
||||
this.props.options.svgMappings = [...svgMappings];
|
||||
this.props.onOptionsChange(this.props.options);
|
||||
this.forceUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
renderSVG(element: SVGSVGElement | SVGDom | null) {
|
||||
if (element) {
|
||||
if (
|
||||
this.props.options.initSource !== this.state.initFunctionSource ||
|
||||
this.state.addAllIDs !== this.props.options.addAllIDs
|
||||
) {
|
||||
this.setState({
|
||||
initFunctionSource: this.props.options.initSource,
|
||||
addAllIDs: this.props.options.addAllIDs,
|
||||
initialized: false,
|
||||
});
|
||||
}
|
||||
if (!this.state.initialized) {
|
||||
console.log('initializing');
|
||||
let svgNode = SVG(element);
|
||||
svgNode.clear();
|
||||
svgNode.svg(this.props.options.svgSource);
|
||||
svgNode.size(this.props.width, this.props.height);
|
||||
if (this.props.options.addAllIDs) {
|
||||
this.mapAllIDs(svgNode);
|
||||
}
|
||||
this.initializeMappings(svgNode);
|
||||
this.setState({ svgNode: svgNode });
|
||||
|
||||
try {
|
||||
this.setState({
|
||||
initFunction: Function(
|
||||
'data',
|
||||
'options',
|
||||
'svgnode',
|
||||
'svgmap',
|
||||
this.props.replaceVariables(this.props.options.initSource)
|
||||
),
|
||||
});
|
||||
if (this.state.mappedElements && this.state.initFunction) {
|
||||
this.state.initFunction(this.props.data, this.props.options, this.state.svgNode, this.state.mappedElements);
|
||||
this.setState({ initialized: true });
|
||||
}
|
||||
} catch (e) {
|
||||
this.setState({ initialized: true });
|
||||
console.log(`User init code failed: ${e}`);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let eventFunction = this.state.eventFunction;
|
||||
if (this.props.options.eventSource !== this.state.eventFunctionSource) {
|
||||
let eventFunctionSource = this.props.options.eventSource;
|
||||
eventFunction = Function(
|
||||
'data',
|
||||
'options',
|
||||
'svgnode',
|
||||
'svgmap',
|
||||
this.props.replaceVariables(eventFunctionSource)
|
||||
);
|
||||
this.setState({ eventFunctionSource: eventFunctionSource, eventFunction: eventFunction, initialized: false });
|
||||
}
|
||||
if (this.state.mappedElements && eventFunction) {
|
||||
eventFunction(this.props.data, this.props.options, this.state.svgNode, this.state.mappedElements);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`User event code failed: ${e}`);
|
||||
}
|
||||
|
||||
return this.state.svgNode ? this.state.svgNode.svg() : null;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
render() {
|
||||
const styles = this.getStyles();
|
||||
return (
|
||||
<div
|
||||
className={styles.wrapper}
|
||||
onClick={this.props.options.captureMappings ? this.mappingClickHandler.bind(this) : undefined}
|
||||
>
|
||||
<svg
|
||||
style={{
|
||||
width: `${this.props.width}px`,
|
||||
height: `${this.props.height}px`,
|
||||
}}
|
||||
className={'svg-object'}
|
||||
ref={(ref) => this.renderSVG(ref)}
|
||||
></svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
getStyles = stylesFactory(() => {
|
||||
return {
|
||||
wrapper: css`
|
||||
position: relative;
|
||||
`,
|
||||
svg: css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
`,
|
||||
textBox: css`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10px;
|
||||
`,
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import React from 'react';
|
||||
import { PanelProps } from '@grafana/data';
|
||||
import { SimpleOptions } from 'types';
|
||||
import { css, cx } from 'emotion';
|
||||
import { stylesFactory, useTheme } from '@grafana/ui';
|
||||
|
||||
interface Props extends PanelProps<SimpleOptions> {}
|
||||
|
||||
export const SimplePanel: React.FC<Props> = ({ options, data, width, height }) => {
|
||||
const theme = useTheme();
|
||||
const styles = getStyles();
|
||||
return (
|
||||
<div
|
||||
className={cx(
|
||||
styles.wrapper,
|
||||
css`
|
||||
width: ${width}px;
|
||||
height: ${height}px;
|
||||
`
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
className={styles.svg}
|
||||
width={width}
|
||||
height={height}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
viewBox={`-${width / 2} -${height / 2} ${width} ${height}`}
|
||||
>
|
||||
<g>
|
||||
<circle style={{ fill: `${theme.isLight ? theme.palette.greenBase : theme.palette.blue95}` }} r={100} />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<div className={styles.textBox}>
|
||||
{options.showSeriesCount && (
|
||||
<div
|
||||
className={css`
|
||||
font-size: ${theme.typography.size[options.seriesCountSize]};
|
||||
`}
|
||||
>
|
||||
Number of series: {data.series.length}
|
||||
</div>
|
||||
)}
|
||||
<div>Text option value: {options.text}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory(() => {
|
||||
return {
|
||||
wrapper: css`
|
||||
position: relative;
|
||||
`,
|
||||
svg: css`
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
`,
|
||||
textBox: css`
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
padding: 10px;
|
||||
`,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,61 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import { SVGDefaults } from './types';
|
||||
|
||||
export const props_defaults: SVGDefaults = {
|
||||
svgNode: `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="377px"
|
||||
viewBox="-0.5 -0.5 377 377"
|
||||
style="max-width:100%;max-height:377px;"
|
||||
>
|
||||
<defs />
|
||||
<g>
|
||||
<ellipse
|
||||
cx="188"
|
||||
cy="188"
|
||||
rx="185"
|
||||
ry="185"
|
||||
fill="#6666ff"
|
||||
stroke="#ff6666"
|
||||
stroke-width="7"
|
||||
pointer-events="all"
|
||||
/>
|
||||
</g>
|
||||
</svg>;
|
||||
`,
|
||||
initSource: '',
|
||||
eventSource: '',
|
||||
svgMappings: [
|
||||
{
|
||||
mappedName: 'barTwo',
|
||||
svgId: 'rect4526',
|
||||
},
|
||||
{
|
||||
mappedName: 'barThree',
|
||||
svgId: 'rect4528',
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 81.9 71.52"><defs><style>.cls-1{fill:#84aff1;}.cls-2{fill:#3865ab;}.cls-3{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="42.95" y1="16.88" x2="81.9" y2="16.88" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M55.46,62.43A2,2,0,0,1,54.07,59l4.72-4.54a2,2,0,0,1,2.2-.39l3.65,1.63,3.68-3.64a2,2,0,1,1,2.81,2.84l-4.64,4.6a2,2,0,0,1-2.22.41L60.6,58.26l-3.76,3.61A2,2,0,0,1,55.46,62.43Z"/><path class="cls-2" d="M37,0H2A2,2,0,0,0,0,2V31.76a2,2,0,0,0,2,2H37a2,2,0,0,0,2-2V2A2,2,0,0,0,37,0ZM4,29.76V8.84H35V29.76Z"/><path class="cls-3" d="M79.9,0H45a2,2,0,0,0-2,2V31.76a2,2,0,0,0,2,2h35a2,2,0,0,0,2-2V2A2,2,0,0,0,79.9,0ZM47,29.76V8.84h31V29.76Z"/><path class="cls-2" d="M37,37.76H2a2,2,0,0,0-2,2V69.52a2,2,0,0,0,2,2H37a2,2,0,0,0,2-2V39.76A2,2,0,0,0,37,37.76ZM4,67.52V46.6H35V67.52Z"/><path class="cls-2" d="M79.9,37.76H45a2,2,0,0,0-2,2V69.52a2,2,0,0,0,2,2h35a2,2,0,0,0,2-2V39.76A2,2,0,0,0,79.9,37.76ZM47,67.52V46.6h31V67.52Z"/><rect class="cls-1" x="10.48" y="56.95" width="4" height="5.79"/><rect class="cls-1" x="17.43" y="53.95" width="4" height="8.79"/><rect class="cls-1" x="24.47" y="50.95" width="4" height="11.79"/><path class="cls-1" d="M19.47,25.8a6.93,6.93,0,1,1,6.93-6.92A6.93,6.93,0,0,1,19.47,25.8Zm0-9.85a2.93,2.93,0,1,0,2.93,2.93A2.93,2.93,0,0,0,19.47,16Z"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,28 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { SVGOptions } from './types';
|
||||
import { SVGPanel } from './SVGjsPanel';
|
||||
import { optionsBuilder } from 'options';
|
||||
|
||||
export const plugin = new PanelPlugin<SVGOptions>(SVGPanel).useFieldConfig().setPanelOptions(optionsBuilder);
|
||||
@@ -0,0 +1,349 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
import React from 'react';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import { SVGIDMapping, SVGOptions } from './types';
|
||||
import { props_defaults } from 'examples';
|
||||
|
||||
import { HorizontalGroup, Label, Tooltip, Button, Input, VerticalGroup, stylesFactory } from '@grafana/ui';
|
||||
import { PanelOptionsEditorProps, GrafanaTheme, PanelOptionsEditorBuilder } from '@grafana/data';
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { css } from 'emotion';
|
||||
|
||||
interface MonacoEditorProps {
|
||||
value: string;
|
||||
theme: string;
|
||||
language: string;
|
||||
onChange: (value?: string | undefined) => void;
|
||||
}
|
||||
class MonacoEditor extends React.PureComponent<MonacoEditorProps> {
|
||||
getEditorValue: any | undefined;
|
||||
editorInstance: any | undefined;
|
||||
|
||||
// onSourceChange = () => {
|
||||
// console.log(this.getEditorValue);
|
||||
// this.props.onChange(this.getEditorValue());
|
||||
// };
|
||||
handleEditorChange = (getEditorValue: any, editorInstance: any) => {
|
||||
this.props.onChange(getEditorValue);
|
||||
console.log('here is the current model value:', getEditorValue);
|
||||
};
|
||||
onEditorDidMount = (editorInstance: any, getEditorValue: any) => {
|
||||
this.editorInstance = editorInstance;
|
||||
this.getEditorValue = getEditorValue;
|
||||
};
|
||||
updateDimensions() {
|
||||
this.editorInstance.layout();
|
||||
}
|
||||
render() {
|
||||
const source = this.props.value;
|
||||
if (this.editorInstance) {
|
||||
this.editorInstance.layout();
|
||||
}
|
||||
return (
|
||||
//<div onBlur={this.onSourceChange}>
|
||||
<Editor
|
||||
height={'33vh'}
|
||||
language={this.props.language}
|
||||
theme={this.props.theme}
|
||||
value={source}
|
||||
onMount={this.onEditorDidMount}
|
||||
onChange={this.handleEditorChange}
|
||||
/>
|
||||
//</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
interface SVGIDMappingProps {
|
||||
value: SVGIDMapping;
|
||||
index?: number;
|
||||
styles?: any;
|
||||
onChangeItem?: (a: SVGIDMapping, b: number) => void | undefined;
|
||||
onAdd?: (a: SVGIDMapping) => void;
|
||||
onDelete?: (a: number) => void;
|
||||
}
|
||||
class SvgMapping extends React.PureComponent<SVGIDMappingProps> {
|
||||
constructor(props: SVGIDMappingProps) {
|
||||
super(props);
|
||||
this.state = { ...props.value };
|
||||
}
|
||||
render() {
|
||||
const { value, index, onChangeItem, onAdd, onDelete } = this.props;
|
||||
return (
|
||||
<HorizontalGroup>
|
||||
<Label>SVG ID</Label>
|
||||
<Input
|
||||
type="text"
|
||||
name="svgId"
|
||||
defaultValue={value.svgId}
|
||||
onBlur={(e) => {
|
||||
const svgId = e.currentTarget.value;
|
||||
this.setState({ svgId: svgId });
|
||||
onChangeItem && index && onChangeItem({ ...value, svgId: svgId }, index);
|
||||
}}
|
||||
/>
|
||||
<Label>Mapped Name</Label>
|
||||
<Input
|
||||
type="text"
|
||||
name="mappedName"
|
||||
defaultValue={value.mappedName}
|
||||
onBlur={(e) => {
|
||||
const mappedName = e.currentTarget.value;
|
||||
this.setState({ mappedName: mappedName });
|
||||
onChangeItem && index && onChangeItem({ ...value, mappedName: mappedName }, index);
|
||||
}}
|
||||
/>
|
||||
{value.svgId && onDelete && index !== undefined && (
|
||||
<Tooltip content="Delete this mapping" theme={'info'}>
|
||||
<Button
|
||||
variant="destructive"
|
||||
icon="trash-alt"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
onDelete(index);
|
||||
}}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!value.svgId && onAdd && (
|
||||
<Tooltip content="Add a new SVG Element ID to svgmap property mapping manually" theme={'info'}>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
icon="plus-circle"
|
||||
onClick={() => {
|
||||
onAdd(this.state as SVGIDMapping);
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</HorizontalGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SvgMappings extends React.PureComponent<PanelOptionsEditorProps<SVGIDMapping[]>> {
|
||||
onChangeItem = (updatedMapping: SVGIDMapping, index: number) => {
|
||||
let newMappings = [...this.props.value];
|
||||
newMappings[index] = updatedMapping;
|
||||
this.props.onChange(newMappings);
|
||||
};
|
||||
onAdd = (newMapping: SVGIDMapping) => {
|
||||
if (newMapping.svgId !== '') {
|
||||
let newMappings = [...this.props.value, newMapping];
|
||||
this.props.onChange(newMappings);
|
||||
}
|
||||
};
|
||||
onDelete = (index: number) => {
|
||||
let newMappings = [...this.props.value];
|
||||
newMappings.splice(index, 1);
|
||||
this.props.onChange(newMappings);
|
||||
};
|
||||
render() {
|
||||
const styles = getStyles(config.theme);
|
||||
const svgMappings = this.props.value;
|
||||
return (
|
||||
<VerticalGroup>
|
||||
<HorizontalGroup>
|
||||
<Tooltip content="Clear all SVG Element ID to svgmap property mappings" theme="info">
|
||||
<Button
|
||||
variant="destructive"
|
||||
icon="trash-alt"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
this.props.onChange([]);
|
||||
}}
|
||||
>
|
||||
Clear All
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<SvgMapping value={{ svgId: '', mappedName: '' }} styles={styles} onAdd={this.onAdd} />
|
||||
</HorizontalGroup>
|
||||
{svgMappings.map((currentMapping: SVGIDMapping, index: number) => {
|
||||
return (
|
||||
<SvgMapping
|
||||
key={currentMapping.svgId}
|
||||
value={currentMapping}
|
||||
index={index}
|
||||
onChangeItem={this.onChangeItem}
|
||||
onDelete={this.onDelete}
|
||||
styles={styles}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</VerticalGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
|
||||
export const optionsBuilder = (builder: PanelOptionsEditorBuilder<SVGOptions>) => {
|
||||
return builder
|
||||
.addBooleanSwitch({
|
||||
category: ['SVG Document'],
|
||||
path: 'svgAutoComplete',
|
||||
name: 'Enable SVG AutoComplete',
|
||||
description: 'Enable editor autocompletion, optional as it can be buggy on large documents',
|
||||
})
|
||||
.addCustomEditor({
|
||||
category: ['SVG Document'],
|
||||
path: 'svgSource',
|
||||
name: 'SVG Document',
|
||||
description: `Editor for SVG Document, while small tweaks can be made here, we recommend using a dedicated
|
||||
Graphical SVG Editor and simply pasting the resulting XML here`,
|
||||
id: 'svgSource',
|
||||
defaultValue: props_defaults.svgNode,
|
||||
//editor: (props) => {
|
||||
editor: function myNamedFuntion(props) {
|
||||
const grafanaTheme = config.theme.name;
|
||||
return (
|
||||
<MonacoEditor
|
||||
language="xml"
|
||||
theme={grafanaTheme === 'Grafana Light' ? 'vs-light' : 'vs-dark'}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
);
|
||||
},
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
category: ['User JS Render'],
|
||||
path: 'eventAutoComplete',
|
||||
name: 'Enable Render JS AutoComplete',
|
||||
description: 'Enable editor autocompletion, optional as it can be buggy on large documents',
|
||||
defaultValue: true,
|
||||
})
|
||||
.addCustomEditor({
|
||||
category: ['User JS Render'],
|
||||
path: 'eventSource',
|
||||
name: 'User JS Render Code',
|
||||
description: `The User JS Render code is executed whenever new data is available, the root svg document is available as 'svgnode',
|
||||
and elements you've mapped using the SVG Mapping tools below are available as properties on the 'svgmap' object.
|
||||
The Grafana DataFrame is provided as 'data' and the 'options' object can be used to pass values and references between
|
||||
the Render context and the Init context`,
|
||||
id: 'eventSource',
|
||||
defaultValue: props_defaults.eventSource,
|
||||
//editor: (props) => {
|
||||
editor: function myNamedFunc(props) {
|
||||
const grafanaTheme = config.theme.name;
|
||||
return (
|
||||
<MonacoEditor
|
||||
language="javascript"
|
||||
theme={grafanaTheme === 'Grafana Light' ? 'vs-light' : 'vs-dark'}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
);
|
||||
},
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
category: ['User JS Init'],
|
||||
path: 'initAutoComplete',
|
||||
name: 'Enable Init JS AutoComplete',
|
||||
description: 'Enable editor autocompletion, optional as it can be buggy on large documents',
|
||||
defaultValue: true,
|
||||
})
|
||||
.addCustomEditor({
|
||||
category: ['User JS Init'],
|
||||
path: 'initSource',
|
||||
name: 'User JS Init Code',
|
||||
description: `The User JS Init code is executed once when the panel loads, you can use this to define helper functions that
|
||||
you later reference in the User JS Render code section. The sections have identical execution contexts, and any
|
||||
JS objects you want to reference between them will need to be attached to the options object as properties`,
|
||||
id: 'initSource',
|
||||
defaultValue: props_defaults.initSource,
|
||||
//editor: (props) => {
|
||||
editor: function myNamedFunc(props) {
|
||||
const grafanaTheme = config.theme.name;
|
||||
return (
|
||||
<MonacoEditor
|
||||
language="javascript"
|
||||
theme={grafanaTheme === 'Grafana Light' ? 'vs-light' : 'vs-dark'}
|
||||
value={props.value}
|
||||
onChange={props.onChange}
|
||||
/>
|
||||
);
|
||||
},
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
category: ['SVG Mapping'],
|
||||
path: 'addAllIDs',
|
||||
name: 'Add all SVG Element IDs',
|
||||
description:
|
||||
'Parse the SVG Document for Elements with IDs assigned and automatically add them to the mapping list',
|
||||
defaultValue: false,
|
||||
})
|
||||
.addBooleanSwitch({
|
||||
category: ['SVG Mapping'],
|
||||
path: 'captureMappings',
|
||||
name: 'Enable SVG Mapping on Click',
|
||||
description:
|
||||
'When activated, clicking an element in the panel will attempt to map the clicked element or its nearest parent element with an ID assigned',
|
||||
defaultValue: false,
|
||||
})
|
||||
.addCustomEditor({
|
||||
category: ['SVG Mapping'],
|
||||
id: 'svgMappings',
|
||||
path: 'svgMappings',
|
||||
name: 'SVG Mappings',
|
||||
description:
|
||||
'The SVG ID should match an element in the SVG document with an existing ID tag, the element will be attached to the "svgmap" object in the user code execution contexts as a property using the Mapped Name provided below',
|
||||
defaultValue: props_defaults.svgMappings,
|
||||
editor: SvgMappings,
|
||||
});
|
||||
};
|
||||
|
||||
const getStyles = stylesFactory((theme: GrafanaTheme) => {
|
||||
return {
|
||||
colorPicker: css`
|
||||
padding: 0 ${theme.spacing.sm};
|
||||
`,
|
||||
inputPrefix: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`,
|
||||
trashIcon: css`
|
||||
color: ${theme.colors.textWeak};
|
||||
cursor: pointer;
|
||||
//
|
||||
&:hover {
|
||||
color: ${theme.colors.text};
|
||||
}
|
||||
`,
|
||||
addIcon: css`
|
||||
color: ${theme.colors.textWeak};
|
||||
cursor: pointer;
|
||||
//
|
||||
&:hover {
|
||||
color: ${theme.colors.text};
|
||||
}
|
||||
`,
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/grafana/grafana/master/docs/sources/developers/plugins/plugin.schema.json",
|
||||
"type": "panel",
|
||||
"name": "custom-svg",
|
||||
"id": "amd-custom-svg",
|
||||
"info": {
|
||||
"description": "",
|
||||
"author": {
|
||||
"name": "Cole Ramos",
|
||||
"url": ""
|
||||
},
|
||||
"keywords": [],
|
||||
"logos": {
|
||||
"small": "img/logo.svg",
|
||||
"large": "img/logo.svg"
|
||||
},
|
||||
"screenshots": [],
|
||||
"version": "%VERSION%",
|
||||
"updated": "%TODAY%"
|
||||
},
|
||||
"dependencies": {
|
||||
"grafanaDependency": ">=7.0.0",
|
||||
"plugins": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// ################################################################################
|
||||
// # Copyright (c) 2020 ACE IoT Solutions LLC
|
||||
// #
|
||||
// # Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// # of this software and associated documentation files (the "Software"), to deal
|
||||
// # in the Software without restriction, including without limitation the rights
|
||||
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// # copies of the Software, and to permit persons to whom the Software is
|
||||
// # furnished to do so, subject to the following conditions:
|
||||
|
||||
// # The above copyright notice and this permission notice shall be included in all
|
||||
// # copies or substantial portions of the Software.
|
||||
// #
|
||||
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// # SOFTWARE.
|
||||
// ################################################################################
|
||||
|
||||
type SeriesSize = 'sm' | 'md' | 'lg';
|
||||
|
||||
export interface SimpleOptions {
|
||||
text: string;
|
||||
showSeriesCount: boolean;
|
||||
seriesCountSize: SeriesSize;
|
||||
}
|
||||
export interface SVGIDMapping {
|
||||
svgId: string;
|
||||
mappedName: string;
|
||||
}
|
||||
export interface SVGOptions {
|
||||
captureMappings: boolean;
|
||||
addAllIDs: boolean;
|
||||
svgSource: string;
|
||||
svgAutocomplete: boolean;
|
||||
eventSource: string;
|
||||
eventAutocomplete: boolean;
|
||||
initSource: string;
|
||||
initAutocomplete: boolean;
|
||||
svgMappings: SVGIDMapping[];
|
||||
}
|
||||
export interface SVGDefaults {
|
||||
svgNode: string; //svg default text
|
||||
initSource: string; //init default text
|
||||
eventSource: string; //render default text
|
||||
svgMappings: SVGIDMapping[]; //default mappings
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"extends": "./node_modules/@grafana/toolkit/src/config/tsconfig.plugin.json",
|
||||
"include": ["src", "types"],
|
||||
"compilerOptions": {
|
||||
"types": ["@emotion/core"],
|
||||
"rootDir": "./src",
|
||||
"baseUrl": "./src",
|
||||
"typeRoots": ["./node_modules/@types"],
|
||||
"jsx": "react"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
[tool.black]
|
||||
line-length = 90
|
||||
target-version = ['py38', 'py39']
|
||||
include = '\.py$'
|
||||
exclude = '''
|
||||
(
|
||||
/(
|
||||
\.eggs
|
||||
| \.git
|
||||
| \.github
|
||||
| \.tox
|
||||
| \.venv
|
||||
| \.misc
|
||||
| \.vscode
|
||||
| \.pyc
|
||||
| dist
|
||||
| external
|
||||
| .pytest_cache
|
||||
| build
|
||||
| build-omniperf
|
||||
)/
|
||||
)
|
||||
'''
|
||||
[tool.pytest.ini_options]
|
||||
addopts = [
|
||||
"--import-mode=importlib",
|
||||
"--cov=src",
|
||||
|
||||
]
|
||||
pythonpath = [
|
||||
".",
|
||||
"src",
|
||||
"src/utils",
|
||||
"src/omniperf_cli/utils"
|
||||
]
|
||||
@@ -0,0 +1,12 @@
|
||||
astunparse==1.6.2
|
||||
colorlover
|
||||
dash
|
||||
matplotlib
|
||||
numpy
|
||||
pandas
|
||||
pymongo
|
||||
pyyaml
|
||||
tabulate
|
||||
tqdm
|
||||
dash-svg
|
||||
dash-bootstrap-components
|
||||
@@ -0,0 +1,148 @@
|
||||
#include "hip/hip_runtime.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
|
||||
#define HIP_ASSERT(x) (assert((x)==hipSuccess))
|
||||
|
||||
// HIP kernel. Each thread takes care of one element of c
|
||||
__global__ void vecCopy(double *a, double *b, double *c, int n,int stride)
|
||||
{
|
||||
// Get our global thread ID
|
||||
int id = blockIdx.x*blockDim.x+threadIdx.x;
|
||||
|
||||
if (id < n) {
|
||||
c[id] = a[id];
|
||||
}
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
printf("\nUsage: vcopy [n] [blocksize]\n\n");
|
||||
exit(1);
|
||||
return;
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
// Size of vectors
|
||||
int n; //64 MB
|
||||
int blockSize, gridSize;
|
||||
|
||||
// Host input vectors
|
||||
double *h_a;
|
||||
double *h_b;
|
||||
//Host output vector
|
||||
double *h_c;
|
||||
//Host output vector for verification
|
||||
double *h_verify_c;
|
||||
|
||||
// Device input vectors
|
||||
double *d_a;
|
||||
double *d_b;
|
||||
//Device output vector
|
||||
double *d_c;
|
||||
|
||||
int stride = 1;
|
||||
|
||||
if(argc < 3)
|
||||
usage();
|
||||
|
||||
n = atoi(argv[1]);
|
||||
blockSize = atoi(argv[2]);
|
||||
|
||||
assert(n > 0);
|
||||
assert(blockSize > 0);
|
||||
|
||||
// Size, in bytes, of each vector
|
||||
size_t bytes = n*sizeof(double)*stride;
|
||||
|
||||
// Allocate memory for each vector on host
|
||||
h_a = (double*)malloc(bytes);
|
||||
h_b = (double*)malloc(bytes);
|
||||
h_c = (double*)malloc(bytes);
|
||||
h_verify_c = (double*)malloc(bytes);
|
||||
|
||||
printf("Finished allocating vectors on the CPU\n");
|
||||
// Allocate memory for each vector on GPU
|
||||
HIP_ASSERT(hipMalloc(&d_a, bytes));
|
||||
HIP_ASSERT(hipMalloc(&d_b, bytes));
|
||||
HIP_ASSERT(hipMalloc(&d_c, bytes));
|
||||
|
||||
printf("Finished allocating vectors on the GPU\n");
|
||||
|
||||
int i;
|
||||
// Initialize vectors on host
|
||||
for( i = 0; i < n; i++ ) {
|
||||
h_a[i] = i;
|
||||
h_b[i] = i;
|
||||
}
|
||||
|
||||
|
||||
// Copy host vectors to device
|
||||
HIP_ASSERT(hipMemcpy( d_a, h_a, bytes, hipMemcpyHostToDevice));
|
||||
HIP_ASSERT(hipMemcpy(d_b, h_b, bytes, hipMemcpyHostToDevice));
|
||||
|
||||
printf("Finished copying vectors to the GPU\n");
|
||||
|
||||
|
||||
// Number of thread blocks in grid
|
||||
gridSize = (int)ceil((float)n/blockSize);
|
||||
//gridSize = 1;
|
||||
|
||||
int tot_waves = (blockSize*gridSize)/64;
|
||||
float num_bytes_kb = ((sizeof(double))*n)/(1024);
|
||||
float num_bytes_wave = (1.0*num_bytes_kb)/(1.0*tot_waves);
|
||||
|
||||
printf("sw thinks it moved %f KB per wave \n", (2.0*num_bytes_wave));
|
||||
|
||||
printf("Total threads: %d, Grid Size: %d block Size:%d, Wavefronts:%d:\n", n, gridSize, blockSize, tot_waves);
|
||||
printf("Launching the kernel on the GPU\n");
|
||||
// Execute the kernel
|
||||
hipLaunchKernelGGL(vecCopy, dim3(gridSize), dim3(blockSize), 0, 0, d_a, d_b, d_c, n,stride);
|
||||
hipDeviceSynchronize( );
|
||||
printf("Finished executing kernel\n");
|
||||
// Copy array back to host
|
||||
HIP_ASSERT(hipMemcpy( h_c, d_c, bytes, hipMemcpyDeviceToHost));
|
||||
printf("Finished copying the output vector from the GPU to the CPU\n");
|
||||
|
||||
//Compute for CPU
|
||||
for(i=0; i <n; i++)
|
||||
{
|
||||
// h_verify_c[i*stride] = h_a[i*stride] + h_b[i*stride];
|
||||
h_verify_c[i*stride] = h_a[i*stride] ;
|
||||
}
|
||||
|
||||
|
||||
//Verfiy results
|
||||
for(i=0; i <n; i++)
|
||||
{
|
||||
if (abs(h_verify_c[i*stride] - h_c[i*stride]) > 1e-5)
|
||||
{
|
||||
printf("Error at position i %d, Expected: %f, Found: %f \n", i, h_c[i], d_c[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// printf("Printing few elements from the output vector\n");
|
||||
|
||||
for(i=0; i < 20; i++)
|
||||
{
|
||||
// printf("Output[%d]:%f\n",i, h_c[i]);
|
||||
}
|
||||
|
||||
printf("Releasing GPU memory\n");
|
||||
|
||||
// Release device memory
|
||||
HIP_ASSERT(hipFree(d_a));
|
||||
HIP_ASSERT(hipFree(d_b));
|
||||
HIP_ASSERT(hipFree(d_c));
|
||||
|
||||
// Release host memory
|
||||
printf("Releasing CPU memory\n");
|
||||
free(h_a);
|
||||
free(h_b);
|
||||
free(h_c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
################################################################################
|
||||
# Copyright (c) 2021 - 2022 Advanced Micro Devices, Inc. All rights reserved.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
OMNIPERF_HOME = Path(__file__).resolve().parent
|
||||
|
||||
# OMNIPERF INFO
|
||||
PROG = "omniperf"
|
||||
SOC_LIST = ["mi50", "mi100", "mi200"]
|
||||
DISTRO_MAP = {
|
||||
"platform:el8": "rhel8",
|
||||
"15.3": "sle15sp3",
|
||||
}
|
||||
version = os.path.join(OMNIPERF_HOME.parent, "VERSION")
|
||||
try:
|
||||
with open(version, "r") as file:
|
||||
VER = file.read().replace("\n", "")
|
||||
except EnvironmentError:
|
||||
print("ERROR: Cannot find VERSION file at {}".format(version))
|
||||
sys.exit(1)
|
||||
@@ -0,0 +1,5 @@
|
||||
/build*
|
||||
/_build
|
||||
/_doxygen
|
||||
/.gitinfo
|
||||
/omniperf.dox
|
||||
@@ -0,0 +1,20 @@
|
||||
# Minimal makefile for Sphinx documentation
|
||||
#
|
||||
|
||||
# You can set these variables from the command line, and also
|
||||
# from the environment for the first two.
|
||||
SPHINXOPTS ?=
|
||||
SPHINXBUILD ?= sphinx-build
|
||||
SOURCEDIR = .
|
||||
BUILDDIR = _build
|
||||
|
||||
# Put it first so that "make" without argument is like "make help".
|
||||
help:
|
||||
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
|
||||
.PHONY: help Makefile
|
||||
|
||||
# Catch-all target: route all unknown targets to Sphinx using the new
|
||||
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
|
||||
%: Makefile
|
||||
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
|
||||
@@ -0,0 +1,168 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Configuration file for the Sphinx documentation builder.
|
||||
#
|
||||
# This file does only contain a selection of the most common options. For a
|
||||
# full list see the documentation:
|
||||
# http://www.sphinx-doc.org/en/master/config
|
||||
|
||||
# -- Path setup --------------------------------------------------------------
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#
|
||||
import os
|
||||
import sys
|
||||
import subprocess as sp
|
||||
|
||||
sys.path.insert(0, os.path.abspath(".."))
|
||||
|
||||
repo_version = "unknown"
|
||||
# Determine short version by file in repo
|
||||
if os.path.isfile("../../VERSION"):
|
||||
with open("../../VERSION") as f:
|
||||
repo_version = f.readline().strip()
|
||||
|
||||
|
||||
def install(package):
|
||||
sp.call([sys.executable, "-m", "pip", "install", package])
|
||||
|
||||
|
||||
# -- Project information -----------------------------------------------------
|
||||
|
||||
project = "Omniperf"
|
||||
copyright = "2022, Audacious Software Group"
|
||||
author = "Audacious Software Group"
|
||||
|
||||
# The short X.Y version
|
||||
version = repo_version
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = ""
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
||||
install("sphinx_rtd_theme")
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
"sphinx.ext.githubpages",
|
||||
"myst_parser",
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ["_templates"]
|
||||
|
||||
# The suffix(es) of source filenames.
|
||||
# You can specify multiple suffix as a list of string:
|
||||
source_suffix = {
|
||||
".rst": "restructuredtext",
|
||||
".txt": "markdown",
|
||||
".md": "markdown",
|
||||
}
|
||||
|
||||
from recommonmark.parser import CommonMarkParser
|
||||
|
||||
source_parsers = {".md": CommonMarkParser}
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = "index"
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#
|
||||
# This is also used if you do content translation via gettext catalogs.
|
||||
# Usually you set "language" from the command line for these cases.
|
||||
language = "en"
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
# This pattern also affects html_static_path and html_extra_path.
|
||||
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = None
|
||||
|
||||
|
||||
# -- Options for HTML output -------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = "sphinx_rtd_theme"
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ["_static"]
|
||||
|
||||
|
||||
# -- Options for HTMLHelp output ---------------------------------------------
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = "Omniperfdoc"
|
||||
|
||||
html_theme_options = {
|
||||
# "analytics_id": "G-1HLBBRSTT9", # Provided by Google in your dashboard
|
||||
# "analytics_anonymize_ip": False,
|
||||
"logo_only": False,
|
||||
"display_version": True,
|
||||
"prev_next_buttons_location": "bottom",
|
||||
"style_external_links": False,
|
||||
"vcs_pageview_mode": "",
|
||||
# 'style_nav_header_background': 'white',
|
||||
# Toc options
|
||||
"collapse_navigation": True,
|
||||
"sticky_navigation": True,
|
||||
"navigation_depth": 4,
|
||||
"includehidden": True,
|
||||
"titles_only": False,
|
||||
}
|
||||
|
||||
from pygments.styles import get_all_styles
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
styles = list(get_all_styles())
|
||||
preferences = ("emacs", "pastie", "colorful")
|
||||
for pref in preferences:
|
||||
if pref in styles:
|
||||
pygments_style = pref
|
||||
break
|
||||
|
||||
from recommonmark.transform import AutoStructify
|
||||
|
||||
# app setup hook
|
||||
def setup(app):
|
||||
app.add_config_value(
|
||||
"recommonmark_config",
|
||||
{
|
||||
"auto_toc_tree_section": "Contents",
|
||||
"enable_eval_rst": True,
|
||||
"enable_auto_doc_ref": False,
|
||||
},
|
||||
True,
|
||||
)
|
||||
app.add_transform(AutoStructify)
|
||||
app.add_config_value("docstring_replacements", {}, True)
|
||||
app.connect("source-read", replaceString)
|
||||
|
||||
|
||||
# function to replace version string througout documentation
|
||||
|
||||
|
||||
def replaceString(app, docname, source):
|
||||
result = source[0]
|
||||
for key in app.config.docstring_replacements:
|
||||
result = result.replace(key, app.config.docstring_replacements[key])
|
||||
source[0] = result
|
||||
|
||||
|
||||
docstring_replacements = {"{__VERSION__}": version}
|
||||
@@ -0,0 +1,32 @@
|
||||
# FAQ
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 4
|
||||
```
|
||||
|
||||
**1. How do I export profiling data I've already generated using Omniperf?**
|
||||
|
||||
In order to interact with the Grafana GUI you must sync data with the MongoDB backend. This interaction is done through ***database*** mode.
|
||||
|
||||
Simply pass the directory of your desired workload like so,
|
||||
```shell
|
||||
$ omniperf database --import -w <path-to-results> -H <hostname> -u <username> -t <team-name>
|
||||
```
|
||||
**2. python ast error: 'Constant' object has no attribute 'kind'**
|
||||
|
||||
This comes from a bug in the default astunparse 1.6.3 with python 3.8. Seems good with python 3.7 and 3.9.
|
||||
|
||||
Workaround:
|
||||
```shell
|
||||
$ pip3 uninstall astunparse
|
||||
$ pip3 astunparse
|
||||
```
|
||||
|
||||
**3. tabulate doesn't print properly**
|
||||
Workaround:
|
||||
```shell
|
||||
$ export LC_ALL=C.UTF-8
|
||||
$ export LANG=C.UTF-8
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
# Getting Started
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 4
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
1. **Launch & Profile the target application with the command line profiler**
|
||||
|
||||
The command line profiler launches the target application, calls the rocProfiler API, and collects profile results for the specified kernels, dispatches, and/or ipblock’s.
|
||||
|
||||
To collect the default set of data for all kernels in the target application, launch:
|
||||
```shell
|
||||
$ omniperf profile -n vcopy -- ./vcopy 1048576 256
|
||||
```
|
||||
The app runs, each kernel is launched, and profiling results are generated. By default, results are written to ./workloads/\<name>. To collect all requested profile information, it may be required to replay kernels multiple times.
|
||||
|
||||
2. **Customize data collection**
|
||||
|
||||
Options are available to specify for which kernels data should be collected.
|
||||
`-k`/`--kernel` enables filtering kernels by name. `-d`/`--dispatch` enables filtering based on dispatch ID. `-b`/`--ipblocks` enables profiling on one or more IP Block(s).
|
||||
|
||||
To view available metrics by IP Block you can always use `--list-metrics` to view a list of all available metrics organized by IP Block.
|
||||
```shell
|
||||
$ omniperf analyze --list-metrics <sys_arch>
|
||||
```
|
||||
Note that filtering can also be applied after the fact, at the analysis stage, however filtering at the profiling level will often speed up your overall profiling run time.
|
||||
|
||||
3. **Analyze at the command line**
|
||||
|
||||
After generating a local output folder (./workloads/\<name>), the command line tool can also be used to quickly interface with profiling results. View different metrics derived from your profiled results and get immediate access all metrics organized by IP block.
|
||||
|
||||
If no kernel, dispatch, or ipblock filters are applied at this stage, analysis will be reflective of the entirety of the profiling data.
|
||||
|
||||
To interact with profiling results from a different session, users just provide the workload path. `-p`/`--path` enables users to analyze existing profiling data in the Omniperf CLI.
|
||||
|
||||
4. **Analyze in the Grafana GUI**
|
||||
|
||||
To conduct a more in-depth analysis of profiling results we recommend users utilize the Omniperf Grafana GUI. To interact with profiling results, users must import their data to the MongoDB instance included in the Omniperf dockerfile.
|
||||
|
||||
To interact with Grafana GUI data, stored in the Omniperf DB, users can enter ***database*** mode. For example:
|
||||
```shell
|
||||
$ omniperf database --import [CONNECTION OPTIONS]
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Modes
|
||||
Modes change the fundamental behavior of the Omniperf command line tool. Depending on which mode is chosen, different command line options become available.
|
||||
|
||||
- **Profile**: Target application is launched on the local system utilizing AMD’s [ROC Profiler](https://github.com/ROCm-Developer-Tools/rocprofiler). Depending on the profiling options chosen, selected kernels, dispatches, and/or IP Blocks in the application are profiled and results are stored locally in an output folder (./workloads/\<name>).
|
||||
|
||||
```shell
|
||||
$ omniperf profile --help
|
||||
```
|
||||
|
||||
- **Analyze**: Profiling data from `-p`/`--path` directory is loaded into the Omniperf CLI analyzer where users have immediate access to profiling results and generated metrics. Metrics are quickly generated from the entirety of your profiled application or a subset you’ve identified through the Omniperf CLI analysis filters.
|
||||
|
||||
To gererate a lightweight GUI interface users can add the `--gui` flag to their analysis command.
|
||||
|
||||
This mode is designed to be a middle ground to the highly detailed Omniperf Grafana GUI and is great for users who want immediate access to an IP Block they’re already familiar with.
|
||||
|
||||
```shell
|
||||
$ omniperf analyze --help
|
||||
```
|
||||
|
||||
- **Database**: Our detailed Grafana GUI is built on a MongoDB database. `--import` profiling results to the DB to interact with the workload in Grafana or `--remove` the workload from the DB.
|
||||
|
||||
Connection options will need to be specified. See the [*Omniperf Performance Analysis*](performance_analysis.md#omniperf-grafana-gui-import) section for more details on this.
|
||||
|
||||
```shell
|
||||
$ omniperf database --help
|
||||
```
|
||||
|
||||
## Basic Operations
|
||||
|
||||
Operation | Mode | Required Arguments
|
||||
:--|:--|:--
|
||||
Profile a workload | profile | `--name`, `-- <profile_cmd>`
|
||||
Standalone roofline analysis | profile | `--name`, `--only-roof`, `-- <profile_cmd>`
|
||||
Import a workload to database | database | `--import`, `--host`, `--username`, `--workload`, `--team`
|
||||
Remove a workload from database | database | `--remove`, `--host`, `--username`, `--workload`, `--team`
|
||||
Interact with profiling results from CLI | analyze | `--path`, `--gui`
|
||||
@@ -0,0 +1,260 @@
|
||||
# Omniperf Grafana GUI Analyzer
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 4
|
||||
```
|
||||
|
||||
## Features
|
||||
The Omniperf Grafana GUI Analyzer supports the following features to facilitate MI GPU performance profiling and analysis:
|
||||
|
||||
- System and IP-Block Speed-of-Light (SOL)
|
||||
- Multiple normalization options, including per-cycle, per-wave, per-kernel and per-second.
|
||||
- Baseline comparisons
|
||||
- Regex based Dispatch ID filtering
|
||||
- Roofline Analysis
|
||||
- Detailed per IP Block performance counters and metrics
|
||||
- CPC/CPF
|
||||
- SPI
|
||||
- SQ
|
||||
- SQC
|
||||
- TA/TD
|
||||
- TCP
|
||||
- TCC (both aggregated and per-channel perf info)
|
||||
|
||||
### Speed-of-Light
|
||||
Speed-of-light panels are provided at both the system and per IP block level to help diagnosis performance bottlenecks. The performance numbers of the workload under testing are compared to the theoretical maximum, (e.g. floating point operations, bandwidth, cache hit rate, etc.), to indicate the available room to further utilize the hardware capability.
|
||||
|
||||
### Multi Normalization
|
||||
|
||||
Multiple performance number normalizations are provided to allow performance inspection within both HW and SW context. The following normalizations are permitted:
|
||||
- per cycle
|
||||
- per wave
|
||||
- per kernel
|
||||
- per second
|
||||
|
||||
### Baseline Comparison
|
||||
Omniperf enables baseline comparison to allow checking A/B effect. The current release limits the baseline comparison to the same SoC. Cross comparison between SoCs is in development.
|
||||
|
||||
For both the Current Workload and the Baseline Workload, one can independently setup the following filters to allow fine grained comparions:
|
||||
- Workload Name
|
||||
- GPU ID filtering (multi selection)
|
||||
- Kernel Name filtering (multi selection)
|
||||
- Dispatch ID filtering (Regex filtering)
|
||||
- Omniperf Panels (multi selection)
|
||||
|
||||
### Regex based Dispatch ID filtering
|
||||
This release enables regex based dispatch ID filtering to flexibly choose the kernel invocations. One may refer to [Regex Numeric Range Generator](https://3widgets.com/), to generate typical number ranges.
|
||||
|
||||
For example, if one wants to inspect Dispatch Range from 17 to 48, inclusive, the corresponding regex is : **(1[7-9]|[23]\d|4[0-8])**. The generated express can be copied over for filtering.
|
||||
|
||||
### Incremental Profiling
|
||||
Omniperf supports incremental profiling to significantly speed up performance analysis.
|
||||
|
||||
> Refer to [*IP Block profiling*](performance_analysis.md#ip-block-profiling) section for this command.
|
||||
|
||||
By default, the entire application is profiled to collect perfmon counter for all IP blocks, giving a system level view of where the workload stands in terms of performance optimization opportunities and bottlenecks.
|
||||
|
||||
After that one may focus on only a few IP blocks, (e.g., L1 Cache or LDS) to closely check the effect of software optimizations, without performing application replay for all other IP Blocks. This saves lots of compute time. In addition, the prior profiling results for other IP blocks are not overwritten. Instead, they can be merged during the import to piece together the system view.
|
||||
|
||||
### Color Coding
|
||||
The uniform color coding is applied to most visualizations (bars, table, diagrams etc). Typically, Yellow color means over 50%, while Red color mean over 90% percent, for easy inspection.
|
||||
|
||||
### Global Variables and Configurations
|
||||
|
||||

|
||||
|
||||
## Omniperf Panels
|
||||
|
||||
### Overview
|
||||
|
||||
There are currently 18 main panel categories available for analyzing the compute workload performance. Each category contains several panels for close inspection of the system performance.
|
||||
|
||||
- Kernel Statistics
|
||||
- Kernel time histogram
|
||||
- Top Ten bottleneck kernels
|
||||
- System Speed-of-Light
|
||||
- Speed-of-Light
|
||||
- System Info table
|
||||
- Memory Chart Analysis
|
||||
- Roofline Analysis
|
||||
- FP32/FP64
|
||||
- FP16/INT8
|
||||
- Command Processor
|
||||
- Command Processor - Fetch (CPF)
|
||||
- Command Processor - Controller (CPC)
|
||||
- Shader Processing Input (SPI)
|
||||
- SPI Stats
|
||||
- SPI Resource Allocations
|
||||
- Wavefront Launch
|
||||
- Wavefront Launch Stats
|
||||
- Wavefront runtime stats
|
||||
- per-SE Wavefront Scheduling performance
|
||||
- Wavefront Lifetime
|
||||
- Wavefront lifetime breakdown
|
||||
- per-SE wavefront life (average)
|
||||
- per-SE wavefront life (histogram)
|
||||
- Wavefront Occupancy
|
||||
- per-SE wavefront occupancy
|
||||
- per-CU wavefront occupancy
|
||||
- Compute Unit - Instruction Mix
|
||||
- per-wave Instruction mix
|
||||
- per-wave VALU Arithmetic instruction mix
|
||||
- per-wave MFMA Arithmetic instruction mix
|
||||
- Compute Unit - Compute Pipeline
|
||||
- Speed-of-Light: Compute Pipeline
|
||||
- Arithmetic OPs count
|
||||
- Compute pipeline stats
|
||||
- Memory latencies
|
||||
- Local Data Share (LDS)
|
||||
- Speed-of-Light: LDS
|
||||
- LDS stats
|
||||
- Instruction Cache
|
||||
- Speed-of-Light: Instruction Cache
|
||||
- Instruction Cache Accesses
|
||||
- Constant Cache
|
||||
- Speed-of-Light: Constant Cache
|
||||
- Constant Cache Accesses
|
||||
- Constant Cache - L2 Interface stats
|
||||
- Texture Address and Texture Data
|
||||
- Texture Address (TA)
|
||||
- Texture Data (TD)
|
||||
- L1 Cache
|
||||
- Speed-of-Light: L1 Cache
|
||||
- L1 Cache Accesses
|
||||
- L1 Cache Stalls
|
||||
- L1 - L2 Transactions
|
||||
- L1 - UTCL1 Interface stats
|
||||
- L2 Cache
|
||||
- Speed-of-Light: L2 Cache
|
||||
- L2 Cache Accesses
|
||||
- L2 - EA Transactions
|
||||
- L2 - EA Stalls
|
||||
- L2 Cache Per Channel Performance
|
||||
- Per-channel L2 Hit rate
|
||||
- Per-channel L1-L2 Read requests
|
||||
- Per-channel L1-L2 Write Requests
|
||||
- Per-channel L1-L2 Atomic Requests
|
||||
- Per-channel L2-EA Read requests
|
||||
- Per-channel L2-EA Write requests
|
||||
- Per-channel L2-EA Atomic requests
|
||||
- Per-channel L2-EA Read latency
|
||||
- Per-channel L2-EA Write latency
|
||||
- Per-channel L2-EA Atomic latency
|
||||
- Per-channel L2-EA Read stall (I/O, GMI, HBM)
|
||||
- Per-channel L2-EA Write stall (I/O, GMI, HBM, Starve)
|
||||
|
||||
Most panels are designed around a specific IP block to thoroughly understand its behavior. Additional panels, including custom panels, could also be added to aid the performance analysis.
|
||||
|
||||
### System Info Panel
|
||||

|
||||
### Kernel Statistics
|
||||
|
||||
#### Kernel Time Histogram
|
||||

|
||||
#### Top Bottleneck Kernels
|
||||

|
||||
#### Top Bottleneck Dispatches
|
||||

|
||||
#### Current and Baseline Dispatch IDs (Filtered)
|
||||

|
||||
|
||||
### System Speed-of-Light
|
||||

|
||||
|
||||
### Memory Chart Analysis
|
||||
> Note: The Memory Chart Analysis support multiple normalizations. Due to the space limit, all transactions, when normalized to per-sec, default to unit of Billion transactions per second.
|
||||
|
||||

|
||||
|
||||
### Roofline Analysis
|
||||

|
||||
### Command Processor
|
||||

|
||||
### Shader Processing Input (SPI)
|
||||

|
||||
### Wavefront Launch
|
||||

|
||||
|
||||
### Compute Unit - Instruction Mix
|
||||
#### Instruction Mix
|
||||

|
||||
#### VALU Arithmetic Instruction Mix
|
||||

|
||||
#### MFMA Arithmetic Instruction Mix
|
||||

|
||||
#### VMEM Arithmetic Instruction Mix
|
||||

|
||||
|
||||
### Compute Unit - Compute Pipeline
|
||||
#### Speed-of-Light
|
||||

|
||||
#### Compute Pipeline Stats
|
||||

|
||||
#### Arithmetic Operations
|
||||

|
||||
#### Memory Latencies
|
||||

|
||||
|
||||
### Local Data Share (LDS)
|
||||
#### Speed-of-Light
|
||||

|
||||
#### LDS Stats
|
||||

|
||||
|
||||
### Instruction Cache
|
||||
#### Speed-of-Light
|
||||

|
||||
#### Instruction Cache Stats
|
||||

|
||||
|
||||
### Scalar L1D Cache
|
||||
#### Speed-of-Light
|
||||

|
||||
#### Constant Cache Stats
|
||||

|
||||
#### Constant Cache - L2 Interface
|
||||

|
||||
|
||||
### Texture Address and Texture Data
|
||||
#### Texture Address (TA)
|
||||

|
||||
#### Texture Data (TD)
|
||||

|
||||
|
||||
### Vector L1D Cache
|
||||
#### Speed-of-Light
|
||||

|
||||
#### Vector L1D Cache Accesses
|
||||

|
||||
#### L1 Cache Stalls
|
||||

|
||||
#### L1 - L2 Transactions
|
||||

|
||||
#### L1 - UTCL1 Interface Stats
|
||||

|
||||
|
||||
### L2 Cache
|
||||
#### Speed-of-Light
|
||||

|
||||
#### L2 Cache Accesses
|
||||

|
||||
#### L2 - EA Transactions
|
||||

|
||||
#### L2 - EA Stalls
|
||||

|
||||
|
||||
### L2 Cache Per Channel Performance
|
||||
#### L1-L2 Transactions
|
||||

|
||||
#### L2-EA Transactions
|
||||

|
||||
#### L2-EA Latencies
|
||||

|
||||
#### L2-EA Stalls
|
||||

|
||||
#### L2-EA Write Stalls
|
||||

|
||||
#### L2-EA Write Starvation
|
||||

|
||||
@@ -0,0 +1,19 @@
|
||||
# Omniperf High Level Design
|
||||
|
||||
```eval_rst
|
||||
.. toctree::
|
||||
:glob:
|
||||
:maxdepth: 4
|
||||
```
|
||||
|
||||
The [Omniperf](https://github.com/AMDResearch/omniperf) Tool is architecturally composed of three major components, as shown in the following figure.
|
||||
|
||||
- **Omniperf Profiling**: Acquire raw performance counters via application replay based on the [ROC Profiler](https://github.com/ROCm-Developer-Tools/rocprofiler). A set of MI200 specific micro benchmarks are also run to acquire the hierarchical roofline data.
|
||||
|
||||
- **Omniperf Grafana Analyzer**:
|
||||
- *Grafana database import*: All raw performance counters are imported into the backend MongoDB database for Grafana GUI analysis and visualization.
|
||||
- *Grafana GUI Analyzer*: A Grafana dashboard is designed to retrieve the raw counters info from the backend database. It also creates the relevant performance metrics and visualization.
|
||||
- **Omniperf Standalone GUI Analyzer**: A standalone GUI is provided to enable performance analysis without importing data into the backend database.
|
||||
|
||||

|
||||
|
||||
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 47 KiB |