Browse Source

tools: add test for ROM symbols in GDB

Alexey Lapshin 3 years ago
parent
commit
0fd4b09a46

+ 2 - 0
.gitlab/ci/build.yml

@@ -235,8 +235,10 @@ build_non_test_components_apps:
       - "**/build*/flasher_args.json"
       - "**/build*/flash_project_args"
       - "**/build*/config/sdkconfig.json"
+      - "**/build*/bootloader/*.elf"
       - "**/build*/bootloader/*.bin"
       - "**/build*/partition_table/*.bin"
+      - "**/build*/project_description.json"
       - list_job_*.json
       - size_info.txt
     when: always

+ 8 - 0
tools/idf_py_actions/constants.py

@@ -34,3 +34,11 @@ URL_TO_DOC = 'https://docs.espressif.com/projects/esp-idf'
 
 SUPPORTED_TARGETS = ['esp32', 'esp32s2', 'esp32c3', 'esp32s3', 'esp32c2']
 PREVIEW_TARGETS = ['linux', 'esp32h2', 'esp32c6']
+
+OPENOCD_TAGET_CONFIG_DEFAULT = '-f interface/ftdi/esp32_devkitj_v1.cfg -f target/{target}.cfg'
+OPENOCD_TAGET_CONFIG: Dict[str, str] = {
+    'esp32': '-f board/esp32-wrover-kit-3.3v.cfg',
+    'esp32s2': '-f board/esp32s2-kaluga-1.cfg',
+    'esp32c3': '-f board/esp32c3-builtin.cfg',
+    'esp32s3': '-f board/esp32s3-builtin.cfg',
+}

+ 20 - 14
tools/idf_py_actions/debug_ext.py

@@ -14,6 +14,7 @@ from threading import Thread
 from typing import Any, Dict, List, Optional
 
 from click.core import Context
+from idf_py_actions.constants import OPENOCD_TAGET_CONFIG, OPENOCD_TAGET_CONFIG_DEFAULT
 from idf_py_actions.errors import FatalError
 from idf_py_actions.tools import PropertyDict, ensure_build_directory
 
