Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/106475/?format=api
https://patches.dpdk.org/api/patches/106475/?format=api", "web_url": "https://patches.dpdk.org/project/dts/patch/20220125083836.2354-2-junx.dong@intel.com/", "project": { "id": 3, "url": "https://patches.dpdk.org/api/projects/3/?format=api", "name": "DTS", "link_name": "dts", "list_id": "dts.dpdk.org", "list_email": "dts@dpdk.org", "web_url": "", "scm_url": "git://dpdk.org/tools/dts", "webscm_url": "http://git.dpdk.org/tools/dts/", "list_archive_url": "https://inbox.dpdk.org/dts", "list_archive_url_format": "https://inbox.dpdk.org/dts/{}", "commit_url_format": "" }, "msgid": "<20220125083836.2354-2-junx.dong@intel.com>", "list_archive_url": "https://inbox.dpdk.org/dts/20220125083836.2354-2-junx.dong@intel.com", "date": "2022-01-25T08:38:35", "name": "[V2,1/2] framework/*: Add function to support ASan test", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "e13a42459240170cd877b3b7f6d01015bac7835d", "submitter": { "id": 2237, "url": "https://patches.dpdk.org/api/people/2237/?format=api", "name": "Jun Dong", "email": "junx.dong@intel.com" }, "delegate": null, "mbox": "https://patches.dpdk.org/project/dts/patch/20220125083836.2354-2-junx.dong@intel.com/mbox/", "series": [ { "id": 21351, "url": "https://patches.dpdk.org/api/series/21351/?format=api", "web_url": "https://patches.dpdk.org/project/dts/list/?series=21351", "date": "2022-01-25T08:38:34", "name": "Add function to support ASan test", "version": 2, "mbox": "https://patches.dpdk.org/series/21351/mbox/" } ], "comments": "https://patches.dpdk.org/api/patches/106475/comments/", "check": "pending", "checks": "https://patches.dpdk.org/api/patches/106475/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "<dts-bounces@dpdk.org>", "X-Original-To": "patchwork@inbox.dpdk.org", "Delivered-To": "patchwork@inbox.dpdk.org", "Received": [ "from mails.dpdk.org (mails.dpdk.org [217.70.189.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 64653A04AD;\n\tTue, 25 Jan 2022 09:38:46 +0100 (CET)", "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 50CE34285F;\n\tTue, 25 Jan 2022 09:38:46 +0100 (CET)", "from mga12.intel.com (mga12.intel.com [192.55.52.136])\n by mails.dpdk.org (Postfix) with ESMTP id 7E30C4013F\n for <dts@dpdk.org>; Tue, 25 Jan 2022 09:38:44 +0100 (CET)", "from fmsmga003.fm.intel.com ([10.253.24.29])\n by fmsmga106.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 25 Jan 2022 00:38:44 -0800", "from shwdenpg197.ccr.corp.intel.com ([10.253.109.35])\n by fmsmga003-auth.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 25 Jan 2022 00:38:42 -0800" ], "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1643099924; x=1674635924;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=x8Lh0knNZGuGVYcUaYSpJoVRutzotg2haCnVyLUqWCE=;\n b=BF+cHyAXzTTzmTgwR+JFO1/AfeX8r8SC26k0iVrcL+zKKFYKw+tsQqTg\n 3SeDmOOzESzGu1jxQgC+cEArwQckVsqfaQO83miBwxNYchBqCVi1GmqFu\n XPKg2kp3uVHtpjQAjW06AcHoaIZiCNydseNJTAu2l4c6AYsORi/eryTns\n cRR5hkvMj9CmlzFSETmqgTX9MH64xXgh5wwqmsG+wwhYDF5gGJZw7JJd0\n TlRwLnHY4EvbmPnLhpRZ3EIGFEDNyIb88WeIiZ0Q83UoHQBRIjnH46V7N\n JUuNHGko+fvZDpjuNoQxVqoRlK1ySiO3eYaKRRbJtprVDP9t3CvwANakL Q==;", "X-IronPort-AV": [ "E=McAfee;i=\"6200,9189,10237\"; a=\"226232171\"", "E=Sophos;i=\"5.88,314,1635231600\"; d=\"scan'208\";a=\"226232171\"", "E=Sophos;i=\"5.88,314,1635231600\"; d=\"scan'208\";a=\"617542703\"" ], "From": "DongJunX <junx.dong@intel.com>", "To": "dts@dpdk.org", "Cc": "lijuan.tu@intel.com,\n\tqingx.sun@intel.com,\n\tjunx.dong@intel.com", "Subject": "[DTS][V2 1/2] framework/*: Add function to support ASan test", "Date": "Tue, 25 Jan 2022 16:38:35 +0800", "Message-Id": "<20220125083836.2354-2-junx.dong@intel.com>", "X-Mailer": "git-send-email 2.33.1.windows.1", "In-Reply-To": "<20220125083836.2354-1-junx.dong@intel.com>", "References": "<20220125083836.2354-1-junx.dong@intel.com>", "MIME-Version": "1.0", "Content-Transfer-Encoding": "8bit", "X-BeenThere": "dts@dpdk.org", "X-Mailman-Version": "2.1.29", "Precedence": "list", "List-Id": "test suite reviews and discussions <dts.dpdk.org>", "List-Unsubscribe": "<https://mails.dpdk.org/options/dts>,\n <mailto:dts-request@dpdk.org?subject=unsubscribe>", "List-Archive": "<http://mails.dpdk.org/archives/dts/>", "List-Post": "<mailto:dts@dpdk.org>", "List-Help": "<mailto:dts-request@dpdk.org?subject=help>", "List-Subscribe": "<https://mails.dpdk.org/listinfo/dts>,\n <mailto:dts-request@dpdk.org?subject=subscribe>", "Errors-To": "dts-bounces@dpdk.org" }, "content": "V2:\n- Modify filter bound format in conf file.\n- Add json foramt test report and text statistics info file.\n\nV1: \n- Add one dts run parameter to control asan test.\n- Add asan.cfg in conf folder to config asan test.\n- Add one module of asan_test to support asan test.\n\nSigned-off-by: DongJunX <junx.dong@intel.com>\n---\n conf/asan.cfg | 6 +\n framework/asan_test.py | 393 +++++++++++++++++++++++++++++++++++++++++\n framework/dts.py | 10 +-\n main.py | 6 +-\n 4 files changed, 412 insertions(+), 3 deletions(-)\n create mode 100644 conf/asan.cfg\n create mode 100644 framework/asan_test.py", "diff": "diff --git a/conf/asan.cfg b/conf/asan.cfg\nnew file mode 100644\nindex 00000000..18537f0b\n--- /dev/null\n+++ b/conf/asan.cfg\n@@ -0,0 +1,6 @@\n+[ASan]\n+# Filter bounds pairs, use colon split bounds, use comma split pairs\n+filter_bounds=LeakSanitizer:SUMMARY,AddressSanitizer:SUMMARY\n+\n+# ASan meson build related params\n+build_param=-Dbuildtype=debug -Db_lundef=false -Db_sanitize=address\ndiff --git a/framework/asan_test.py b/framework/asan_test.py\nnew file mode 100644\nindex 00000000..cc12c994\n--- /dev/null\n+++ b/framework/asan_test.py\n@@ -0,0 +1,393 @@\n+import configparser\n+import os\n+import re\n+import xlrd\n+import sys\n+\n+DTS_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\n+sys.path.insert(0, DTS_PATH)\n+\n+from contextlib import contextmanager\n+from framework.excel_reporter import ExcelReporter\n+from .json_reporter import JSONReporter\n+from .stats_reporter import StatsReporter\n+from framework.settings import FOLDERS\n+from framework.test_result import Result\n+\n+CONFIG_FILE_NAME = 'asan.cfg'\n+CONFIG_FILE_PARENT_DIR = 'conf'\n+ASan_CONFIG_SECT = 'ASan'\n+ASan_FILTER_BOUNDS = 'filter_bounds'\n+ASan_PARAM_KEY = 'build_param'\n+ORIGIN_TEST_REPORT_FILE = 'test_results.xls'\n+NEW_TEST_REPORT_FILE = 'asan_test_results.xls'\n+NEW_JSON_REPORT_FILE = 'asan_test_results.json'\n+NEW_STATS_REPORT_FILE = 'asan_statistics.txt'\n+MIN_LENGTH_OF_FILTERED_OUTPUT = 50\n+COMMAND_PATTERN_OF_ADDRESS_RANDOM_SWITCH = 'echo %s > /proc/sys/kernel/randomize_va_space'\n+COMMAND_OF_CLOSE_ADDRESS_RANDOM = COMMAND_PATTERN_OF_ADDRESS_RANDOM_SWITCH % 0\n+COMMAND_OF_OPEN_ADDRESS_RANDOM = COMMAND_PATTERN_OF_ADDRESS_RANDOM_SWITCH % 2\n+REPORT_OUTPUT_PATH = os.path.join(DTS_PATH, FOLDERS['Output'])\n+\n+\n+def asan_test(asan_switch):\n+ ASanTestProcess.test_prepare(asan_switch)\n+ ASanTestProcess.test_process(asan_switch)\n+\n+\n+class ASanTestProcess(object):\n+ @staticmethod\n+ def test_prepare(is_support_ASan_test):\n+ if is_support_ASan_test:\n+ _FrameworkADAPTER.decorator_dts_run()\n+ _FrameworkADAPTER.decorator_send_expect()\n+ _FrameworkADAPTER.decorator_build_install_dpdk()\n+\n+ @staticmethod\n+ def test_process(is_support_ASan_test):\n+ if is_support_ASan_test:\n+ report_process_obj = _NewReport()\n+ report_process_obj.process_report_header()\n+ report_process_obj.process_report_detail()\n+ report_process_obj.save_report()\n+\n+\n+class _FrameworkADAPTER(object):\n+ @staticmethod\n+ def decorator_build_install_dpdk():\n+ added_param = _ASanConfig().build_param\n+ if added_param is not None:\n+ from framework.project_dpdk import DPDKdut\n+ origin_func = DPDKdut.build_install_dpdk\n+\n+ def new_func(*args, **kwargw):\n+ kwargw['extra_options'] = ' '.join([kwargw.get('extra_options', ''), added_param])\n+ origin_func(*args, **kwargw)\n+\n+ DPDKdut.build_install_dpdk = new_func\n+\n+ @staticmethod\n+ def decorator_dts_run():\n+ import framework.dts as dts\n+ origin_func = dts.dts_run_suite\n+\n+ def new_func(*args, **kwargs):\n+ duts = args[0]\n+ for dut in duts:\n+ dut.send_expect(COMMAND_OF_CLOSE_ADDRESS_RANDOM, \"#\")\n+\n+ origin_func(*args, **kwargs)\n+\n+ for dut in duts:\n+ dut.send_expect(COMMAND_OF_OPEN_ADDRESS_RANDOM, \"#\")\n+\n+ dts.dts_run_suite = new_func\n+\n+ @staticmethod\n+ def decorator_send_expect():\n+ import framework.ssh_pexpect as ssh_pexpect\n+ origin_func = ssh_pexpect.SSHPexpect._SSHPexpect__flush\n+\n+ def new_func(self):\n+ DELETE_CONTENT_PATTERN = r'^\\s*\\[?PEXPECT\\]?#?\\s*$'\n+ befored_info = re.sub(DELETE_CONTENT_PATTERN, '', self.session.before).strip()\n+ if len(befored_info) > MIN_LENGTH_OF_FILTERED_OUTPUT and self.logger:\n+ self.logger.info(f'Buffered info: {befored_info}')\n+ origin_func(self)\n+\n+ ssh_pexpect.SSHPexpect._SSHPexpect__flush = new_func\n+\n+\n+class _ASanConfig(object):\n+ def __init__(self, ):\n+ self.config = configparser.ConfigParser()\n+ self.config.read(os.path.join(DTS_PATH, CONFIG_FILE_PARENT_DIR, CONFIG_FILE_NAME))\n+ self._filter_list = None\n+ self._build_params = None\n+\n+ def _read_ASan_sect_conf(self, key):\n+ return self.config.get(ASan_CONFIG_SECT, key)\n+\n+ def _set_ASan_filter(self):\n+ try:\n+ origin_filter_string = self._read_ASan_sect_conf(ASan_FILTER_BOUNDS)\n+ self._filter_list = [tuple(re.split(r':\\s*', _filter)) for _filter in\n+ re.split(r',\\s*', origin_filter_string)]\n+ except KeyError:\n+ self._filter_list = []\n+\n+ def _set_ASan_param(self):\n+ try:\n+ param_string = self._read_ASan_sect_conf(ASan_PARAM_KEY)\n+ except KeyError:\n+ param_string = ''\n+ self._build_params = param_string\n+\n+ @property\n+ def filter_list(self):\n+ self._set_ASan_filter()\n+ return self._filter_list\n+\n+ @property\n+ def build_param(self):\n+ self._set_ASan_param()\n+ return self._build_params\n+\n+\n+class _OldExcelReport(object):\n+ def __init__(self):\n+ self._report_file = os.path.join(REPORT_OUTPUT_PATH, ORIGIN_TEST_REPORT_FILE)\n+ self._workbook: xlrd.Book = xlrd.open_workbook(self._report_file)\n+ self._sheet_obj: xlrd.sheet.Sheet = self._workbook.sheets()[0]\n+ self._rows = self._sheet_obj.nrows\n+ self.current_row_num = 0\n+\n+ def generator_rows(self):\n+ while True:\n+ if self.current_row_num >= self._rows:\n+ raise IndexError\n+ row_number_of_jump_to = yield self._sheet_obj.row(self.current_row_num)\n+ row = row_number_of_jump_to if row_number_of_jump_to is not None else self.current_row_num + 1\n+ self.current_row_num = row\n+\n+\n+class _OldExcelReportReader(object):\n+ def __init__(self):\n+ self._old_report = _OldExcelReport()\n+ self._header_line_num = 1\n+ self._test_env_content_column = None\n+ self._test_suite_content_column = None\n+ self._gen_report_rows = self._old_report.generator_rows()\n+ next(self._gen_report_rows)\n+ self._report_content_dict = dict()\n+ self._current_suite = None\n+\n+ def get_report_info(self):\n+ try:\n+ self._get_first_line()\n+ self._get_test_env()\n+ self._get_cases_result()\n+ except IndexError:\n+ pass\n+ return self._report_content_dict\n+\n+ def _get_first_line(self):\n+ header_row_title = self._gen_report_rows.send(self._header_line_num - 1)\n+ header_row_content = self._gen_report_rows.send(self._header_line_num)\n+ cell_num = 0\n+ while header_row_title[cell_num].value != 'Test suite':\n+ header_cell_title: str = header_row_title[cell_num].value\n+ header_cell_content = header_row_content[cell_num].value\n+ self._report_content_dict[header_cell_title.lower().replace(' ', '_')] = header_cell_content\n+ cell_num = cell_num + 1\n+ self._test_env_content_column = cell_num - 1\n+ self._test_suite_content_column = cell_num\n+\n+ @staticmethod\n+ def _get_value_from_cell(cells_list_of_row):\n+ return [cell.value for cell in cells_list_of_row]\n+\n+ def _get_test_env(self):\n+ env_key_list = ['driver', 'kdriver', 'firmware', 'package']\n+ for env_key in env_key_list:\n+ env_info_row = next(self._gen_report_rows)\n+ env_cell_value = env_info_row[self._test_env_content_column].value\n+ if env_cell_value:\n+ env_value = env_cell_value.split(': ')[1]\n+ self._report_content_dict[env_key] = env_value\n+ else:\n+ self._report_content_dict[env_key] = None\n+ # back to previous line\n+ self._gen_report_rows.send(self._old_report.current_row_num - 1)\n+\n+ def _get_cases_result(self):\n+ for row_cells in self._gen_report_rows:\n+ suite_content_column_begin = self._test_suite_content_column\n+ suite_content_column_end = self._test_suite_content_column + 3\n+ suite_name, case_name, original_result_msg = \\\n+ self._get_value_from_cell(row_cells[suite_content_column_begin:suite_content_column_end])\n+ EMPTY_LINE_CONDITION = not suite_name and not case_name\n+ NO_CASE_LINE_CONDITION = not case_name\n+ SUITE_BEGIN_LINE_CONDITON = suite_name\n+ if EMPTY_LINE_CONDITION or NO_CASE_LINE_CONDITION:\n+ continue\n+\n+ if SUITE_BEGIN_LINE_CONDITON:\n+ self._add_suite_info(suite_name)\n+\n+ self._add_case_info(case_name, original_result_msg)\n+\n+ def _add_suite_info(self, _suite):\n+ self._report_content_dict.setdefault(_suite, dict())\n+ self._current_suite = _suite\n+\n+ def _add_case_info(self, _case, _result_msg):\n+ self._report_content_dict.get(self._current_suite)[_case] = _result_msg\n+\n+\n+class _SuiteLogReader(object):\n+ def __init__(self, suite_name):\n+ self._suite_name = suite_name\n+\n+ @contextmanager\n+ def suite_log_file(self):\n+ from framework.utils import get_subclasses\n+ from framework.test_case import TestCase\n+ suite_full_name = 'TestSuite_' + self._suite_name\n+ suite_module = __import__('tests.' + suite_full_name, fromlist=[suite_full_name])\n+ suite_class_name = [test_case_name for test_case_name, _ in get_subclasses(suite_module, TestCase)][0]\n+ log_file_path = os.path.join(REPORT_OUTPUT_PATH, suite_class_name)\n+ log_file_obj = open(log_file_path + '.log', 'r')\n+ yield log_file_obj\n+ log_file_obj.close()\n+\n+\n+class _NewReport(object):\n+ def __init__(self):\n+ self._excel_report_file = os.path.join(REPORT_OUTPUT_PATH, NEW_TEST_REPORT_FILE)\n+ self._json_report_file = os.path.join(REPORT_OUTPUT_PATH, NEW_JSON_REPORT_FILE)\n+ self._stats_report_file = os.path.join(REPORT_OUTPUT_PATH, NEW_STATS_REPORT_FILE)\n+ self._remove_history_asan_report()\n+ self._excel_report = ExcelReporter(self._excel_report_file)\n+ self._json_report = JSONReporter(self._json_report_file)\n+ self._stats_report = StatsReporter(self._stats_report_file)\n+ self._result_obj = Result()\n+ self._old_report_reader = _OldExcelReportReader()\n+ self._old_report_content: dict = self._old_report_reader.get_report_info()\n+ self._new_suites_result = dict()\n+ self._ASan_filter = _ASanConfig().filter_list\n+ self._current_case = None\n+ self._current_suite = None\n+ self._filtered_line_cache = []\n+ self._filter_begin = None\n+ self._filter_end = None\n+\n+ def process_report_header(self):\n+ head_key_list = ['dut', 'kdriver', 'firmware', 'package', 'driver', 'dpdk_version', 'target', 'nic']\n+ for head_key in head_key_list:\n+ head_value = self._old_report_content.setdefault(head_key, None)\n+ self._old_report_content.pop(head_key)\n+ setattr(self._result_obj, head_key, head_value)\n+\n+ def process_report_detail(self):\n+ for suite in self._old_report_content.keys():\n+ self._get_suite_new_result(suite)\n+ self._parse_suite_result_to_result_obj()\n+\n+ def _get_suite_new_result(self, suite):\n+ suite_log_reader = _SuiteLogReader(suite)\n+ self._current_suite = suite\n+ gen_suite_lines = suite_log_reader.suite_log_file()\n+ self._get_case_result(gen_suite_lines)\n+\n+ def _parse_suite_result_to_result_obj(self):\n+ self._result_obj.test_suite = self._current_suite\n+ for case in self._old_report_content[self._current_suite]:\n+ self._result_obj.test_case = case\n+ if case in self._new_suites_result:\n+ self._result_obj._Result__set_test_case_result(*self._new_suites_result[case])\n+ else:\n+ origin_result = self._get_origin_case_result(case)\n+ self._result_obj._Result__set_test_case_result(*origin_result)\n+\n+ def save_report(self):\n+ for report in (self._excel_report, self._json_report, self._stats_report):\n+ report.save(self._result_obj)\n+\n+ def _remove_history_asan_report(self):\n+ for file in (self._excel_report_file, self._json_report_file, self._stats_report_file):\n+ if os.path.exists(file):\n+ os.remove(file)\n+\n+ def _get_origin_case_result(self, case_name):\n+ origin_cases_result: dict = self._old_report_content.get(self._current_suite)\n+ origin_case_result: str = origin_cases_result.get(case_name)\n+ CASE_RESULT_AND_MSG_PATTERN = r'(\\S+)\\s?(.*)'\n+ result, msg = re.search(CASE_RESULT_AND_MSG_PATTERN, origin_case_result).groups()\n+ if msg:\n+ msg = msg.replace(\"'\", '').replace('\"', '')\n+\n+ return result, msg\n+\n+ def _get_case_result(self, suite_log_reader):\n+ with suite_log_reader as log_file:\n+ for line in log_file:\n+ self._filter_asan_except(line)\n+\n+ self._log_file_end_handler()\n+\n+ def _filter_asan_except(self, line):\n+ CASE_LOG_BEGIN_PATTERN = r'Test Case test_(\\w+) Begin'\n+ case_begin_match = re.search(CASE_LOG_BEGIN_PATTERN, line)\n+\n+ if case_begin_match:\n+ case_name = case_begin_match.groups()[0]\n+ self._case_begin_handler(case_name)\n+ return\n+\n+ for filter_tuple in self._ASan_filter:\n+ begin_filter, end_filter = filter_tuple\n+ if begin_filter in line:\n+ self._filter_matched_begin_handler(begin_filter, line)\n+ return\n+\n+ if self._filter_begin is not None:\n+ self._filter_matched_line_handler(line)\n+ return\n+\n+ if end_filter in line:\n+ self._filter_matched_end_handler(end_filter, line)\n+ return\n+\n+ def _case_begin_handler(self, case_name):\n+ self._save_previous_case_result_and_clean_env()\n+ self._current_case = case_name\n+\n+ def _filter_matched_begin_handler(self, begin_key, line):\n+ self._filter_begin = begin_key\n+ self._filtered_line_cache.append(line)\n+\n+ def _filter_matched_line_handler(self, line):\n+ self._filtered_line_cache.append(line)\n+\n+ def _filter_matched_end_handler(self, end_key, line):\n+ self._filtered_line_cache.append(line)\n+ self._filter_begin = end_key\n+\n+ def _log_file_end_handler(self):\n+ self._save_previous_case_result_and_clean_env()\n+\n+ def _save_previous_case_result_and_clean_env(self):\n+ exist_previous_case_condition = self._current_case is not None\n+ origin_report_contain_previous_case_result = \\\n+ self._current_case in self._old_report_content.get(self._current_suite)\n+\n+ if exist_previous_case_condition and origin_report_contain_previous_case_result:\n+ self._save_case_result()\n+\n+ self._filtered_line_cache.clear()\n+ self._filter_begin = None\n+ self._filter_end = None\n+\n+ def _save_case_result(self):\n+ cached_content = self._get_filtered_cached_result()\n+ if self._current_case in self._new_suites_result:\n+ # Run multiple times and keep the last result\n+ self._new_suites_result.pop(self._current_case)\n+\n+ if cached_content:\n+ # filter hit scene\n+ self._new_suites_result[self._current_case] = ('FAILED', cached_content)\n+ else:\n+ # filter not hit scene\n+ self._new_suites_result[self._current_case] = self._get_origin_case_result(self._current_case)\n+\n+ def _get_filtered_cached_result(self):\n+ ASan_FILTER_CONTENT_PATTERN = rf\"{self._filter_begin}[\\s\\S]+(?!{self._filter_end})?\"\n+ key_search_result = re.findall(ASan_FILTER_CONTENT_PATTERN, ''.join(self._filtered_line_cache))\n+\n+ return key_search_result[0] if key_search_result else ''\n+\n+\n+if __name__ == '__main__':\n+ asan_test(True)\ndiff --git a/framework/dts.py b/framework/dts.py\nindex 892aa1fc..1a629fc4 100644\n--- a/framework/dts.py\n+++ b/framework/dts.py\n@@ -66,7 +66,7 @@ from .utils import (\n create_parallel_locks,\n get_subclasses,\n )\n-\n+from framework.asan_test import ASanTestProcess\n imp.reload(sys)\n \n requested_tests = None\n@@ -504,7 +504,7 @@ def dts_run_suite(duts, tester, test_suites, target, subtitle):\n def run_all(config_file, pkgName, git, patch, skip_setup,\n read_cache, project, suite_dir, test_cases,\n base_dir, output_dir, verbose, virttype, debug,\n- debugcase, re_run, commands, subtitle, update_expected):\n+ debugcase, re_run, commands, subtitle, update_expected, asan):\n \"\"\"\n Main process of DTS, it will run all test suites in the config file.\n \"\"\"\n@@ -517,6 +517,9 @@ def run_all(config_file, pkgName, git, patch, skip_setup,\n global log_handler\n global check_case_inst\n \n+ # prepare ASan test\n+ ASanTestProcess.test_prepare(asan)\n+\n # check the python version of the server that run dts \n check_dts_python_version()\n \n@@ -635,6 +638,9 @@ def run_all(config_file, pkgName, git, patch, skip_setup,\n \n save_all_results()\n \n+ # process ASan test report\n+ ASanTestProcess.test_process(asan)\n+\n \n def show_speedup_options_messages(read_cache, skip_setup):\n if read_cache:\ndiff --git a/main.py b/main.py\nindex 10ed88b5..cae1840e 100755\n--- a/main.py\n+++ b/main.py\n@@ -158,6 +158,10 @@ parser.add_argument('--update-expected',\n action='store_true',\n help='update expected values based on test results')\n \n+parser.add_argument('--asan',\n+ action='store_true',\n+ help='add function to support ASan test')\n+\n args = parser.parse_args()\n \n \n@@ -175,4 +179,4 @@ dts.run_all(args.config_file, args.snapshot, args.git,\n args.project, args.suite_dir, args.test_cases,\n args.dir, args.output, args.verbose,args.virttype,\n args.debug, args.debugcase, args.re_run, args.commands,\n- args.subtitle, args.update_expected)\n+ args.subtitle, args.update_expected, args.asan)\n", "prefixes": [ "V2", "1/2" ] }{ "id": 106475, "url": "