get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/106475/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 106475,
    "url": "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"
    ]
}