Debug Kernel(kgdb, gcover, blktrace, ftrace)(Rockchip Linux)

|

kgdb

Introduction

以前上学的时候, 我参加过一开源活动, 给一个RTOS移植GDB Stub, 那个时候看过内核的kgdb实现.
在我的印象里, 内核的arm kgdb比我实现的还简陋..只有软件断点, 没有数据断点, 没有单步支持… 现在看git log, 似乎还是这样子……

不过相比log debug, kgdb可以用作vanilla kernel上的ttyfiq替代项, 专门来debug hang住的系统, 比如死锁问题. 因此kgdb在实际环境上还是蛮有用的, 更多的用处可以看这里linaro的文章:
https://www.linaro.org/blog/core-dump/debugging-arm-kernels-using-nmifiq/

Usage

Kconfig:

CONFIG_KGDB
CONFIG_SERIAL_KGDB_NMI

1.编译烧写完内核后, 在Kernel Cmdline里加入

kgdbwait kgdbcon kgdboc=ttyS2,115200

kgdbwait:
The Kernel command line option kgdbwait makes kgdb wait for a debugger connection during booting of a kernel.

kgdbcon:
The kgdbcon feature allows you to see printk() messages inside gdb while gdb is connected to the kernel. Kdb does not make use of the kgdbcon feature.

kgdboc:
The kgdboc driver was originally an abbreviation meant to stand for “kgdb over console”. Today it is the primary mechanism to configure how to communicate from gdb to kgdb as well as the devices you want to use to interact with the kdb shell.

也可以使用下面命令通过以太网debug.

kgdbwait kgdbcon kgdboe=@192.168.248.76/,@192.168.242.24/00:11:22:33:44:55

内核起来到所需的driver初始化完后, 就会停住等待Host链接.

2.In Host side,

$arm-none-eabi-gdb vmlinux

    gdb > set serial baud 115200
    gdb > target remote /dev/ttyUSB0

or

arm-none-eabi-gdb vmlinux -ex "set print pretty on" -ex "set pagination off" -ex "set serial baud 115200" -ex "target remote /dev/ttyUSB0"

执行continue后, 可以在client上输入下面命令回到中断上:

echo g > /proc/sysrq-trigger

如果打开了CONFIG_SERIAL_KGDB_NMI, 可以直接通过nmi终端唤醒, 在串口上做如下操作就可进到gdb:

To trigger kdb you must manually type the gdbserver protocols wake up command $3#33

如果是调式kernel module的话, 需要另外加载符号

add-symbol-file ./test/test.ko 0xbf000000

地址可在client用下面命令取得:

MODULE_NAME=your_module_name
MODULE_FILE=$(modinfo $MODULE_NAME| awk '/filename/{print $2}')
DIR="/sys/module/${MODULE_NAME}/sections/"
echo add-symbol-file $MODULE_FILE $(cat "$DIR/.text") -s .bss $(cat "$DIR/.bss") -s .data $(cat "$DIR/.data")

ftrace

Ftrace is an internal tracer designed to help out developers and designers of systems to find what is going on inside the kernel.

Ftrace是一个非常好用的profiling/trace工具, 如果你需要在项目中分析函数的调用时间(perf那是用来测i/o效率的), 或者函数的调用过程.

Ftrace的理解应该结合Kernel tracepoint来看, 所以最好先看下下面这篇文章:

Usage

Kconfig:

CONFIG_HAVE_FTRACE_NMI_ENTER=y 
CONFIG_HAVE_DYNAMIC_FTRACE=y 
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y 
CONFIG_FTRACE_NMI_ENTER=y 
CONFIG_FTRACE=y
CONFIG_FTRACE_SYSCALLS=y 
CONFIG_DYNAMIC_FTRACE=y 
CONFIG_FTRACE_MCOUNT_RECORD=y 

进入trace目录:

cd /sys/kernel/debug/tracing/

查看可用的tracer:

cat available_tracers 

开始跟踪:

echo > set_event
echo 0 > tracing_on
echo > trace
echo function_graph > current_tracer 
echo 1 > tracing_on
cat trace

下面的命令可以过滤函数:

echo vmalloc_* > set_ftrace_filter

或者特定事件:

cat available_events 

ftrace的参数还有很多, 这里也不好列出来.
有需要, 你可以转到下面的链接来看看解释:
https://github.com/wzyy2/linux/blob/master/Documentation/trace/ftrace.txt
https://www.ibm.com/developerworks/cn/linux/l-cn-ftrace/

