我们在做openharmony版本的weekly build的时候,需要使用kyPacker来打包update镜像,本文介绍kypacker的使用
kypacker仓库地址如下:
https://gitlab2.kylin.com/sh-product-Embedded/openharmony/kypacker
获取仓库后,我们可以先运行一下make,这样能够获取平台相关的crc32库librkcrc32.so,如果不执行make,则使用python实现的crc32.
为了能够让kypacker正常工作,需要package-file文件,此文件需要描述文件名字和文件位置,举例如下
package-file package-file bootloader Image/rk3588_spl_loader_v1.11.112.bin parameter Image/parameter.txt uboot Image/uboot.img boot Image/boot.img rootfs Image/rootfs.img userdata Image/userdata.img
根据package-file,我们需要将文件放在Image目录下的位置,举例如下
-rwxrwx--- 1 root vboxsf 100M 12月 26 09:36 boot.img -rwxrwx--- 1 root vboxsf 369 12月 21 17:15 parameter.txt -rwxrwx--- 1 root vboxsf 457K 12月 26 09:24 rk3588_spl_loader_v1.11.112.bin -rwxrwx--- 1 root vboxsf 100M 12月 26 09:36 rootfs.img -rwxrwx--- 1 root vboxsf 4.0M 12月 22 14:38 uboot.img -rwxrwx--- 1 root vboxsf 100M 12月 26 09:36 userdata.img
在打包update之前,我们需要打包firmware固件,通过如下命令
kyPacker.py afptool pack ./tests ./tests/Image/firmware.img
至此,可以在./tests/Image/firmware.img查看firmware.img文件
如果firmware.img生成成功,则可以打包update.img,如下命令
kyPacker.py imgmker pack RK3588 ./tests/Image/bootloader.img ./tests/Image/firmware.img -os_type androidos
至此,可以在当前目录上发现update.img文件,打包update.img成功。
为了更加了解kypacker的工作方式,可以参考仓库的readme.md,如下
https://gitlab2.kylin.com/sh-product-Embedded/openharmony/kypacker
为了调试内核,我们经常会使用kprobe来调试程序,目前的kprobe挂在了ftrace框架下,所以我们有必要讲一下kprobe/kretprobe,它非常有助于我们调试内核。本文以一个例子来展示kprobe/kretprobe的使用,从而让大家具备调试内核的基本能力
kprobe是能够动态的进入内核收集函数基本信息的工具,它通过替换指令的方式替换指令的方式,将原本运行在某函数的栈跳到了kprobe的栈,故kprobe能够收集函数的入参,调用栈等基本信息
而kretprobe是改进的kprobe,顾名思义,它可以用于获取函数的返回值信息
所以,kprobe/kretprobe能够获取:
p[:[GRP/][EVENT]] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS] : Set a probe r[MAXACTIVE][:[GRP/][EVENT]] [MOD:]SYM[+0] [FETCHARGS] : Set a return probe p[:[GRP/][EVENT]] [MOD:]SYM[+0]%return [FETCHARGS] : Set a return probe -:[GRP/][EVENT] : Clear a probe GRP : Group name. If omitted, use "kprobes" for it. EVENT : Event name. If omitted, the event name is generated based on SYM+offs or MEMADDR. MOD : Module name which has given SYM. SYM[+offs] : Symbol+offset where the probe is inserted. SYM%return : Return address of the symbol MEMADDR : Address where the probe is inserted. MAXACTIVE : Maximum number of instances of the specified function that can be probed simultaneously, or 0 for the default value as defined in Documentation/trace/kprobes.rst section 1.3.1. FETCHARGS : Arguments. Each probe can have up to 128 args. %REG : Fetch register REG @ADDR : Fetch memory at ADDR (ADDR should be in kernel) @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) $stackN : Fetch Nth entry of stack (N >= 0) $stack : Fetch stack address. $argN : Fetch the Nth function argument. (N >= 1) (\*1) $retval : Fetch return value.(\*2) $comm : Fetch current task comm. +|-[u]OFFS(FETCHARG) : Fetch memory at FETCHARG +|- OFFS address.(\*3)(\*4) \IMM : Store an immediate value to the argument. NAME=FETCHARG : Set NAME as the argument name of FETCHARG. FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types (x8/x16/x32/x64), VFS layer common type(%pd/%pD), "char", "string", "ustring", "symbol", "symstr" and bitfield are supported. (\*1) only for the probe on function entry (offs == 0). Note, this argument access is best effort, because depending on the argument type, it may be passed on the stack. But this only support the arguments via registers. (\*2) only for return probe. Note that this is also best effort. Depending on the return value type, it might be passed via a pair of registers. But this only accesses one register. (\*3) this is useful for fetching a field of data structures. (\*4) "u" means user-space dereference. See :ref:`user_mem_access`.
上面的描述,大家可以看看,具体内容直接试试即可。
为了能够正常操作kprobe,我们关注如下几个文件:
/sys/kernel/debug/tracing/kprobe_events /sys/kernel/debug/tracing/events/kprobes /sys/kernel/debug/kprobes/list
这三个文件分别为:
这里直接以系统调用open为例,方便大家理解
我们知道一个open事件对应内核是do_sys_openat2,所以查看声明如下:
static long do_sys_openat2(int dfd, const char __user *filename, struct open_how *how)
基于此,我们直接打印arg2即可,这里是char *,我们可以通过string打印,如下
echo 'p:kylin do_sys_openat2 filename=+0($arg2):string' > /sys/kernel/debug/tracing/kprobe_events
然后打开trace信息如下:
echo 1 > events/kprobes/kylin/enable
此时日志如下:
systemd-journal-311 [003] d... 394.007790: kylin: (do_sys_openat2+0x0/0x2d0) filename="/run/log/journal/150c911f73834975b41af71e80344144/system.journal" systemd-journal-311 [003] d... 394.576585: kylin: (do_sys_openat2+0x0/0x2d0) filename="/run/log/journal/150c911f73834975b41af71e80344144/system.journal" systemd-journal-311 [003] d... 394.576798: kylin: (do_sys_openat2+0x0/0x2d0) filename="/run/log/journal/150c911f73834975b41af71e80344144/system.journal"
如果无需继续trace,关闭方法如下:
echo 0 > /sys/kernel/debug/tracing/events/kprobes/kylin/enable echo > /sys/kernel/debug/tracing/kprobe_events
我们知道系统所有的openat系统调用到内核都是调用的do_sys_openat2,而在do_sys_openat2中存在getname函数,其函数声明如下:
struct filename *getname(const char __user *);
此函数返回一个struct filename,而这个结构如下:
struct filename { const char *name; /* pointer to actual string */ const __user char *uptr; /* original userland pointer */ int refcnt; struct audit_names *aname; const char iname[]; };
所以我们可以编写kprobe_events如下:
echo 'r:kprobes/kylin getname arg1=+0($retval):string[1]' > kprobe_events
这里值得注意的是 string[1]的编写,可以参考如下:
The string array type is a bit different from other types. For other base types, <base-type>[1] is equal to <base-type> (e.g. +0(%di):x32[1] is same as +0(%di):x32.) But string[1] is not equal to string. The string type itself represents “char array”, but string array type represents “char * array”. So, for example, +0(%di):string[1] is equal to +0(+0(%di)):string. Bitfield is another special type, which takes 3 parameters, bit-width, bit- offset, and container-size (usually 32). The syntax is: b<bit-width>@<bit-offset>/<container-size>
所以我们直接拿到了char* name字段。此时我们打开kprobe即可,如下:
echo 1 > /sys/kernel/debug/tracing/events/kprobes/kylin/enable
然后我们直接读取ftrace的pipe即可
cat /sys/kernel/tracing/trace_pipe
我截取了一部分输出,以systemd的journal 服务,可以发现如下日志
systemd-journal-308 [001] d... 58935.290005: kylin: (do_sys_openat2+0x18c/0x2d0 <- getname) arg1={"/run/log/journal/150c911f73834975b41af71e80344144/system.journal"} systemd-journal-308 [001] d... 58935.290339: kylin: (do_sys_openat2+0x18c/0x2d0 <- getname) arg1={"/run/log/journal/150c911f73834975b41af71e80344144/system.journal"} systemd-journal-308 [001] d... 58935.290542: kylin: (do_sys_openat2+0x18c/0x2d0 <- getname) arg1={"/run/log/journal/150c911f73834975b41af71e80344144/system.journal"} systemd-journal-308 [001] d... 58935.290740: kylin: (do_sys_openat2+0x18c/0x2d0 <- getname) arg1={"/run/log/journal/150c911f73834975b41af71e80344144/system.journal"}
这里直接打印了openat的name字段是/run/log/journal/150c911f73834975b41af71e80344144/system.journal
至此,我从如何使用上解释了kprobe和kretprobe,相信大家具备基于kprobe的调试基本能力了,如果需要了解原文的,可以查看如下:
https://www.kernel.org/doc/html/latest/trace/kprobetrace.html https://www.kernel.org/doc/html/latest/trace/kprobes.html
如果大家还想深入的,可以查看如下文章:
An introduction to KProbes [LWN.net]
https://lwn.net/Articles/132196/
图示如下:
根据上次描述的kgdb在arm64的使用,我们可以直接在arm64的机器上运行kgdb了,但是考虑到大家并不是每个人都有arm64的机器,故,这里基于windows的wsl2来实现kgdb连接,方便大家调试。
关于kgdb功能打开,如下:
CONFIG_KGDB CONFIG_KGDB_SERIAL_CONSOLE
默认的gdb不支持1500000波特率,需要合并补丁如下补丁:
https://gitlab.com/gnutools/binutils-gdb/-/commit/78d16865df671f80da8d0a97b18596ef8a3feae3
主要步骤如下:
wget https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/gdb/8.1-0ubuntu3/gdb_8.1-0ubuntu3.dsc https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/gdb/8.1-0ubuntu3/gdb_8.1-0ubuntu3.debian.tar.xz https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/gdb/8.1-0ubuntu3/gdb_8.1.orig.tar.xz dpkg-source -x *.dsc apt-get build-dep . patch -p1 < 0001-78d16865df671f80da8d0a97b18596ef8a3feae3.patch DEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -uc -us
然后安装即可
dpkg -i ../*.deb
关于在wsl2中支持usb serial的方式,可以参考如下:
https://learn.microsoft.com/en-us/windows/wsl/connect-usb
主要步骤如下:
我们要保证wsl的版本是2,内核大于5.10.60.1,如下
wsl --status 默认分发:Ubuntu 默认版本:2 适用于 Linux 的 Windows 子系统最后更新于 2023/11/13 适用于 Linux 的 Windows 子系统内核可以使用“wsl --update”手动更新,但由于你的系统设置,无法进行自动更新。 若要接收自动内核更新,请启用 Windows 更新设置:“在更新 Windows 时接收其他 Microsoft 产品的更新”。 有关详细信息,请访问https://aka.ms/wsl2kernel。 内核版本: 5.10.102.1
直接到如下网址下载msi文件安装
https://github.com/dorssel/usbipd-win/releases
当前版本是usbipd-win_4.3.0.msi
此时我们需要administrator权限的powershell,运行如下:
PS C:\> usbipd list Connected: BUSID VID:PID DEVICE STATE 1-10 1ea7:0064 USB 输入设备 Not shared 1-11 0603:0351 USB 输入设备 Not shared 1-12 1840:13fd USB Attached SCSI (UAS) 大容量存储设备 Not shared 1-21 0bc2:ac31 USB Attached SCSI (UAS) 大容量存储设备 Not shared 1-23 1d5c:2000 FrescoLogic FL2000 USB Display Adapter Not shared 1-24 2207:0006 Android ADB Interface Not shared 3-2 0403:6001 USB Serial Converter Shared Persisted: GUID DEVICE
我们看到了我的串口是在3-2的设备上
如下命令直接绑定即可:
PS C:\> usbipd bind --busid 3-2 PS C:\> usbipd attach --wsl --busid 3-2 usbipd: info: Using WSL distribution 'Ubuntu' to attach; the device will be available in all WSL 2 distributions. usbipd: info: Using IP address 172.20.64.1 to reach the host.
此时我们在wsl可以看到日志如下:
[85304.990343] usb 1-1: new full-speed USB device number 3 using vhci_hcd [85305.180488] usb 1-1: SetAddress Request (3) to port 0 [85305.243206] usb 1-1: New USB device found, idVendor=0403, idProduct=6001, bcdDevice= 6.00 [85305.243249] usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=3 [85305.243293] usb 1-1: Product: FT232R USB UART [85305.259943] usb 1-1: Manufacturer: FTDI [85305.259961] usb 1-1: SerialNumber: A5XK3RJT [85305.300748] ftdi_sio 1-1:1.0: FTDI USB Serial Device converter detected [85305.300921] usb 1-1: Detected FT232RL [85305.302620] usb 1-1: FTDI USB Serial Device converter now attached to ttyUSB0
如下:
# ls /dev/ttyUSB0 /dev/ttyUSB0
然后直接minicom连接串口看是否正常即可:
minicom /dev/ttyUSB0 -s
正常就能操作串口了
Welcome to minicom 2.7.1 OPTIONS: I18n Compiled on Aug 13 2017, 15:25:34. Port /dev/ttyUSB0, 10:33:22 Press CTRL-A Z for help on special keys
如不用了记得主动断开。
usbipd detach --busid <busid>
这时候我们在机器内,命令如下:
echo ttyFIQ0 > /sys/module/kgdboc/parameters/kgdboc echo g > /proc/sysrq-trigger
这时候机器在Debug状态了,所以退出串口,拿gdb去连接它
在连接之前,我们需要一份内核vmlinux和源码,方便调试,而通常我们的代码在服务器,所以需要sshfs
sshfs root@172.25.130.130:/root/public-workspace/tf/01-3588-x11/squashfs-root/root/kernel/ ~/sshfs
为了gdb能够ss调试,我们需要合一下不规范的补丁
https://patchwork.kernel.org/project/linux-arm-kernel/patch/20170523043058.5463-3-takahiro.akashi@linaro.org/
然后直接使用gdb,如下:
root@9:~/sshfs/# gdb-multiarch vmlinux GNU gdb (Ubuntu 8.1-0ubuntu3) 8.1.0.20180409-git Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from vmlinux...done. (gdb) set serial baud 1500000 (gdb) set architecture aarch64 The target architecture is assumed to be aarch64 (gdb) target remote /dev/ttyUSB0 Remote debugging using /dev/ttyUSB0 arch_kgdb_breakpoint () at ./arch/arm64/include/asm/kgdb.h:21 21 asm ("brk %0" : : "I" (KGDB_COMPILED_DBG_BRK_IMM)); (gdb)
搞定
为了调测和学习linux kernel,我们可以使用kgdb,关于kgdb一直没有相关文档,最近有同事产生疑问,故基于rk3399的经验上,在rk3588上尝试使用kgdb来进行内核调试
我相信大家都了解gdb,用gdb来调试linux 内核,在内核的实现叫做kgdb,kgdb需要host和target两台机器,并且只能通过串口。对于想要进一步了解的,可以查看如下文章,文章内容不多
https://www.kernel.org/pub/linux/kernel/people/jwessel/kgdb/ch01.html
为了内核打开kgdb,我们需要配置如下:
CONFIG_KGDB CONFIG_KGDB_SERIAL_CONSOLE CONFIG_KALLSYMS
这样,我们在启动后内核就有如下参数配置项
/sys/module/kgdboc/parameters/kgdboc
我们知道rk的tty叫做ttyFIQ0,所以我们可以设置kgdb管理此tty,如下:
echo ttyFIQ0 > /sys/module/kgdboc/parameters/kgdboc
此时我们主动触发linux进入debug模式即可,如下:
echo g > /proc/sysrq-trigger
然后关闭串口即可
为了能够使用kgdb,我们需要有一台arm64的机器,我这里是飞腾笔记本。
通常,我们的代码放在了服务器,而我们笔记本如果想访问不是很方便,所以我们要借助sshfs来映射,如下:
mkdir ~/sshfs sshfs root@172.25.130.130:/root/public-workspace/tf/01-3588-x11/squashfs-root/root/kernel/ ~/sshfs
这样我们就能在主机上访问内核代码
这里值得注意的是,我们默认的gdb工具不支持1500000的波特率,所以我们需要重新编译gdb工具,补丁如下:
From 78d16865df671f80da8d0a97b18596ef8a3feae3 Mon Sep 17 00:00:00 2001 From: Dan Callaghan <dan.callaghan@morsemicro.com> Date: Mon, 8 May 2023 18:29:45 +1000 Subject: [PATCH] Support higher baud rates when they are defined On Linux at least, baud rate codes are defined up to B4000000. Allow the user to select them if they are present in the system headers. Change-Id: I393ff32e4a4b6127bdf97e3306ad5b6ebf7c934e --- gdb/ser-unix.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/gdb/ser-unix.c b/gdb/ser-unix.c index ede2a58308a..cdc0cf98b7b 100644 --- a/gdb/ser-unix.c +++ b/gdb/ser-unix.c @@ -327,6 +327,72 @@ baudtab[] = 460800, B460800 } , +#endif +#ifdef B500000 + { + 500000, B500000 + } + , +#endif +#ifdef B576000 + { + 576000, B576000 + } + , +#endif +#ifdef B921600 + { + 921600, B921600 + } + , +#endif +#ifdef B1000000 + { + 1000000, B1000000 + } + , +#endif +#ifdef B1152000 + { + 1152000, B1152000 + } + , +#endif +#ifdef B1500000 + { + 1500000, B1500000 + } + , +#endif +#ifdef B2000000 + { + 2000000, B2000000 + } + , +#endif +#ifdef B2500000 + { + 2500000, B2500000 + } + , +#endif +#ifdef B3000000 + { + 3000000, B3000000 + } + , +#endif +#ifdef B3500000 + { + 3500000, B3500000 + } + , +#endif +#ifdef B4000000 + { + 4000000, B4000000 + } + , #endif { -1, -1
此补丁来源gdb上游,如下地址,大家自行合并即可
https://gitlab.com/gnutools/binutils-gdb/-/commit/78d16865df671f80da8d0a97b18596ef8a3feae3
这样我们将gdb拉起sshfs的vmlinux如下:
gdb vmlinux
由于我们的内核配置波特率是1500000,和默认的115200不一致,我们需要单独设置,如下:
set serial baud 1500000
然后直接连接连接kgdb的target即可,如下:
target remote /dev/ttyUSB0
这样我们就正常的kgdb调试linux kernel了。
(gdb) target remote /dev/ttyUSB0 Remote debugging using /dev/ttyUSB0 arch_kgdb_breakpoint () at ./arch/arm64/include/asm/kgdb.h:21 21 asm ("brk %0" : : "I" (KGDB_COMPILED_DBG_BRK_IMM));
关于kgdb的正常使用,有一笔patch一直没办法合入,主要原因是step调试如果禁用中断了,本身就相当于修改了内核行为,它是不应该的,关于具体的内容,可以查看如下:
https://patchwork.kernel.org/project/linux-arm-kernel/patch/20170523043058.5463-3-takahiro.akashi@linaro.org/
关于此讨论,主要是如下补丁:
root@kylin:~/sshfs/kernel# git diff arch/arm64/kernel/kgdb.c diff --git a/arch/arm64/kernel/kgdb.c b/arch/arm64/kernel/kgdb.c index e4e95821b1f6..932b0d9d592f 100644 --- a/arch/arm64/kernel/kgdb.c +++ b/arch/arm64/kernel/kgdb.c @@ -15,10 +15,13 @@ #include <linux/kprobes.h> #include <linux/sched/task_stack.h> +#include <asm/ptrace.h> #include <asm/debug-monitors.h> #include <asm/insn.h> #include <asm/traps.h> +static DEFINE_PER_CPU(unsigned int, kgdb_pstate); + struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { { "x0", 8, offsetof(struct pt_regs, regs[0])}, { "x1", 8, offsetof(struct pt_regs, regs[1])}, @@ -206,6 +209,10 @@ int kgdb_arch_handle_exception(int exception_vector, int signo, err = 0; break; case 's': + /* mask interrupts while single stepping */ + __this_cpu_write(kgdb_pstate, linux_regs->pstate); + linux_regs->pstate |= PSR_I_BIT; + /* * Update step address value with address passed * with step packet. @@ -251,9 +258,18 @@ NOKPROBE_SYMBOL(kgdb_compiled_brk_fn); static int kgdb_step_brk_fn(struct pt_regs *regs, unsigned int esr) { + unsigned int pstate; + if (!kgdb_single_step) return DBG_HOOK_ERROR; + /* restore interrupt mask status */ + pstate = __this_cpu_read(kgdb_pstate); + if (pstate & PSR_I_BIT) + regs->pstate |= PSR_I_BIT; + else + regs->pstate &= ~PSR_I_BIT; + kgdb_handle_exception(0, SIGTRAP, 0, regs); return DBG_HOOK_HANDLED; }
上面代码很容易理解,我们知道AARCH64有PSSTATE寄存器
armv8_arm_v8.6.pdf D1.7 Process state, PSTATE .................................................................................. D1-2330
这里描述如下:
这里简单直接的在step的时候禁用了IRQ。
这里我们为了gdb能够step调试,能够承担这样的风险,所以合并了这个patch。
当一切准备好了之后,我们直接看到如下信息:
# gdb vmlinux GNU gdb (Kylin 9.1-0kylin1) 9.1 Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "aarch64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from vmlinux... (gdb) set serial baud 1500000 (gdb) target remote /dev/ttyUSB0 Remote debugging using /dev/ttyUSB0 arch_kgdb_breakpoint () at ./arch/arm64/include/asm/kgdb.h:21 21 asm ("brk %0" : : "I" (KGDB_COMPILED_DBG_BRK_IMM)); (gdb) 这里以我常用的do_sys_openat2为例子,如下: (gdb) b do_sys_openat2 Breakpoint 1 at 0xffffffc0082c78e0: do_sys_openat2. (2 locations) (gdb) c Continuing. [Switching to Thread 669] Thread 238 hit Breakpoint 1, 0xffffffc0082c78e0 in do_sys_openat2 () at fs/open.c:1098 1098 if (flags & O_DIRECTORY)
堆栈如下:
(gdb) bt #0 0xffffffc0082c78e0 in do_sys_openat2 () at fs/open.c:1098 #1 0xffffffc0082c7e7c in do_sys_open (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1002 #2 __do_sys_openat (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1249 #3 __se_sys_openat (mode=<optimized out>, flags=<optimized out>, filename=<optimized out>, dfd=<optimized out>) at fs/open.c:1244 #4 __arm64_sys_openat (regs=<optimized out>) at fs/open.c:1244 #5 0xffffffc008026074 in __invoke_syscall (syscall_fn=<optimized out>, regs=0xffffffc00dd4beb0) at arch/arm64/kernel/syscall.c:48 #6 invoke_syscall (syscall_table=0xffffffc009510888 <sys_call_table>, sc_nr=449, scno=<optimized out>, regs=0xffffffc00dd4beb0) at arch/arm64/kernel/syscall.c:48 #7 el0_svc_common (regs=0xffffffc00dd4beb0, scno=<optimized out>, syscall_table=0xffffffc009510888 <sys_call_table>, sc_nr=449) at arch/arm64/kernel/syscall.c:155 #8 0xffffffc0080261a0 in do_el0_svc (regs=<optimized out>) at arch/arm64/kernel/syscall.c:194 #9 0xffffffc0094eea70 in el0_svc (regs=0xffffffc00dd4beb0) at arch/arm64/kernel/entry-common.c:357 #10 0xffffffc0094eefbc in el0_sync_handler (regs=<optimized out>) at arch/arm64/kernel/entry-common.c:373 #11 0xffffffc008011e18 in el0_sync () at arch/arm64/kernel/entry.S:788 Backtrace stopped: Cannot access memory at address 0xffffffc00dd4c0c8
这里可以看到很多optimized out,可能和内核默认的O2有关系,这里就没有演示把内核编译成O1了。
这里我们step一下看看,如下:
(gdb) s Thread 238 hit Breakpoint 1, do_sys_openat2 (dfd=-100, filename=0x55580b8700 <error: Cannot access memory at address 0x55580b8700>, how=how@entry=0xffffffc00dd4bdf8) at fs/open.c:1203 1203 {
可以看到这里信息一切准确。
我们看看汇编和代码
(gdb) disassemble Dump of assembler code for function do_sys_openat2: => 0xffffffc0082c78e0 <+0>: mov x9, x30 0xffffffc0082c78e4 <+4>: nop 0xffffffc0082c78e8 <+0>: stp x29, x30, [sp, #-80]! 0xffffffc0082c78ec <+4>: mov x29, sp 0xffffffc0082c78f0 <+8>: stp x19, x20, [sp, #16] 0xffffffc0082c78f4 <+12>: mrs x20, sp_el0 0xffffffc0082c78f8 <+16>: mov x19, x2 0xffffffc0082c78fc <+20>: stp x21, x22, [sp, #32] 0xffffffc0082c7900 <+24>: mov x21, x1 0xffffffc0082c7904 <+28>: mov w22, w0 0xffffffc0082c7908 <+32>: ldr x1, [x20, #1344] 0xffffffc0082c790c <+36>: str x1, [sp, #72] 0xffffffc0082c7910 <+40>: mov x1, #0x0 // #0 0xffffffc0082c7914 <+44>: mov x0, x2 0xffffffc0082c7918 <+48>: add x1, sp, #0x34 0xffffffc0082c791c <+52>: bl 0xffffffc0082c7740 <build_open_flags> 0xffffffc0082c7920 <+56>: cbz w0, 0xffffffc0082c794c <do_sys_openat2+100> 0xffffffc0082c7924 <+60>: sxtw x0, w0 0xffffffc0082c7928 <+64>: add x20, x20, #0x540 0xffffffc0082c792c <+68>: ldr x2, [sp, #72] 0xffffffc0082c7930 <+72>: ldr x1, [x20] 0xffffffc0082c7934 <+76>: eor x1, x2, x1 0xffffffc0082c7938 <+80>: cbnz x1, 0xffffffc0082c7a34 <do_sys_openat2+332> 0xffffffc0082c793c <+84>: ldp x19, x20, [sp, #16] 0xffffffc0082c7940 <+88>: ldp x21, x22, [sp, #32] 0xffffffc0082c7944 <+92>: ldp x29, x30, [sp], #80 0xffffffc0082c7948 <+96>: ret 0xffffffc0082c794c <+100>: mov x0, x21 ............
对于代码:
static long do_sys_openat2(int dfd, const char __user *filename, struct open_how *how) { struct open_flags op; int fd = build_open_flags(how, &op); struct filename *tmp; if (fd) return fd; ............
我们系统虽然已经经历过一次qt化,但是仍然有一些默认的程序还是基于gtk的,例如mate-terminal和filezilla,这些程序在我们系统上,针对一些使用习惯上的问题,还是存在一些bug的,对于这些问题,我们是操作系统的提供者,所以有必要查看原因和解决。
例如filezilla程序在我们默认kylin-virtual-keyboard程序上会默认自动唤起虚拟键盘,经过定位我们发现此行为的原因是filezilla主动发起了focus事件,所以对于kylin-virtual-keyboard的行为是正常的,这种情况下,我们就需要调试filezilla。而针对gtk程序,通常情况下,我们有一个检查程序inspector,这个程序可以给我们初步定位问题,本文章主要介绍一下inspector的使用说明。便于大家针对gtk的程序来进行初步问题筛查
针对gtk类的程序,我们都可以用gtk debug来尝试调试,gtk包含了多个调试选项,这里就不一一列举了,可以参考如下文章,我们以gtk3为例
https://docs.gtk.org/gtk3/running.html
我们关心的是inspector,所以如下:
interactive: Open the interactive debugger
关于检查器的官方介绍文档,可以参考如下:
https://developer.gnome.org/documentation/tools/inspector.html
inspector有多种打开方式,主要如下:
我们可以通过--gtk-debug=interactive打开程序的检查器,以filezilla为例
# filezilla --gtk-debug=interactive
可以通过赋予GTK_DEBUG的值来打开调试,如下:
# GTK_DEBUG=interactive filezilla
为了支持快捷键打开检查器,我们需要先将gtk的gsettings值打开,如下:
gsettings set org.gtk.Settings.Debug enable-inspector-keybinding true
此时我们可以通过如下两种方式打开(在运行程序内部按下快捷键)
ctrl + shift + i / ctrl + shift + d
打开后调试界面如下所示:
通过上述图片我们可以发现几个要素,如下:
我们打开检查器时,默认就打开了对象信息,如上图,未展开情况下我们可以知道此程序顶层存在几个widget,可以发现filezilla总共存在三个GtkWindow顶层窗口。这里两个置灰的窗口意味着未正常显示。所以如果我们点击非置灰的GtkWindow,我们可以看到filezilla程序会闪烁如下:
根据上述,我们可以知道第二个GtkWindow就是filezilla的顶层窗口,我们可以进一步跟踪,直到找到远程站点的文件列表的GtkWidget是什么类型为止,如下图,我们发现远程站点的文件列表实际上是一个GtkScrolledWindow类型。
如果需要显示统计信息,则需要将glib设置为debug版本,也就是提供 --enable-debug
参数,并且程序在运行时需要提供如下配置:
GOBJECT_DEBUG=instance-count
这里我们的系统没有把glib编译带此参数,故无法演示
对于资源,我们可以理解和qt的res一样,也就是这个程序需要加载的动效,图片,和css,主要是一个统计作用,效果如下:
样式表我们可以给对应的窗口设置相应的样式用作测试css效果。
这里有多种关于主题,窗口属性,渲染细节的设置,细节不一一展示了,如下所示。
这里提供了gtk版本等一系列的基本信息的查看,如下:
inspector可以支持直接选择对象,我们点击右上角的准星按钮如下:
此时我们的光标变成准星来选择窗口,如下
当选择完毕,则提供这个窗口的显示细节,如下显示
以属性为例,我们可以动态的修改某个子窗口的属性值如下
细节是靠近选择对象的按钮,它可以将窗口转到窗口的具体细节,例如杂项,属性,信号,子元素属性,类层级结构,CSS选择器,CSS节点,放大镜,如下图:
至此,我们介绍了inspector的基本操作,希望这些介绍有助于我们调试linux上的gtk程序