| .. SPDX-License-Identifier: GPL-2.0 |
| |
| ========================== |
| Userspace debugging advice |
| ========================== |
| |
| This document provides a brief overview of common tools to debug the Linux |
| Kernel from userspace. |
| For debugging advice aimed at driver developers go :doc:`here |
| </process/debugging/driver_development_debugging_guide>`. |
| For general debugging advice, see :doc:`general advice document |
| </process/debugging/index>`. |
| |
| .. contents:: |
| :depth: 3 |
| |
| The following sections show you the available tools. |
| |
| Dynamic debug |
| ------------- |
| |
| Mechanism to filter what ends up in the kernel log by dis-/en-abling log |
| messages. |
| |
| Prerequisite: ``CONFIG_DYNAMIC_DEBUG`` |
| |
| Dynamic debug is only able to target: |
| |
| - pr_debug() |
| - dev_dbg() |
| - print_hex_dump_debug() |
| - print_hex_dump_bytes() |
| |
| Therefore the usability of this tool is, as of now, quite limited as there is |
| no uniform rule for adding debug prints to the codebase, resulting in a variety |
| of ways these prints are implemented. |
| |
| Also, note that most debug statements are implemented as a variation of |
| dprintk(), which have to be activated via a parameter in respective module, |
| dynamic debug is unable to do that step for you. |
| |
| Here is one example, that enables all available pr_debug()'s within the file:: |
| |
| $ alias ddcmd='echo $* > /proc/dynamic_debug/control' |
| $ ddcmd '-p; file v4l2-h264.c +p' |
| $ grep =p /proc/dynamic_debug/control |
| drivers/media/v4l2-core/v4l2-h264.c:372 [v4l2_h264]print_ref_list_b =p |
| "ref_pic_list_b%u (cur_poc %u%c) %s" |
| drivers/media/v4l2-core/v4l2-h264.c:333 [v4l2_h264]print_ref_list_p =p |
| "ref_pic_list_p (cur_poc %u%c) %s\n" |
| |
| **When should you use this over Ftrace ?** |
| |
| - When the code contains one of the valid print statements (see above) or when |
| you have added multiple pr_debug() statements during development |
| - When timing is not an issue, meaning if multiple pr_debug() statements in |
| the code won't cause delays |
| - When you care more about receiving specific log messages than tracing the |
| pattern of how a function is called |
| |
| For the full documentation see :doc:`/admin-guide/dynamic-debug-howto` |
| |
| Ftrace |
| ------ |
| |
| Prerequisite: ``CONFIG_DYNAMIC_FTRACE`` |
| |
| This tool uses the tracefs file system for the control files and output files. |
| That file system will be mounted as a ``tracing`` directory, which can be found |
| in either ``/sys/kernel/`` or ``/sys/debug/kernel/``. |
| |
| Some of the most important operations for debugging are: |
| |
| - You can perform a function trace by adding a function name to the |
| ``set_ftrace_filter`` file (which accepts any function name found within the |
| ``available_filter_functions`` file) or you can specifically disable certain |
| functions by adding their names to the ``set_ftrace_notrace`` file (more info |
| at: :ref:`trace/ftrace:dynamic ftrace`). |
| - In order to find out where calls originate from you can activate the |
| ``func_stack_trace`` option under ``options/func_stack_trace``. |
| - Tracing the children of a function call and showing the return values are |
| possible by adding the desired function in the ``set_graph_function`` file |
| (requires config ``FUNCTION_GRAPH_RETVAL``); more info at |
| :ref:`trace/ftrace:dynamic ftrace with the function graph tracer`. |
| |
| For the full Ftrace documentation see :doc:`/trace/ftrace` |
| |
| Or you could also trace for specific events by :ref:`using event tracing |
| <trace/events:2. using event tracing>`, which can be defined as described here: |
| :ref:`Creating a custom Ftrace tracepoint |
| <process/debugging/driver_development_debugging_guide:ftrace>`. |
| |
| For the full Ftrace event tracing documentation see :doc:`/trace/events` |
| |
| .. _read_ftrace_log: |
| |
| Reading the ftrace log |
| ~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The ``trace`` file can be read just like any other file (``cat``, ``tail``, |
| ``head``, ``vim``, etc.), the size of the file is limited by the |
| ``buffer_size_kb`` (``echo 1000 > buffer_size_kb``). The |
| :ref:`trace/ftrace:trace_pipe` will behave similarly to the ``trace`` file, but |
| whenever you read from the file the content is consumed. |
| |
| Kernelshark |
| ~~~~~~~~~~~ |
| |
| A GUI interface to visualize the traces as a graph and list view from the |
| output of the `trace-cmd |
| <https://git.kernel.org/pub/scm/utils/trace-cmd/trace-cmd.git/>`__ application. |
| |
| For the full documentation see `<https://kernelshark.org/Documentation.html>`__ |
| |
| Perf & alternatives |
| ------------------- |
| |
| The tools mentioned above provide ways to inspect kernel code, results, |
| variable values, etc. Sometimes you have to find out first where to look and |
| for those cases, a box of performance tracking tools can help you to frame the |
| issue. |
| |
| Why should you do a performance analysis? |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| A performance analysis is a good first step when among other reasons: |
| |
| - you cannot define the issue |
| - you do not know where it occurs |
| - the running system should not be interrupted or it is a remote system, where |
| you cannot install a new module/kernel |
| |
| How to do a simple analysis with linux tools? |
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| For the start of a performance analysis, you can start with the usual tools |
| like: |
| |
| - ``top`` / ``htop`` / ``atop`` (*get an overview of the system load, see |
| spikes on specific processes*) |
| - ``mpstat -P ALL`` (*look at the load distribution among CPUs*) |
| - ``iostat -x`` (*observe input and output devices utilization and performance*) |
| - ``vmstat`` (*overview of memory usage on the system*) |
| - ``pidstat`` (*similar to* ``vmstat`` *but per process, to dial it down to the |
| target*) |
| - ``strace -tp $PID`` (*once you know the process, you can figure out how it |
| communicates with the Kernel*) |
| |
| These should help to narrow down the areas to look at sufficiently. |
| |
| Diving deeper with perf |
| ~~~~~~~~~~~~~~~~~~~~~~~ |
| |
| The **perf** tool provides a series of metrics and events to further dial down |
| on issues. |
| |
| Prerequisite: build or install perf on your system |
| |
| Gather statistics data for finding all files starting with ``gcc`` in ``/usr``:: |
| |
| # perf stat -d find /usr -name 'gcc*' | wc -l |
| |
| Performance counter stats for 'find /usr -name gcc*': |
| |
| 1277.81 msec task-clock # 0.997 CPUs utilized |
| 9 context-switches # 7.043 /sec |
| 1 cpu-migrations # 0.783 /sec |
| 704 page-faults # 550.943 /sec |
| 766548897 cycles # 0.600 GHz (97.15%) |
| 798285467 instructions # 1.04 insn per cycle (97.15%) |
| 57582731 branches # 45.064 M/sec (2.85%) |
| 3842573 branch-misses # 6.67% of all branches (97.15%) |
| 281616097 L1-dcache-loads # 220.390 M/sec (97.15%) |
| 4220975 L1-dcache-load-misses # 1.50% of all L1-dcache accesses (97.15%) |
| <not supported> LLC-loads |
| <not supported> LLC-load-misses |
| |
| 1.281746009 seconds time elapsed |
| |
| 0.508796000 seconds user |
| 0.773209000 seconds sys |
| |
| |
| 52 |
| |
| The availability of events and metrics depends on the system you are running. |
| |
| For the full documentation see |
| `<https://perf.wiki.kernel.org/index.php/Main_Page>`__ |
| |
| Perfetto |
| ~~~~~~~~ |
| |
| A set of tools to measure and analyze how well applications and systems perform. |
| You can use it to: |
| |
| * identify bottlenecks |
| * optimize code |
| * make software run faster and more efficiently. |
| |
| **What is the difference between perfetto and perf?** |
| |
| * perf is tool as part of and specialized for the Linux Kernel and has CLI user |
| interface. |
| * perfetto cross-platform performance analysis stack, has extended |
| functionality into userspace and provides a WEB user interface. |
| |
| For the full documentation see `<https://perfetto.dev/docs/>`__ |
| |
| Kernel panic analysis tools |
| --------------------------- |
| |
| To capture the crash dump please use ``Kdump`` & ``Kexec``. Below you can find |
| some advice for analysing the data. |
| |
| For the full documentation see the :doc:`/admin-guide/kdump/kdump` |
| |
| In order to find the corresponding line in the code you can use `faddr2line |
| <https://elixir.bootlin.com/linux/v6.11.6/source/scripts/faddr2line>`__; note |
| that you need to enable ``CONFIG_DEBUG_INFO`` for that to work. |
| |
| An alternative to using ``faddr2line`` is the use of ``objdump`` (and its |
| derivatives for the different platforms like ``aarch64-linux-gnu-objdump``). |
| Take this line as an example: |
| |
| ``[ +0.000240] rkvdec_device_run+0x50/0x138 [rockchip_vdec]``. |
| |
| We can find the corresponding line of code by executing:: |
| |
| aarch64-linux-gnu-objdump -dS drivers/staging/media/rkvdec/rockchip-vdec.ko | grep rkvdec_device_run\>: -A 40 |
| 0000000000000ac8 <rkvdec_device_run>: |
| ac8: d503201f nop |
| acc: d503201f nop |
| { |
| ad0: d503233f paciasp |
| ad4: a9bd7bfd stp x29, x30, [sp, #-48]! |
| ad8: 910003fd mov x29, sp |
| adc: a90153f3 stp x19, x20, [sp, #16] |
| ae0: a9025bf5 stp x21, x22, [sp, #32] |
| const struct rkvdec_coded_fmt_desc *desc = ctx->coded_fmt_desc; |
| ae4: f9411814 ldr x20, [x0, #560] |
| struct rkvdec_dev *rkvdec = ctx->dev; |
| ae8: f9418015 ldr x21, [x0, #768] |
| if (WARN_ON(!desc)) |
| aec: b4000654 cbz x20, bb4 <rkvdec_device_run+0xec> |
| ret = pm_runtime_resume_and_get(rkvdec->dev); |
| af0: f943d2b6 ldr x22, [x21, #1952] |
| ret = __pm_runtime_resume(dev, RPM_GET_PUT); |
| af4: aa0003f3 mov x19, x0 |
| af8: 52800081 mov w1, #0x4 // #4 |
| afc: aa1603e0 mov x0, x22 |
| b00: 94000000 bl 0 <__pm_runtime_resume> |
| if (ret < 0) { |
| b04: 37f80340 tbnz w0, #31, b6c <rkvdec_device_run+0xa4> |
| dev_warn(rkvdec->dev, "Not good\n"); |
| b08: f943d2a0 ldr x0, [x21, #1952] |
| b0c: 90000001 adrp x1, 0 <rkvdec_try_ctrl-0x8> |
| b10: 91000021 add x1, x1, #0x0 |
| b14: 94000000 bl 0 <_dev_warn> |
| *bad = 1; |
| b18: d2800001 mov x1, #0x0 // #0 |
| ... |
| |
| Meaning, in this line from the crash dump:: |
| |
| [ +0.000240] rkvdec_device_run+0x50/0x138 [rockchip_vdec] |
| |
| I can take the ``0x50`` as offset, which I have to add to the base address |
| of the corresponding function, which I find in this line:: |
| |
| 0000000000000ac8 <rkvdec_device_run>: |
| |
| The result of ``0xac8 + 0x50 = 0xb18`` |
| And when I search for that address within the function I get the |
| following line:: |
| |
| *bad = 1; |
| b18: d2800001 mov x1, #0x0 |
| |
| **Copyright** ©2024 : Collabora |