blktrace

gcover

Remote Wake Up from Deep Sleep with USB

|

PM Code have support keeping power for USB.

You just need to apply below pacthes to avoid usb phy being disabled.

    From ba76095e25daa35220f681aaccbcd54f2940b222 Mon Sep 17 00:00:00 2001
    From: Jacob Chen <jacob-chen@iotwrt.com>
    Date: Thu, 7 Sep 2017 15:06:13 +0800
    Subject: [PATCH] USB: dwc2: Don't turn off the usbphy in suspend

    Change-Id: I0dc42678f44664dfc2bbbc67bbfdf851bb87369a
    Signed-off-by: Jacob Chen <jacob-chen@iotwrt.com>
    ---
    drivers/usb/dwc2/platform.c | 4 ++++
    1 file changed, 4 insertions(+)

    diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
    index 60ffcb6..113bea0 100644
    --- a/drivers/usb/dwc2/platform.c
    +++ b/drivers/usb/dwc2/platform.c
    @@ -641,6 +641,8 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
            if (dwc2_is_device_mode(dwc2))
                    dwc2_hsotg_suspend(dwc2);
    
    +	return 0;
    +
            if (dwc2->ll_hw_enabled)
                    ret = __dwc2_lowlevel_hw_disable(dwc2);
    
    @@ -652,6 +654,8 @@ static int __maybe_unused dwc2_resume(struct device *dev)
            struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
            int ret = 0;
    
    +	return 0;
    +
            if (dwc2->ll_hw_enabled) {
                    ret = __dwc2_lowlevel_hw_enable(dwc2);
                    if (ret)
    -- 
    2.7.4

Use OpenCV with Gstreamer

|

最近在Rockchip Linux的平台尝试了一下OpenCV + Gstreamer的组合, 发现效果还蛮不错的. :)
过程中有些心得, 在这里记录一下…. 我想这些也不只适用RockChip平台,因为涉及的都是标准的概念, 比如DMABUF, DRM, OpenCL,G2D…放到像Intel, Nvdia这些平台也是成立的.

下面的内容会涉及一些Linux概念, 如果你不懂的话建议先查阅下相关文章, 当然最好是接触下对应的开发: (有需要解释可以留言, 如果你看的到disqus的话 :-P)

Code

一个简单的人脸识别应用:
使用了2D加速, 视频硬解加速
gstreamer-opencv

Changes

这几天尝试添加了一下异步处理, 这样来看拷贝的方式反而不重要了, 因为一秒里可能就处理了2,3张图片而已, 拷贝耗时不大, 而且拷贝后的buffer是cached的normal内存, 处理起来速度会更快. 所以拷贝是不是个问题, 得看相应的应用场景和算法需求.

Background

Gstreamer

首先,要先讨论下为什么需要在OpenCV上用上Gstreamer. 比如我直接一个摄像头 v4l2 图像传给 OpenCV 不行吗?

Gstreamer是嵌入式平台处理Media的首选组件, 像Nvdia/TI/NXP/Rockchip平台, 都是使用Gstreamer来整合Media应用. 在Rockchip平台上, 我们已经有为Gstreamer开发了像Decode/Encode/ISP-Camera/2D加速器/DRM-Display-sink这些的Plugin.

所以OpenCV如果链接上Gstreamer, 输入源就不仅仅是摄像头, 还可以是RTSP/本地视频;输出显示的代码可以不用写, 让Gstreamer来显示; 转换格式让Gstreamer来转, 利用硬件加速; 处理的图像送回Gstreamer编码.

ARM

在ARM系统上做Media的开发, 有一个原则要很重要, 就是 : 避免拷贝.
如果你手边正好有一块ARM板子和Linux PC, 可以尝试在上面跑一些memcpy的Test. 一般来说, 测试的性能会相差5,6倍. 即时是DDR同频的两个系统, 性能也会差到3-4倍(不过也可能是DDR其他参数有影响?). 内存操作速度的劣势是RISC天生的, ARM也不列外. (虽然也没有研究过对应微处理器结构,道听途说 :-P)

还有一个更影响速度的就是, 这些Buffer一般都是uncached的DMA Buffer, 为了保证cpu和其他ip的内存一致性, 所以CPU读写速度就更慢了..

