Skip to content

Commit ca8e778

Browse files
authored
Added generator table to report.html and compare startpc to the compare_dumps function. (#23)
* Changed compare_dumps * Added log generation to the cmd in compare_dumps in utils.py * Formatted rout to match old format * Added start_hex parameter to compare_dumps and renamed the bash implementation to compare_dumps_bash * Updated compare_dumps * added -iw to compare dumps * Added --comparestartpc to the rivercore compile api * Updated CHANGELOG and installation.rst * Bump version: 1.8.0 → 1.9.0 * Added rivercore comparison * Fixed comparison arguments * Fixed comparison arguments * Fixed arguments of rivercore_comparison * Cleaned up rivercore_comparison * Added generator count table to report.html
1 parent 0f77588 commit ca8e778

File tree

9 files changed

+347
-40
lines changed

9 files changed

+347
-40
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# CHANGELOG
22

33
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4+
## [1.9.0] - 2024-07-10
5+
- added --comparestartpc to river_core compile api
6+
- added river_core comparison command to perform log comparisons without compilation
7+
- added a table to report.html displaying the number of tests from each generator
8+
49
## [1.8.0] - 2024-06-06
510
- added river_core enquire command
611
- added --timeout to the river_core compile api

docs/source/installation.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Output for ``river_core --help``:
183183
184184
Commands:
185185
clean subcommand to clean generated programs.
186+
comparison subcommand to compare compiled test logs.
186187
compile subcommand to compile generated programs.
187188
enquire subcommand to enquire status of tests.
188189
generate subcommand to generate programs.
@@ -207,6 +208,23 @@ Output for ``river_core clean --help``:
207208
--help Show this message and exit.
208209
209210
211+
Output for ``river_core generate --help``:
212+
213+
.. code-block:: console
214+
215+
Usage: river_core comparison [OPTIONS]
216+
217+
subcommand to compare compiled test logs.
218+
219+
Options:
220+
--comparestartpc TEXT Start pc value in Hex for log comparisons
221+
--timeout INTEGER Timeout period for tests
222+
--nproc INTEGER Number of processes dedicated to rivercore framework
223+
-o, --output_dir PATH Output directory [required]
224+
-t, --test_list FILE Test List file to pass [required]
225+
--help Show this message and exit.
226+
227+
210228
Output for ``river_core generate --help``:
211229

212230
.. code-block:: console
@@ -236,6 +254,7 @@ Output for ``river_core compile --help``:
236254
subcommand to compile generated programs.
237255
238256
Options:
257+
--comparestartpc TEXT Start pc value in Hex for log comparisons
239258
--timeout INTEGER Timeout period for tests
240259
--nproc INTEGER Number of processes dedicated to river_core framework
241260
--coverage Enable collection of coverage statistics

river_core/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
__author__ = """InCore Semiconductors"""
66
__email__ = '[email protected]'
7-
__version__ = '1.8.0'
7+
__version__ = '1.9.0'

river_core/main.py

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import click
44
import os
55
from river_core.log import *
6-
from river_core.rivercore import rivercore_clean, rivercore_compile, rivercore_generate, rivercore_merge, rivercore_setup
6+
from river_core.rivercore import rivercore_clean, rivercore_compile, rivercore_generate, rivercore_merge, rivercore_setup, rivercore_comparison
77
from river_core.__init__ import __version__
88
import river_core.constants as constants
99
import river_core.utils as utils
@@ -151,9 +151,14 @@ def setup(config, dut, gen, ref, verbosity):
151151
default = -1,
152152
help = 'Timeout period for tests'
153153
)
154+
@click.option(
155+
'--comparestartpc',
156+
default = '-1',
157+
help = 'Start pc value in Hex for log comparisons'
158+
)
154159
@cli.command()
155160
def compile(config, test_list, coverage, verbosity, dut_stage, ref_stage,
156-
compare, nproc, timeout):
161+
compare, nproc, timeout, comparestartpc):
157162
'''
158163
subcommand to compile generated programs.
159164
'''
@@ -183,7 +188,38 @@ def compile(config, test_list, coverage, verbosity, dut_stage, ref_stage,
183188
'Compare is enabled\nThis will be generating incomplete reports'
184189
)
185190
rivercore_compile(config, test_list, coverage, verbosity, dut_stage,
186-
ref_stage, compare, nproc, timeout)
191+
ref_stage, compare, nproc, timeout,comparestartpc)
192+
@click.option('-t',
193+
'--test_list',
194+
type=click.Path(dir_okay=False, exists=True),
195+
help='Test List file to pass',
196+
required=True)
197+
@click.option('-o',
198+
'--output_dir',
199+
type=click.Path(dir_okay=True, exists=True),
200+
help='Output directory',
201+
required=True)
202+
@click.option(
203+
'--nproc',
204+
default=1,
205+
help='Number of processes dedicated to rivercore framework')
206+
@click.option(
207+
'--timeout',
208+
default = -1,
209+
help = 'Timeout period for tests'
210+
)
211+
@click.option(
212+
'--comparestartpc',
213+
default = '-1',
214+
help = 'Start pc value in Hex for log comparisons'
215+
)
216+
@cli.command()
217+
def comparison(test_list, output_dir, nproc, timeout, comparestartpc):
218+
'''
219+
subcommand to compare compiled test logs.
220+
'''
221+
logger.info(constants.header_temp.format(__version__))
222+
rivercore_comparison(test_list, output_dir, nproc, timeout,comparestartpc)
187223

