Part 1
요약·해설
GDB server를 QEMU·ninja·west로 열고 client를 연결하는 방법, Eclipse pyOCD project 설정, thread awareness, I2C message dump와 allowlist를 다룹니다.
Server · Client · TUI
pyOCD · SVD · RTOS
Dump · Decode · Filter
Part 2
접을 수 있는 영어 원문 전체
영어 원문 전체 펼치기
548EA89CB8B315BA8A2DDF78588C6DDCE8E2590976C12B966325FFEB8DBC161C
.. _develop_debug:
Debugging
#########
.. _application_debugging:
Application Debugging
*********************
This section is a quick hands-on reference to start debugging your
application with QEMU. Most content in this section is already covered in
`QEMU`_ and `GNU_Debugger`_ reference manuals.
.. _QEMU: http://wiki.qemu.org/Main_Page
.. _GNU_Debugger: http://www.gnu.org/software/gdb
In this quick reference, you'll find shortcuts, specific environmental
variables, and parameters that can help you to quickly set up your debugging
environment.
The simplest way to debug an application running in QEMU is using the GNU
Debugger and setting a local GDB server in your development system through QEMU.
You will need an :abbr:`ELF (Executable and Linkable Format)` binary image for
debugging purposes. The build system generates the image in the build
directory. By default, the kernel binary name is :file:`zephyr.elf`. The name
can be changed using :kconfig:option:`CONFIG_KERNEL_BIN_NAME`.
GDB server
==========
We will use the standard 1234 TCP port to open a :abbr:`GDB (GNU Debugger)`
server instance. This port number can be changed for a port that best suits the
development environment. There are multiple ways to do this. Each way starts a
QEMU instance with the processor halted at startup and with a GDB server
instance listening for a connection.
Running QEMU directly
~~~~~~~~~~~~~~~~~~~~~
You can run QEMU to listen for a "gdb connection" before it starts executing any
code to debug it.
.. code-block:: bash
qemu -s -S <image>
will setup Qemu to listen on port 1234 and wait for a GDB connection to it.
The options used above have the following meaning:
* ``-S`` Do not start CPU at startup; rather, you must type 'c' in the
monitor.
* ``-s`` Shorthand for :literal:`-gdb tcp::1234`: open a GDB server on
TCP port 1234.
Running QEMU via :command:`ninja`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run the following inside the build directory of an application:
.. code-block:: console
ninja debugserver
QEMU will write the console output to the path specified in
:makevar:`${QEMU_PIPE}` via CMake, typically :file:`qemu-fifo` within the build
directory. You may monitor this file during the run with :command:`tail -f
qemu-fifo`.
Running QEMU via :command:`west`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Run the following from your project root:
.. code-block:: console
west build -t debugserver_qemu
QEMU will write the console output to the terminal from which you invoked
:command:`west`.
Configuring the :command:`gdbserver` listening device
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The Kconfig option :kconfig:option:`CONFIG_QEMU_GDBSERVER_LISTEN_DEV` controls
the listening device, which can be a TCP port number or a path to a character
device. GDB releases 9.0 and newer also support Unix domain sockets.
If the option is unset, then the QEMU invocation will lack a ``-s`` or a
``-gdb`` parameter. You can then use the :envvar:`QEMU_EXTRA_FLAGS` shell
environment variable to pass in your own listen device configuration.
GDB client
==========
Connect to the server by running :command:`gdb` and giving these commands:
.. code-block:: bash
$ path/to/gdb path/to/zephyr.elf
(gdb) target remote localhost:1234
(gdb) dir ZEPHYR_BASE
.. note::
Substitute the correct :ref:`ZEPHYR_BASE <important-build-vars>` for your
system.
You can use a local GDB configuration :file:`.gdbinit` to initialize your GDB
instance on every run. Your home directory is a typical location for
:file:`.gdbinit`, but you can configure GDB to load from other locations,
including the directory from which you invoked :command:`gdb`. This example
file performs the same configuration as above:
.. code-block:: none
target remote localhost:1234
dir ZEPHYR_BASE
Alternate interfaces
~~~~~~~~~~~~~~~~~~~~
GDB provides a curses-based interface that runs in the terminal. Pass the ``--tui``
option when invoking :command:`gdb` or give the ``tui enable`` command within
:command:`gdb`.
.. note::
The GDB version on your development system might not support the ``--tui``
option. Please make sure you use the GDB binary from the SDK which
corresponds to the toolchain that has been used to build the binary.
Finally, the command below connects to the GDB server using the :abbr:`DDD
(Data Display Debugger)`, a graphical frontend for GDB. The following command
loads the symbol table from the ELF binary file, in this instance,
:file:`zephyr.elf`.
.. code-block:: bash
ddd --gdb --debugger "gdb zephyr.elf"
Both commands execute :command:`gdb`. The command name might
change depending on the toolchain you are using and your cross-development
tools.
:command:`ddd` may not be installed in your
development system by default. Follow your system instructions to install
it. For example, use :command:`sudo apt-get install ddd` on an Ubuntu system.
Debugging
=========
As configured above, when you connect the GDB client, the application will be
stopped at system startup. You may set breakpoints, step through code, etc. as
when running the application directly within :command:`gdb`.
.. note::
:command:`gdb` will not print the system console output as the application runs,
unlike when you run a native application in GDB directly. If you just
:command:`continue` after connecting the client, the application will run,
but nothing will appear to happen. Check the console output as described
above.
Debug with Eclipse
******************
Overview
========
CMake supports generating a project description file that can be imported into
the Eclipse Integrated Development Environment (IDE) and used for graphical
debugging.
The `GNU MCU Eclipse plug-ins`_ provide a mechanism to debug ARM projects in
Eclipse with pyOCD, Segger J-Link, and OpenOCD debugging tools.
The following tutorial demonstrates how to debug a Zephyr application in
Eclipse with pyOCD in Windows. It assumes you have already installed the GCC
ARM Embedded toolchain and pyOCD.
Set Up the Eclipse Development Environment
==========================================
#. Download and install `Eclipse IDE for C/C++ Developers`_.
#. In Eclipse, install the `GNU MCU Eclipse plug-ins`_ by opening the menu
``Window->Eclipse Marketplace...``, searching for ``GNU MCU Eclipse``, and
clicking ``Install`` on the matching result.
#. Configure the path to the pyOCD GDB server by opening the menu
``Window->Preferences``, navigating to ``MCU``, and setting the ``Global
pyOCD Path``.
Generate and Import an Eclipse Project
======================================
#. Set up a GNU Arm Embedded toolchain as described in
:ref:`toolchain_gnuarmemb`.
#. Navigate to a folder outside of the Zephyr tree to build your application.
.. code-block:: console
# On Windows
cd %userprofile%
.. note::
If the build directory is a subdirectory of the source directory, as is
usually done in Zephyr, CMake will warn:
"The build directory is a subdirectory of the source directory.
This is not supported well by Eclipse. It is strongly recommended to use
a build directory which is a sibling of the source directory."
#. Configure your application with CMake and build it with ninja. Note the
different CMake generator specified by the ``-G"Eclipse CDT4 - Ninja"``
argument. This will generate an Eclipse project description file,
:file:`.project`, in addition to the usual ninja build files.
.. zephyr-app-commands::
:tool: all
:app: %ZEPHYR_BASE%\samples\synchronization
:host-os: win
:board: frdm_k64f
:gen-args: -G"Eclipse CDT4 - Ninja"
:goals: build
:compact:
#. In Eclipse, import your generated project by opening the menu
``File->Import...`` and selecting the option ``Existing Projects into
Workspace``. Browse to your application build directory in the choice,
``Select root directory:``. Check the box for your project in the list of
projects found and click the ``Finish`` button.
Create a Debugger Configuration
===============================
#. Open the menu ``Run->Debug Configurations...``.
#. Select ``GDB PyOCD Debugging``, click the ``New`` button, and configure the
following options:
- In the Main tab:
- Project: ``my_zephyr_app@build``
- C/C++ Application: :file:`zephyr/zephyr.elf`
- In the Debugger tab:
- pyOCD Setup
- Executable path: :file:`${pyocd_path}\\${pyocd_executable}`
- Uncheck "Allocate console for semihosting"
- Board Setup
- Bus speed: 8000000 Hz
- Uncheck "Enable semihosting"
- GDB Client Setup
- Executable path example (use your ``GNUARMEMB_TOOLCHAIN_PATH``):
:file:`C:\\gcc-arm-none-eabi-6_2017-q2-update\\bin\\arm-none-eabi-gdb.exe`
- In the SVD Path tab:
- File path: :file:`<workspace
top>\\modules\\hal\\nxp\\mcux\\devices\\MK64F12\\MK64F12.xml`
.. note::
This is optional. It provides the SoC's memory-mapped register
addresses and bitfields to the debugger.
#. Click the ``Debug`` button to start debugging.
RTOS Awareness
==============
Support for Zephyr RTOS awareness is implemented in `pyOCD v0.11.0`_ and later.
It is compatible with GDB PyOCD Debugging in Eclipse, but you must enable
CONFIG_DEBUG_THREAD_INFO=y in your application.
Debugging I2C communication
***************************
There is a possibility to log all or some of the I2C transactions done by the application.
This feature is enabled by the Kconfig option :kconfig:option:`CONFIG_I2C_DUMP_MESSAGES`, but it
uses the ``LOG_DBG`` function to print the contents so the
:kconfig:option:`CONFIG_I2C_LOG_LEVEL_DBG` option must also be enabled.
The sample output of the dump looks like this::
D: I2C msg: io_i2c_ctrl7_port0, addr=50
D: W len=01: 00
D: R Sr P len=08:
D: contents:
D: 43 42 41 00 00 00 00 00 |CBA.....
The first line indicates the I2C controller and the target address of the transaction.
In above example, the I2C controller is named ``io_i2c_ctrl7_port0`` and the target device address
is ``0x50``
.. note::
the address, length and contents values are in hexadecimal, but lack the ``0x`` prefix
Next lines contain messages, both sent and received. The contents of write messages is
always shown, while the content of read messages is controlled by a parameter to the
function ``i2c_dump_msgs_rw``. This function is available for use by user, but is also
called internally by ``i2c_transfer`` API function with read content dump enabled.
Before the length parameter, the header of the message is printed using abbreviations:
- W - write message
- R - read message
- Sr - restart bit
- P - stop bit
The above example shows one write message with byte ``0x00`` representing the address of register to
read from the I2C target. After that the log shows the length of received message and following
that, the bytes read from the target ``43 42 41 00 00 00 00 00``.
The content dump consist of both the hex and ASCII representation.
Filtering the I2C communication dump
====================================
By default, all I2C communication is logged between all I2C controllers and I2C targets.
It may litter the log with unrelated devices and make it difficult to effectively debug the
communication with a device of interest.
Enable the Kconfig option :kconfig:option:`CONFIG_I2C_DUMP_MESSAGES_ALLOWLIST` to create an
allowlist of I2C targets to log.
The allowlist of devices is configured using the devicetree, for example::
/ {
i2c {
display0: some-display@a {
...
};
sensor3: some-sensor@b {
...
};
};
i2c-dump-allowlist {
compatible = "zephyr,i2c-dump-allowlist";
devices = < &display0 >, < &sensor3 >;
};
};
The filters nodes are identified by the compatible string with ``zephyr,i2c-dump-allowlist`` value.
The devices are selected using the ``devices`` property with phandles to the devices on the I2C bus.
In the above example, the communication with device ``display0`` and ``sensor3`` will be displayed
in the log.
.. _Eclipse IDE for C/C++ Developers: https://www.eclipse.org/downloads/packages/eclipse-ide-cc-developers/oxygen2
.. _GNU MCU Eclipse plug-ins: https://gnu-mcu-eclipse.github.io/plugins/install/
.. _pyOCD v0.11.0: https://github.com/mbedmicro/pyOCD/releases/tag/v0.11.0
Part 3
한국어 전문 번역
Debugging
Application debugging
이 절은 QEMU로 application debugging을 시작하기 위한 빠른 실습 reference입니다. 대부분의 내용은 QEMU와 GNU Debugger reference manual에도 설명되어 있습니다.
Debugging environment를 빠르게 구성하는 데 도움이 되는 shortcut, 특정 환경 변수, parameter를 다룹니다.
QEMU에서 실행되는 application을 가장 간단하게 debug하는 방법은 GNU Debugger를 사용하고 QEMU를 통해 개발 system에 local GDB server를 여는 것입니다.
Debugging에는 ELF(Executable and Linkable Format) binary image가 필요합니다. Build system은 build directory에 image를 생성하며 기본 kernel binary 이름은 zephyr.elf입니다. CONFIG_KERNEL_BIN_NAME으로 이름을 바꿀 수 있습니다.
GDB server
표준 TCP port 1234에서 GDB server instance를 엽니다. 개발 환경에 더 알맞은 port로 바꿀 수도 있습니다. 아래 방법은 모두 QEMU processor를 시작 시 정지시키고 연결을 기다리는 GDB server instance를 시작합니다.
QEMU 직접 실행
Debug할 code를 실행하기 전에 QEMU가 GDB 연결을 기다리도록 실행할 수 있습니다.
qemu -s -S <image>이 command는 QEMU가 port 1234에서 연결을 기다리게 합니다.
-S: 시작할 때 CPU를 실행하지 않습니다. Monitor에서c를 입력해야 실행합니다.-s:-gdb tcp::1234의 축약형으로 TCP port 1234에 GDB server를 엽니다.
ninja를 통한 QEMU 실행
Application build directory 안에서 다음을 실행합니다.
ninja debugserverQEMU console output은 CMake의 ${QEMU_PIPE}에 지정된 path로 기록되며 보통 build directory의 qemu-fifo입니다. 실행 중 tail -f qemu-fifo로 이 file을 관찰할 수 있습니다.
west를 통한 QEMU 실행
Project root에서 다음을 실행합니다.
west build -t debugserver_qemuQEMU는 west를 실행한 terminal에 console output을 기록합니다.
gdbserver listen device 구성
CONFIG_QEMU_GDBSERVER_LISTEN_DEV Kconfig option은 listen device를 제어합니다. TCP port number 또는 character device path를 사용할 수 있으며 GDB 9.0 이상은 Unix domain socket도 지원합니다.
이 option을 설정하지 않으면 QEMU 호출에 -s 또는 -gdb parameter가 없습니다. 이때 QEMU_EXTRA_FLAGS shell 환경 변수로 원하는 listen device 구성을 전달할 수 있습니다.
GDB client
gdb를 실행하고 다음 command를 입력해 server에 연결합니다.
$ path/to/gdb path/to/zephyr.elf
(gdb) target remote localhost:1234
(gdb) dir ZEPHYR_BASELocal GDB configuration .gdbinit으로 GDB instance를 실행할 때마다 초기화할 수 있습니다. 보통 home directory에 두지만 gdb를 실행한 directory를 비롯한 다른 위치에서도 불러오도록 설정할 수 있습니다. 다음 file은 위 command와 같은 구성을 수행합니다.
target remote localhost:1234
dir ZEPHYR_BASE대체 interface
GDB는 terminal에서 실행되는 curses 기반 interface를 제공합니다. gdb 실행 시 --tui를 전달하거나 gdb 안에서 tui enable을 입력합니다.
다음 command는 GDB의 graphical frontend인 DDD(Data Display Debugger)로 GDB server에 연결합니다. 여기서는 ELF binary file zephyr.elf의 symbol table을 불러옵니다.
ddd --gdb --debugger "gdb zephyr.elf"이 interface들도 결국 gdb를 실행합니다. Toolchain과 cross-development 도구에 따라 command name이 달라질 수 있습니다.
ddd는 개발 system에 기본으로 설치되어 있지 않을 수 있습니다. System 지침에 따라 설치하십시오. Ubuntu에서는 sudo apt-get install ddd를 사용할 수 있습니다.
Debugging 실행
위와 같이 구성한 뒤 GDB client가 연결되면 application은 system startup 지점에서 멈춥니다. GDB에서 직접 실행할 때처럼 breakpoint를 설정하고 code를 한 단계씩 실행할 수 있습니다.
Eclipse로 debug하기
개요
CMake는 Eclipse IDE(Integrated Development Environment)로 import해 graphical debugging에 사용할 project description file을 생성할 수 있습니다.
GNU MCU Eclipse plug-in은 Eclipse에서 pyOCD, Segger J-Link, OpenOCD debugging tool로 Arm project를 debug하는 방법을 제공합니다.
다음 tutorial은 Windows Eclipse에서 pyOCD로 Zephyr application을 debug하는 절차입니다. GCC Arm Embedded toolchain과 pyOCD가 이미 설치되어 있다고 가정합니다.
Eclipse 개발 환경 설정
- Eclipse IDE for C/C++ Developers를 내려받아 설치합니다.
- Eclipse에서
Window->Eclipse Marketplace...menu를 열고GNU MCU Eclipse를 검색한 다음 일치하는 결과의Install을 눌러 GNU MCU Eclipse plug-in을 설치합니다. Window->Preferencesmenu에서MCU로 이동하고Global pyOCD Path를 설정해 pyOCD GDB server path를 구성합니다.
Eclipse project 생성과 import
- GNU Arm Embedded toolchain을 설정합니다.
- Zephyr tree 밖의 application build folder로 이동합니다.
# On Windows cd %userprofile% - CMake로 application을 구성하고 ninja로 build합니다.
-G"Eclipse CDT4 - Ninja"로 다른 CMake generator를 지정하면 일반 ninja build file과 함께 Eclipse project description.project를 생성합니다.west build -b frdm_k64f %ZEPHYR_BASE%\samples\synchronization -- -G"Eclipse CDT4 - Ninja"cmake -Bbuild -GNinja -DBOARD=frdm_k64f -G"Eclipse CDT4 - Ninja" %ZEPHYR_BASE%\samples\synchronization ninja -Cbuild - Eclipse에서
File->Import...를 열고Existing Projects into Workspace를 선택합니다.Select root directory:에서 application build directory를 고르고 발견된 project 목록에서 project를 선택한 뒤Finish를 누릅니다.
Debugger configuration 만들기
Run->Debug Configurations...menu를 엽니다.GDB PyOCD Debugging을 선택하고New를 누른 뒤 다음 option을 설정합니다.- Main tab
- Project:
my_zephyr_app@build - C/C++ Application:
zephyr/zephyr.elf
- Project:
- Debugger tab
- pyOCD Setup
- Executable path:
${pyocd_path}\${pyocd_executable} Allocate console for semihosting선택 해제
- Executable path:
- Board Setup
- Bus speed: 8000000 Hz
Enable semihosting선택 해제
- GDB Client Setup
- Executable path 예(
GNUARMEMB_TOOLCHAIN_PATH에 맞게 변경):C:\gcc-arm-none-eabi-6_2017-q2-update\bin\arm-none-eabi-gdb.exe
- Executable path 예(
- pyOCD Setup
- SVD Path tab
- File path:
<workspace top>\modules\hal\nxp\mcux\devices\MK64F12\MK64F12.xml
- File path:
- Main tab
Debugbutton을 눌러 debugging을 시작합니다.
RTOS awareness
pyOCD v0.11.0 이상은 Zephyr RTOS awareness를 지원합니다. Eclipse의 GDB PyOCD Debugging과 호환되지만 application에서 CONFIG_DEBUG_THREAD_INFO=y를 enable해야 합니다.
I2C communication debugging
Application이 수행하는 I2C transaction의 전부 또는 일부를 log할 수 있습니다. CONFIG_I2C_DUMP_MESSAGES Kconfig option으로 enable하지만 content 출력에 LOG_DBG를 사용하므로 CONFIG_I2C_LOG_LEVEL_DBG도 enable해야 합니다.
Dump sample output:
D: I2C msg: io_i2c_ctrl7_port0, addr=50
D: W len=01: 00
D: R Sr P len=08:
D: contents:
D: 43 42 41 00 00 00 00 00 |CBA.....첫 줄은 I2C controller와 transaction의 target address를 나타냅니다. 예제의 controller 이름은 io_i2c_ctrl7_port0, target device address는 0x50입니다.
다음 줄에는 송신·수신 message가 표시됩니다. Write message content는 항상 표시되며 read message content는 i2c_dump_msgs_rw function parameter로 제어합니다. 사용자가 이 function을 호출할 수 있고 i2c_transfer API도 read content dump를 enable한 상태로 내부 호출합니다. Length 앞의 message header는 다음 abbreviation을 사용합니다.
W: write messageR: read messageSr: restart bitP: stop bit
예제는 target에서 읽을 register address를 나타내는 byte 0x00을 write한 뒤, 수신 message 길이와 target에서 읽은 43 42 41 00 00 00 00 00을 표시합니다. Content dump에는 hexadecimal과 ASCII representation이 모두 들어 있습니다.
I2C communication dump filtering
기본적으로 모든 I2C controller와 target 사이의 통신을 log합니다. 관련 없는 device가 log를 채워 관심 device의 통신을 효과적으로 debug하기 어려울 수 있습니다.
CONFIG_I2C_DUMP_MESSAGES_ALLOWLIST Kconfig option을 enable하면 log할 I2C target allowlist를 만들 수 있습니다. Device tree로 allowlist를 구성하는 예:
/ {
i2c {
display0: some-display@a {
...
};
sensor3: some-sensor@b {
...
};
};
i2c-dump-allowlist {
compatible = "zephyr,i2c-dump-allowlist";
devices = < &display0 >, < &sensor3 >;
};
};compatible = "zephyr,i2c-dump-allowlist"인 node를 filter node로 식별합니다. devices property에는 I2C bus device를 가리키는 phandle을 넣습니다.
예제에서는 display0과 sensor3 device의 통신만 log에 표시합니다.
Source
출처
영어 원문에는 문단, 문서 지시문, 표, 코드, 코드 심벌, 경로를 그대로 보존했습니다.