在开发OpenCV + Gstreamer的过程中, 一定要尽量避免拷贝的发生, 如果一定要有, 也不能是由CPU来做. (替代可以是2D加速器, GPU)

OpenCV

我之前只在X86上使用过OpenCV, 其实不太了解OpenCV在ARM Device需要怎么开发. (怀疑其他ARM平台上到底能不能用OpenCV, 因为像TI/NXP这种, CPU/GPU太弱, 估计只能内部的DSP跑算法; 像全志, 基本没有Linux平台的组件支持; 唯一能搞的估计也就是Nvdia的terga了, cuda还是厉害. ;) )

根据上面ARM的原则, 开发的时候要避免调用到OpenCv的cvtcolor和clone这些函数, 因为每次拷贝都会消耗大量的CPU资源.

OpenCV也支持OpenCL加速, 当然..其实没什么卵用, 尤其你是在处理实时的图像的时候, 因为GPU处理数据的时候, 需要加载Texture到GPU内存上, 放OpenCL上, 就是你要处理的帧, 全部要拷一份到新的内存地址上….虽然在嵌入式设备上, GPU并没有和CPU使用分离的内存, 完全没必要这么做; 在图形应用的框架上, GPU处理dmabuf都是zero-copy的, 也就是要处理的帧, 只要让GPU MMAP一下就可以了, 而OpenCV, OpenCL, 我是没找到方法…(所以GPU通用计算还是要靠Vulkan了..)
当然在算法的处理耗时有好几秒的时候, 加载纹理消耗10毫秒也是可以忽视的 : 这种场合才建议使用OpenCL.

才发现这个好像是ARM上特有的问题, opencv已经是用了CL_MEM_USE_HOST_PTR, 理论上不应该有拷贝. 但是ARM上这个flag却会导致拷贝, ARM上需要使用特殊的api来做zero-copy.
嗯…这样你得去修改OpenCV才能用起来…

Desgin

Pipeline

Pipeline Prototype 1:

    video/rtsp/camera -> decoder -> opencv

这是我最先想到的, 通过gstreamer拿到decoder的buffer, 然后全部由opencv来处理. 但是前面说过, 要避免拷贝, 而opencv的显示 imshow , 是存在大量拷贝的, 所以不能这么做.

Pipeline Prototype 2:

    video/rtsp/camera -> decoder -> opencv -> display sink

为了优化显示, 需要把buffer送回给gstreamer, 这样就得到了Prototype 2. 但是是要注意, OpenCV的默认格式是bgr的, 所有的画图函数都是基于bgr; CV的大部分算法都是都需要预处理成灰度图, 而某些图像格式排列不适合转换灰度图.
在Rockchip平台上Decoder出来的颜色格式是NV12的, 必须要想办法转换成BGR格式.
所以decoder到opencv之间还需要有处理颜色格式的单元, 这个工作不可能由CPU来做, 一般可以使用专有硬件, 如果相应的平台没有这样的硬件, 也可以使用GPU用特定的Shader来转(OpenGL的设计目的里, 加速2D就是很重要的一块, 我们有时候看到QT/Wayland这些地方说使用到GPU加速, 就是用GPU做这样的事).

Pipeline Prototype 3:

    video/rtsp/camera -> decoder -> 2d convert -> opencv -> display sink

Implement

首先opencv在gstreamer是有plugin的, 但是从应用开发的角度, 这样不够flexible : plugin里的东西和外界是封闭的. 在实现上, 更建议使用Appsink和AppSrc, 这些模块, 在你的应用里, 是以Thread的形式存在的, 开发起来要更方便.
另外还有一点很重要, 就是什么gstreamer, gobject, 真的很恶心…还是C++舒服…

更多懒得解释了, 看代码吧.. :-P
涉及到的平台坑我都在代码里踩了给你了….
结构上大致如下: Gstreamer AppSink不停的送Buffer, 应用MMap出来给OpenCV处理, 完后AppSrc送会Gstreamer显示.

Gstreamer Pipeline:

    video/rtsp/camera ! decoder ! v4l2videoconvert ! appsink
    appsrc ! display

Rockchip Gstreamer Pipeline:

    "filesrc location=/usr/local/test.mp4 ! qtdemux ! h264parse ! mppvideodec \
    ! v4l2video0convert output-io-mode=dmabuf capture-io-mode=dmabuf ! \
    video/x-raw,format=BGR,width=(int)1920,height=(int)1080 ! \
    appsink caps=video/x-raw,format=BGR name=sink"

    "appsrc caps=video/x-raw,format=(string)BGR,width=(int)1920,height=(int)1080,framerate=(fraction)30/1 \
    block=true name=src ! rkximagesink sync=false";