188224
@click.option('-t',
189225
'--test_list',

river_core/rivercore.py

Lines changed: 123 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import importlib
99
import configparser
1010
import lief
11+
#import time
1112
#import filecmp
1213
import json
1314
import pytest
@@ -26,7 +27,7 @@
2627
yaml.allow_unicode = True
2728
yaml.compact(seq_seq=False, seq_map=False)
2829
from multiprocessing import Pool
29-
30+
startpc = '-1'
3031

3132
# Misc Helper Functions
3233
def sanitise_pytest_json(json):
@@ -197,7 +198,13 @@ def generate_report(output_dir, gen_json_data, target_json_data, ref_json_data,
197198
html_objects['num_failed'] = num_failed
198199
html_objects['num_unav'] = num_unav
199200
html_objects['total_instr'] = total_instr
200-
201+
generator_count = {}
202+
for i in test_dict:
203+
if test_dict[i]['generator'] not in generator_count:
204+
generator_count[test_dict[i]['generator']] = 1
205+
else:
206+
generator_count[test_dict[i]['generator']] += 1
207+
html_objects['generator_count'] = generator_count
201208
if not os.path.exists(report_dir):
202209
os.makedirs(report_dir)
203210

@@ -413,7 +420,7 @@ def rivercore_generate(config_file, verbosity, filter_testgen):
413420

414421

415422
def rivercore_compile(config_file, test_list, coverage, verbosity, dut_flags,
416-
ref_flags, compare, process_count, timeout):
423+
ref_flags, compare, process_count, timeout, comparestartpc):
417424
'''
418425
419426
Function to compile generated assembly programs using the plugin as configured in the config.ini.
@@ -446,6 +453,7 @@ def rivercore_compile(config_file, test_list, coverage, verbosity, dut_flags,
446453
447454
:type compare: bool
448455
'''
456+
449457
logger.level(verbosity)
450458
config = configparser.ConfigParser()
451459
config.read(config_file)
@@ -627,6 +635,8 @@ def rivercore_compile(config_file, test_list, coverage, verbosity, dut_flags,
627635

628636
## Comparing Dumps
629637
if compare:
638+
global startpc
639+
startpc = comparestartpc
630640
test_dict = utils.load_yaml(test_list)
631641
gen_json_data = []
632642
target_json_data = []
@@ -653,7 +663,6 @@ def rivercore_compile(config_file, test_list, coverage, verbosity, dut_flags,
653663
failed_dict_file = output_dir+'/failed_list.yaml'
654664
logger.error(f'Saving failed list of tests in {failed_dict_file}')
655665
utils.save_yaml(failed_dict, failed_dict_file)
656-
657666
# Start checking things after running the commands
658667
# Report generation starts here
659668
# Target
@@ -741,6 +750,116 @@ def rivercore_compile(config_file, test_list, coverage, verbosity, dut_flags,
741750
return 1
742751
if not success:
743752
raise SystemExit(1)
753+
754+
def rivercore_comparison( test_list,output_dir, process_count, timeout, comparestartpc):
755+
'''
756+
757+
Function to compile generated assembly programs using the plugin as configured in the config.ini.
758+
759+
:param config_file: Config.ini file for generation
760+
761+
:param test_list: Test List exported from generate sub-command
762+
763+
:param coverage: Enable coverage merge and stats from the reports
764+
765+
:param verbosity: Verbosity level for the framework
766+
767+
:param dut_flags: Verbosity level for the framework
768+
769+
:param ref_flags: Verbosity level for the framework
770+
771+
:param compare: Verbosity level for the framework
772+
773+
:type config_file: click.Path
774+
775+
:type test_list: click.Path
776+
777+
:type coverage: bool
778+
779+
:type verbosity: str
780+
781+
:type dut_flags: click.Choice
782+
783+
:type ref_flags: click.Choice
784+
785+
:type compare: bool
786+
'''
787+
788+
logger.info('****** Compilation Mode ******')
789+
790+
logger.info(
791+
"The river_core is currently configured to run with following parameters"
792+
)
793+
logger.info("The Output Directory (work_dir) : {0}".format(output_dir))
794+
795+
# Set default values:
796+
target_json = None
797+
ref_json = None
798+
# Load coverage stats
799+
if True:
800+
logger.level("info")
801+
## Comparing Dumps
802+
if True:
803+
global startpc
804+
startpc = comparestartpc
805+
test_dict = utils.load_yaml(test_list)
806+
# parallelized
807+
success = True
808+
items = test_dict.items()
809+
#start = time.time()
810+
with Pool(processes = process_count) as process_pool:
811+
output = process_pool.map(logcomparison, items) #Collecting the return values from each process in the Pool
812+
#stop = time.time()
813+
#logger.warn(stop - start)
814+
#Updating values
815+
for i in output:
816+
success = success and i[0]
817+
test_dict[i[1]]['result'] = i[2]
818+
test_dict[i[1]]['log'] = i[3]
819+
test_dict[i[1]]['num_instr'] = i[4]
820+
utils.save_yaml(test_dict, output_dir+'/result_list.yaml')
821+
failed_dict = {}
822+
for test, attr in test_dict.items():
823+
if attr['result'] == 'Failed' or 'Unavailable' in attr['result']:
824+
failed_dict[test] = attr
825+
826+
if len(failed_dict) != 0:
827+
logger.error(f'Total Tests that Failed :{len(failed_dict)}')
828+
failed_dict_file = output_dir+'/failed_list.yaml'
829+
logger.error(f'Saving failed list of tests in {failed_dict_file}')
830+
utils.save_yaml(failed_dict, failed_dict_file)
831+
832+
if not success:
833+
raise SystemExit(1)
834+
835+
836+
#Helper function for parallel processing
837+
#Returns success,test,attr['result'],attr['log'],attr['numinstr']
838+
def logcomparison(item):
839+
test, attr = item
840+
test_wd = attr['work_dir']
841+
is_self_checking = attr['self_checking']
842+
if not is_self_checking:
843+
if not os.path.isfile(test_wd + '/dut.dump'):
844+
logger.error(f'{test:<30} : DUT dump is missing')
845+
return False, test, 'Unavailable', "DUT dump is missing", None
846+
if not os.path.isfile(test_wd + '/ref.dump'):
847+
logger.error(f'{test:<30} : REF dump is missing')
848+
return False, test, 'Unavailable', 'REF dump is missing', None
849+
compare_start_pc = str(startpc) if str(startpc)!='-1' else ''
850+
result, log, insnsize = utils.compare_dumps(test_wd + '/dut.dump', test_wd + '/ref.dump',compare_start_pc)
851+
else:
852+
if not os.path.isfile(test_wd + '/dut.signature'):
853+
logger.error(f'{test:<30} : DUT signature is missing')
854+
return False, test, 'Unavailable',"DUT signature is missing", None
855+
result, log = utils.self_check(test_wd + '/dut.signature')
856+
insnsize = utils.get_file_size(test_wd + '/dut.dump')
857+
if result == 'Passed':
858+
logger.info(f"{test:<30} : TEST {result.upper()}")
859+
return True, test, result, log, insnsize
860+
else:
861+
logger.error(f"{test:<30} : TEST {result.upper()}")
862+
return False, test, result, log, insnsize
744863

745864

746865
def rivercore_merge(verbosity, db_folders, output, config_file):
@@ -943,32 +1062,6 @@ def rivercore_merge(verbosity, db_folders, output, config_file):
9431062
except:
9441063
logger.info("Couldn't open the browser")
9451064

946-
#Helper function for parallel processing
947-
#Returns success,test,attr['result'],attr['log'],attr['numinstr']
948-
def logcomparison(item):
949-
test, attr = item
950-
test_wd = attr['work_dir']
951-
is_self_checking = attr['self_checking']
952-
if not is_self_checking:
953-
if not os.path.isfile(test_wd + '/dut.dump'):
954-
logger.error(f'{test:<30} : DUT dump is missing')
955-
return False, test, 'Unavailable', "DUT dump is missing", None
956-
if not os.path.isfile(test_wd + '/ref.dump'):
957-
logger.error(f'{test:<30} : REF dump is missing')
958-
return False, test, 'Unavailable', 'REF dump is missing', None
959-
result, log, insnsize = utils.compare_dumps(test_wd + '/dut.dump', test_wd + '/ref.dump')
960-
else:
961-
if not os.path.isfile(test_wd + '/dut.signature'):
962-
logger.error(f'{test:<30} : DUT signature is missing')
963-
return False, test, 'Unavailable',"DUT signature is missing", None
964-
result, log = utils.self_check(test_wd + '/dut.signature')
965-
insnsize = utils.get_file_size(test_wd + '/dut.dump')
966-
if result == 'Passed':
967-
logger.info(f"{test:<30} : TEST {result.upper()}")
968-
return True, test, result, log, insnsize
969-
else:
970-
logger.error(f"{test:<30} : TEST {result.upper()}")
971-
return False, test, result, log, insnsize
9721065
def rivercore_setup(config, dut, gen, ref, verbosity):
9731066
'''
9741067
Function to generate sample plugins

river_core/templates/report.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,23 @@ <h2>Log comparison result:</h2>
295295
<p class="filter" hidden="true">(Un)check the boxes to filter the results.</p><input checked="true" class="filter" data-test-result="passed" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="passed">{{ num_passed }} Passed</span>, <input checked="true" class="filter" data-test-result="failed" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="failed">{{ num_failed }} Failed</span><input checked="true" class="filter" data-test-result="unavailable" name="filter_checkbox" onChange="filter_table(this)" type="checkbox"/><span class="unavailable"> {{ num_unav }} Unavailable</span>
296296
<h3> Out of Total: {{ num_failed + num_passed + num_unav }} Tests </h3>
297297
<h3> Total Instructions Execute: {{ total_instr }} Instructions </h3>
298+
<br>
299+
<table id="generator-type-table">
300+
<thead id="simple-table-head">
301+
<tr>
302+
<th col="name">Generator </th>
303+
<th col="numinsns">Test Count</th>
304+
</tr>
305+
</thead>
306+
{% for generator in generator_count %}
307+
<tbody class= "{{ generator_count[generator]}} simple-table-row" >
308+
<tr>
309+
<td class="col-name">{{ generator }}</td>
310+
<td class="col-numinsns">{{ generator_count[generator] }}</td>
311+
</tr>
312+
</tbody>
313+
{% endfor %}
314+
</table>
298315

299316
<table id="results-table">
300317
<thead id="simple-table-head">

0 commit comments

Comments
 (0)