@@ -59,6 +60,11 @@ source {connect}
 '''
 
 
+def get_openocd_arguments(target: str) -> str:
+    default_args = OPENOCD_TAGET_CONFIG_DEFAULT.format(target=target)
+    return str(OPENOCD_TAGET_CONFIG.get(target, default_args))
+
+
 def action_extensions(base_actions: Dict, project_path: str) -> Dict:
     OPENOCD_OUT_FILE = 'openocd_out.txt'
     GDBGUI_OUT_FILE = 'gdbgui_out.txt'
@@ -286,12 +292,6 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
         """
         Execute openocd as external tool
         """
-        OPENOCD_TAGET_CONFIG = {
-            'esp32': '-f board/esp32-wrover-kit-3.3v.cfg',
-            'esp32s2': '-f board/esp32s2-kaluga-1.cfg',
-            'esp32c3': '-f board/esp32c3-builtin.cfg',
-            'esp32s3': '-f board/esp32s3-builtin.cfg',
-        }
         if os.getenv('OPENOCD_SCRIPTS') is None:
             raise FatalError('OPENOCD_SCRIPTS not found in the environment: Please run export.sh/export.bat', ctx)
         openocd_arguments = os.getenv('OPENOCD_COMMANDS') if openocd_commands is None else openocd_commands
@@ -299,8 +299,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
         if openocd_arguments is None:
             # use default value if commands not defined in the environment nor command line
             target = project_desc['target']
-            default_args = '-f interface/ftdi/esp32_devkitj_v1.cfg -f target/{}.cfg'.format(target)
-            openocd_arguments = OPENOCD_TAGET_CONFIG.get(target, default_args)
+            openocd_arguments = get_openocd_arguments(target)
             print('Note: OpenOCD cfg not found (via env variable OPENOCD_COMMANDS nor as a --openocd-commands argument)\n'
                   'OpenOCD arguments default to: "{}"'.format(openocd_arguments))
         # script directory is taken from the environment by OpenOCD, update only if command line arguments to override
@@ -405,9 +404,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
         """
         Synchronous GDB target with text ui mode
         """
-        gdb(action, ctx, args, 1, gdbinit, require_openocd)
+        gdb(action, ctx, args, False, 1, gdbinit, require_openocd)
 
-    def gdb(action: str, ctx: Context, args: PropertyDict, gdb_tui: Optional[int], gdbinit: Optional[str], require_openocd: bool) -> None:
+    def gdb(action: str, ctx: Context, args: PropertyDict, batch: bool, gdb_tui: Optional[int], gdbinit: Optional[str], require_openocd: bool) -> None:
         """
         Synchronous GDB target
         """
@@ -420,6 +419,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
         args = [gdb, *get_gdb_args(project_desc)]
         if gdb_tui is not None:
             args += ['-tui']
+        if batch:
+            args += ['--batch']
         t = Thread(target=run_gdb, args=(args,))
         t.start()
         while True:
@@ -476,12 +477,17 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
                 'callback': gdb,
                 'help': 'Run the GDB.',
                 'options': [
+                    {
+                        'names': ['--batch'],
+                        'help': ('exit after processing gdbinit.\n'),
+                        'hidden': True,
+                        'is_flag': True,
+                        'default': False,
+                    },
                     {
                         'names': ['--gdb-tui', '--gdb_tui'],
-                        'help':
-                        ('run gdb in TUI mode\n'),
-                        'default':
-                        None,
+                        'help': ('run gdb in TUI mode\n'),
+                        'default': None,
                     }, gdbinit, fail_if_openocd_failed
                 ],
                 'order_dependencies': ['all', 'flash'],

+ 6 - 0
tools/test_apps/system/gdb/CMakeLists.txt

@@ -0,0 +1,6 @@
+# The following lines of boilerplate have to be in your project's
+# CMakeLists in this exact order for cmake to work correctly
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+project(gdb)

+ 6 - 0
tools/test_apps/system/gdb/README.md

@@ -0,0 +1,6 @@
+| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-S2 | ESP32-S3 |
+| ----------------- | ----- | -------- | -------- | -------- | -------- |
+
+# IDF GDB test application
+
+This project tests if `idf.py gdb` works correct

+ 3 - 0
tools/test_apps/system/gdb/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "hello_world_main.c"
+                    INCLUDE_DIRS "")
+target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")

+ 46 - 0
tools/test_apps/system/gdb/main/hello_world_main.c

@@ -0,0 +1,46 @@
+/*
+ * SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: CC0-1.0
+ */
+
+#include <stdio.h>
+#include "sdkconfig.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_chip_info.h"
+#include "esp_flash.h"
+
+void app_main(void)
+{
+    printf("Hello world!\n");
+
+    /* Print chip information */
+    esp_chip_info_t chip_info;
+    uint32_t flash_size;
+    esp_chip_info(&chip_info);
+    printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
+           CONFIG_IDF_TARGET,
+           chip_info.cores,
+           (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
+           (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
+
+    printf("silicon revision %d, ", chip_info.revision);
+    if(esp_flash_get_size(NULL, &flash_size) != ESP_OK) {
+        printf("Get flash size failed");
+        return;
+    }
+
+    printf("%uMB %s flash\n", flash_size / (1024 * 1024),
+           (chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
+
+    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());
+
+    for (int i = 10; i >= 0; i--) {
+        printf("Restarting in %d seconds...\n", i);
+        vTaskDelay(1000 / portTICK_PERIOD_MS);
+    }
+    printf("Restarting now.\n");
+    fflush(stdout);
+    esp_restart();
+}

+ 53 - 0
tools/test_apps/system/gdb/pytest_gdb.py

@@ -0,0 +1,53 @@
+# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Unlicense OR CC0-1.0
+import os
+import re
+import signal
+import subprocess
+import sys
+
+import pexpect
+import pytest
+from pytest_embedded import Dut
+
+try:
+    from idf_py_actions.debug_ext import get_openocd_arguments
+except ModuleNotFoundError:
+    sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..'))
+    from idf_py_actions.debug_ext import get_openocd_arguments
+
+
+@pytest.mark.supported_targets
+@pytest.mark.test_jtag_arm
+def test_idf_gdb(dut: Dut) -> None:
+    # Need to wait a moment to connect via OpenOCD after the hard reset happened.
+    # Along with this check that app runs ok
+    dut.expect('Hello world!')
+
+    # Don't need to have output from UART any more
+    dut.serial.stop_redirect_thread()
+
+    with open(os.path.join(dut.logdir, 'ocd.log'), 'w') as ocd_log:
+        ocd = subprocess.Popen(f'openocd {get_openocd_arguments(dut.target)}', stdout=ocd_log, stderr=ocd_log, shell=True)
+
+        try:
+            gdb_env = os.environ.copy()
+            gdb_env['ESP_IDF_GDB_TESTING'] = '1'
+
+            with open(os.path.join(dut.logdir, 'gdb.log'), 'w') as gdb_log, \
+                 pexpect.spawn(f'idf.py -B {dut.app.binary_path} gdb --batch',
+                               env=gdb_env,
+                               timeout=60,
+                               logfile=gdb_log,
+                               encoding='utf-8',
+                               codec_errors='ignore') as p:
+                p.expect(re.compile(r'add symbol table from file.*bootloader.elf'))
+                p.expect(re.compile(r'add symbol table from file.*rom.elf'))
+                p.expect_exact('hit Temporary breakpoint 1, app_main ()')
+        finally:
+            try:
+                ocd.send_signal(signal.SIGINT)
+                ocd.communicate(timeout=15)
+            except subprocess.TimeoutExpired:
+                ocd.kill()
+                ocd.communicate()