From 0d9389ddc7e3615a9cd6b34d482501b0566a3abe Mon Sep 17 00:00:00 2001 From: AfonsoSantos96 Date: Sun, 15 Oct 2023 12:28:35 +0100 Subject: [PATCH 1/6] doc(readme): update CI readme to include HIS metrics checker Signed-off-by: Afonso Santos --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c3fa7de..d6894ce 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ The following checks are provided: - tidy and cppcheck: C static analysis - misra: MISRA C checking - asmfmt: assembly formatting +- his-check: HIS code metrics checker ## Setting up CI in a repository @@ -98,7 +99,7 @@ repo, except rules related to the commits themselves such as gitlint. For example, for a repo composed of a mix of C an Python sources: ``` -ci: license pylint format-check tidy cppcheck misra +ci: license pylint format-check tidy cppcheck misra his-check ``` ## Using the Docker Container From 2921adf03fe1b0b4bc96a12e87f3651e36dd2264 Mon Sep 17 00:00:00 2001 From: AfonsoSantos96 Date: Thu, 18 May 2023 10:31:35 +0100 Subject: [PATCH 2/6] feat(HIS): add HIS script into makefile python scripts Signed-off-by: Afonso Santos --- Makefile | 1 + his_checker.py | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 his_checker.py diff --git a/Makefile b/Makefile index d51c13f..2c99f4c 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,7 @@ python_scripts:= \ $(root_dir)/license_check.py \ $(root_dir)/spell_check.py \ $(root_dir)/rops_check.py + $(root_dir)/his_checker.py $(call ci, pylint, $(python_scripts)) yaml_files:= \ diff --git a/his_checker.py b/his_checker.py new file mode 100644 index 0000000..d8ddfc1 --- /dev/null +++ b/his_checker.py @@ -0,0 +1,2 @@ + +# wip \ No newline at end of file From ffc5a6edf8dc4c963abed814328e7a014907d69a Mon Sep 17 00:00:00 2001 From: AfonsoSantos96 Date: Thu, 18 May 2023 10:42:33 +0100 Subject: [PATCH 3/6] feat(HIS): add HIS to C workflow Signed-off-by: Afonso Santos --- .github/workflows/templates/c.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/templates/c.yaml b/.github/workflows/templates/c.yaml index a62798c..b8fceaf 100644 --- a/.github/workflows/templates/c.yaml +++ b/.github/workflows/templates/c.yaml @@ -70,3 +70,15 @@ jobs: submodules: recursive - run: git config --global --add safe.directory /__w/bao-partitioner/bao-partitioner - run: make PLATFORM=${{ matrix.platform }} rops-check + + his: + runs-on: ubuntu-latest + container: baoproject/bao:latest + strategy: + matrix: + platform: ["qemu-aarch64-virt", "qemu-riscv64-virt"] + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + - run: make PLATFORM=${{ matrix.platform }} his-check From d92a8d4a22e4820766155a08aa6e70a209a7a87c Mon Sep 17 00:00:00 2001 From: AfonsoSantos96 Date: Thu, 18 May 2023 10:44:47 +0100 Subject: [PATCH 4/6] feat(HIS): add HIS checker to ci makefile Signed-off-by: Afonso Santos --- ci.mk | 21 +++++++++++++++++++++ his_checker.py | 24 +++++++++++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/ci.mk b/ci.mk index 190f6fa..0e8967a 100644 --- a/ci.mk +++ b/ci.mk @@ -360,4 +360,25 @@ endef ############################################################################# +# HIS Checker +# Use this rule to run the HIS add-on checker: +# make his-check +# @param files space separated list of files (with path) +# @example $(call ci, his, file1.c file2.c file3.h) + +his_check_script:=$(ci_dir)/his_checker.py + +his-check: + @$(his_check_script) $(_his_files) + +.PHONY: his-check +non_build_targets+=his-check + +define his +_his_files+=$1 +endef + +############################################################################# + ci=$(eval $(call $1, $2, $3, $4, $5, $6, $7, $8, $9)) + diff --git a/his_checker.py b/his_checker.py index d8ddfc1..2ec03e9 100644 --- a/his_checker.py +++ b/his_checker.py @@ -1,2 +1,24 @@ +#!/bin/python3 -# wip \ No newline at end of file +# SPDX-License-Identifier: Apache-2.0 +# Copyright (c) Bao Project and Contributors. All rights reserved + +""" +Generating HIS metrics check +""" + +import sys +import argparse + +if __name__ == "__main__": + METRICS_LIST = [] + CHECK_FAIL = 0 + PARSER = argparse.ArgumentParser() + PARSER.add_argument("files", nargs="+", help="The files to process") + ARGS = PARSER.parse_args() + + for metric in METRICS_LIST: + CHECK_FAIL += metric(ARGS) + + if CHECK_FAIL: + sys.exit(-1) From 9f7c6d612acba9cf2ff657f6e4c41974916cfab7 Mon Sep 17 00:00:00 2001 From: AfonsoSantos96 Date: Thu, 10 Aug 2023 23:16:58 +0100 Subject: [PATCH 5/6] fix(HIS): change execution permissions of HIS script Signed-off-by: Afonso Santos --- his_checker.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 his_checker.py diff --git a/his_checker.py b/his_checker.py old mode 100644 new mode 100755 From 4a2edbb1434f8b94e759d5e5a919329e0ee0dab6 Mon Sep 17 00:00:00 2001 From: Daniel Oliveira Date: Fri, 16 Feb 2024 18:17:45 +0000 Subject: [PATCH 6/6] ref(his): change script to allow individual metric/threshold spec Signed-off-by: Daniel Oliveira --- Makefile | 2 +- ci.mk | 4 +- his_checker.py | 201 ++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 201 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 2c99f4c..1c9eba3 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ python_scripts:= \ $(root_dir)/misra/deviation_suppression.py \ $(root_dir)/license_check.py \ $(root_dir)/spell_check.py \ - $(root_dir)/rops_check.py + $(root_dir)/rops_check.py \ $(root_dir)/his_checker.py $(call ci, pylint, $(python_scripts)) diff --git a/ci.mk b/ci.mk index 0e8967a..6da478e 100644 --- a/ci.mk +++ b/ci.mk @@ -369,13 +369,15 @@ endef his_check_script:=$(ci_dir)/his_checker.py his-check: - @$(his_check_script) $(_his_files) + @$(his_check_script) -f $(_his_files) $(_his_metrics) -e $(_his_exclude) .PHONY: his-check non_build_targets+=his-check define his _his_files+=$1 +_his_metrics+=$2 +_his_exclude+=$3 endef ############################################################################# diff --git a/his_checker.py b/his_checker.py index 2ec03e9..9f2a17d 100755 --- a/his_checker.py +++ b/his_checker.py @@ -10,15 +10,208 @@ import sys import argparse +def process_calling(files, threshold): + """ + Process the calling metric + """ + print(f"Processing CALLING metric with threshold [0-{threshold}] for files: \ + {', '.join(files)}") + + return 0 + +def process_calls(files, threshold): + """ + Process the calls metric + """ + + print(f"Processing CALLS metric with threshold [0-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_comf(files, threshold): + """ + Process the comf metric + """ + + print(f"Processing COMF metric with threshold >{threshold} for files: {', '.join(files)}") + + return 0 + +def process_goto(files, threshold): + """ + Process the goto metric + """ + + print(f"Processing GOTO metric with threshold {threshold} for files: {', '.join(files)}") + + return 0 + +def process_level(files, threshold): + """ + Process the level metric + """ + + print(f"Processing LEVEL metric with threshold [0-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_param(files, threshold): + """ + Process the parameters metric + """ + + print(f"Processing PARAMETERS metric with threshold [0-{threshold}] for files: \ + {', '.join(files)}") + + return 0 + +def process_path(files, threshold): + """ + Process the path metric + """ + + print(f"Processing PATH metric with threshold [1-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_return(files, threshold): + """ + Process the return metric + """ + + print(f"Processing RETURN metric with threshold [0-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_stmt(files, threshold): + """ + Process the stmt metric + """ + + print(f"Processing STMT metric with threshold [1-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_vocf(files, threshold): + """ + Process the vocf metric + """ + print(f"Processing VOCF metric with threshold [1-{threshold}] for files: {', '.join(files)}") + + return 0 + +def process_ap_cg_cycle(files, threshold): + """ + Process the ap_cg_cycle metric + """ + + print(f"Processing AP_CG_CYCLE metric with threshold {threshold}] for files: \ + {', '.join(files)}") + + return 0 + +def process_v_g(files, threshold): + """ + Process the v_g metric + """ + + print(f"Processing V_G metric with threshold [1-{threshold}] for files: {', '.join(files)}") + + return 0 + +# Define the list of available metrics, their corresponding functions, and their default thresholds +METRICS = { + 'calling' : { + 'function': process_calling, + 'threshold': 5, + }, + 'calls': { + 'function': process_calls, + 'threshold': 7, + }, + 'comf': { + 'function': process_comf, + 'threshold': 0.2, + }, + 'goto': { + 'function': process_goto, + 'threshold': 0, + }, + 'level': { + 'function': process_level, + 'threshold': 4, + }, + 'param': { + 'function': process_param, + 'threshold': 5, + }, + 'path': { + 'function': process_path, + 'threshold': 80, + }, + 'return': { + 'function': process_return, + 'threshold': 1, + }, + 'stmt': { + 'function': process_stmt, + 'threshold': 50, + }, + 'vocf': { + 'function': process_vocf, + 'threshold': 4, + }, + 'ap_cg_cycle': { + 'function': process_ap_cg_cycle, + 'threshold': 0, + }, + 'v_g': { + 'function': process_v_g, + 'threshold': 10, + }, +} + if __name__ == "__main__": - METRICS_LIST = [] CHECK_FAIL = 0 + + # Define the command-line argument parser PARSER = argparse.ArgumentParser() - PARSER.add_argument("files", nargs="+", help="The files to process") + PARSER.add_argument("-f", "--files", nargs="+", help="The files to process", required=True) + + # Add an argument for each metric + for metric, metric_info in METRICS.items(): + PARSER.add_argument(f"--{metric}", type=int, default=metric_info['threshold'], + help=f"Enable {metric} and specify its threshold", metavar="THRESHOLD") + + PARSER.add_argument("-e", "--exclude", nargs="*", default=[], help="The metrics to exclude") + ARGS = PARSER.parse_args() - for metric in METRICS_LIST: - CHECK_FAIL += metric(ARGS) + # Verify that the excluded metrics are valid + for metric in ARGS.exclude: + if metric not in METRICS: # iterate over dictionary directly + print(f"Invalid metric {metric} specified in --exclude") + print(f"Valid metrics are: {', '.join(METRICS)}") # iterate over dictionary directly + sys.exit(-1) + + # Process each metric + for metric, metric_item in METRICS.items(): + # Check if the metric is excluded + if metric in ARGS.exclude: + continue + + # Get the function and threshold for the metric + metric_function = metric_item['function'] + val = getattr(ARGS, metric) + + # Add the number of failures for the metric to the total failure count + CHECK_FAIL += metric_function(ARGS.files, val) + print("--------------------------------------------") + if CHECK_FAIL: + print(f"{metric.upper()} metric failed with {CHECK_FAIL} error(s)\n") + else: + print(f"{metric.upper()} metric passed") + # If there were any failures, exit with an error code if CHECK_FAIL: sys.exit(-1)