MiPi CSI(Rockchip Linux)

|

RK平台上android使用的MIPI CSI驱动是userspace based的驱动, 需要基于android的hal层使用,因此并不适合linux平台.

按照linux standard, 需要是写成v4l2形式的驱动, 类似uvc, 从而可以兼容常见的应用, 比如opencv, gstreamer……

RK的v4l2驱动也分两套.

一套叫cif_isp10, 用于rv系列和sofia系列的linux系统. 我个人来说, 并不是很喜欢这套驱动, 因为他只是output的接口用了v4l2而已, 但内部的实现比较蛋疼. 一是sensor驱动的框架是自己实现了一套,并没有基于标准的subdev接口, 调试sensor的时候不能使用已有的在driver/media/i2c下的驱动. 二是模块分层并不清晰, 理解代码和定位流程都很麻烦.

另一套叫rockchip-isp1, 是后来在chromeos项目上基于cif_isp10改出来的, 符合linux standard的驱动, 也是我们现在给3288使用的驱动.
这个驱动有了比较多的review, 结构比较清晰.

拓扑接口:
sensor->mipiphy->isp subdev->sp/mp vdev

如下的硬件框图:
3的输出对应mp vdev(/dev/videox+2), 可以输出raw格式和yuv格式的数据.
4的输出对应sp vdev(/dev/videox), 可以输出yuv格式和rgb格式的数据.
1-crop, 2-resize, 3和4全部都在capture.c里, 对外stream vdev的形式, 就是你看到的/dev/videox, 对这个节点s_fmt调用resize, s_selesction调用crop.
isp, ie, si是用于3a, 图像处理的部分, 被隐藏在isp subdev里, 默认下输出的是未处理的图像, 只有使用isp gstreamer plugin或者我们提供的linux hal时, 会操作这个subdev, 传入tuning的数据.

Tips

sensor

调试sensor的技巧:

第一步是在rkisp1_mipi_isr里加中断, 确认mipi phy的信号有出来. mipi phy有信号, 可以验证以下事情: mipi phy/controler可以接收输入, sensor信号有了输出. 反之如果没有, 原因也就是这个两个, 可以用下面的命令来验证.

io -4 -l 0x100 0xff911c00(rk3288)

出来的数据, 第2个部分代表mipi status, 多输入几次, 看看有没有变化, bit 8:11代表data lane0-3的stop state, bit12代表clock lane stop state. 如果有所的stop bit没变, 需要检查sensor是不是正确输出了, 如果只是clock lane, 需要看下sensor的clock lane是不是在s_stream前没在lp11模式.

第二步, 如果mipi_isr有中断了, 但是有sync_error的错误, 表示mipi接收的图像和isp配置的信息不一致, 可以有以下原因: mipi接收的图形大小和isp配置的不一致, mipi接收的信号有问题.
第一个原因,检查下是不是sensor配置了1080p输出, 但是设置的软件size确是720p.
第二个原因,检查下是不是bitrate设置的有问题, lane num设置的有问题, 硬件信号有问题.

Burn Mac Address(Rockchip Linux)

|

Unlike rockchip android, we don’t limit you how to get mac address. You can write/read it in any places.

Usually, it could be stored in reserved1.
http://opensource.rock-chips.com/wiki_Partitions

Bellow is how to burn mac address to reserved1 and read it.

First, enter maskrom and issue fllowing command to burn mac adress.

printf '\xde\xad\xbe\xef\xef\xee' > mac.bin
build/flash_tool.sh -p reserved1 -c rk3288 -i mac.bin

Then, add ethadder-set fucntion to u-boot board file, it will read mac address from reserved1.

#include <mmc.h>

int rk_board_late_init(void)
{
  u8 ethaddr[1024];

  // 1 for sd-card
  struct mmc *mmc = find_mmc_device(0);
  struct blk_desc *desc = mmc_get_blk_desc(mmc);
  unsigned long blk_start = 8064;
  unsigned long blk_cnt = 1;

  int d = blk_dread(desc, blk_start, blk_cnt, ethaddr);

  eth_setenv_enetaddr("ethaddr", ethaddr);

  return 0;
}

Comments