观察 Linux 内核中的变量(内存地址)变化,并在变化时打印堆栈跟踪?

2024-02-19

我想以某种方式“监视”Linux 内核(确切地说是内核模块/驱动程序)中的变量(或者内存地址);并找出改变它的原因 - 基本上,当变量改变时打印出堆栈跟踪。

例如,在内核模块中testjiffy-hr.c列于末尾这个答案 https://stackoverflow.com/questions/16920238/reliability-of-linux-kernel-add-timer-at-resolution-of-one-jiffy/17055867#17055867,我想每次打印出堆栈跟踪runcount变量变化;希望堆栈跟踪会包含提及testjiffy_timer_function,这确实是更改该变量的函数。

现在,我知道我可以使用kgdb连接到在虚拟机中运行的调试 Linux 内核,甚至像这样设置断点(希望也是观察点) - 但问题是我实际上想调试 ALSA 驱动程序,特别是播放dma_area缓冲区(我在其中获取一些意外数据)-对时间高度敏感;并且运行调试内核本身会扰乱计时(更不用说在虚拟机中运行它了)。

这里更大的问题是播放dma_area指针仅在播放操作期间存在(或者换句话说,在_start and _stop处理程序) - 所以我必须记录dma_area地址在每个_start回调,然后以某种方式“安排”它在播放操作期间“观看”。

所以我希望有一种方法可以直接在驱动程序代码中执行类似的操作 - 例如,在其中添加一些代码_start记录的回调dma_area指针,并将其用作启动“监视”更改的命令的参数;以及从相应的回调函数打印的堆栈跟踪。 (我知道这也会影响时间,但我希望它足够“轻”,不会对“实时”驱动程序操作产生太大影响)。

所以我的问题是:Linux 内核中是否存在这样的调试技术?

如果不是:是否可以设置一个硬件(或软件)中断,对特定内存地址的更改做出反应?那么我可以设置这样一个中断处理程序,可以打印出堆栈跟踪吗? (尽管如此,我认为当 IRQ 处理程序运行时整个上下文会发生变化,因此获取堆栈跟踪可能是错误的)?

如果没有:是否还有其他技术可以让我打印更改存储在内核中给定内存位置中的值的进程的堆栈跟踪(希望在实时的非调试内核中)?


非常感谢各位的回复@CosminRatiu https://stackoverflow.com/a/19734787/277826 and Eugene https://stackoverflow.com/questions/19725900/watch-a-variable-memory-address-change-in-linux-kernel-and-print-stack-trace#comment29352526_19734787;感谢那些,我发现:

  • 调试 - Linux内核硬件断点 - VoidCC https://stackoverflow.com/questions/16362063/linux-kernel-hardware-break-points/16377522#16377522
  • 硬件断点(或观察点) - Linux 内核档案 https://www.kernel.org/doc/ols/2009/ols2009-pages-149-158.pdf

...我可以用它来开发我在这里发布的示例,testhrarr.c内核模块/驱动程序和Makefile(以下)。它演示了硬件观察点跟踪可以通过两种方式实现:使用perf程序,可以在不改变的情况下探测驱动程序;或者通过向驱动程序添加一些硬件断点代码(在示例中,由HWDEBUG_STACK定义变量)。

本质上,调试标准原子变量类型(如 int)的内容(如runcount变量)很简单,只要它们在内核模块中定义为全局变量,因此它们最终会在全局范围内显示为内核符号。因此,下面的代码添加了testhrarr_作为变量的前缀(以避免命名冲突)。但是,由于需要取消引用,调试数组的内容可能会有点棘手 - 这就是本文演示的内容,调试数组的第一个字节testhrarr_arr大批。它是在以下时间完成的:

$ echo `cat /etc/lsb-release` 
DISTRIB_ID=Ubuntu DISTRIB_RELEASE=11.04 DISTRIB_CODENAME=natty DISTRIB_DESCRIPTION="Ubuntu 11.04"
$ uname -a
Linux mypc 2.6.38-16-generic #67-Ubuntu SMP Thu Sep 6 18:00:43 UTC 2012 i686 i686 i386 GNU/Linux
$ cat /proc/cpuinfo | grep "model name"
model name  : Intel(R) Atom(TM) CPU N450   @ 1.66GHz
model name  : Intel(R) Atom(TM) CPU N450   @ 1.66GHz

The testhrarr模块基本上在模块初始化时为一个小数组分配内存,设置一个计时器函数,并公开一个/proc/testhrarr_proc文件(使用较新的proc_create界面)。然后,尝试从/proc/testhrarr_proc文件(例如,使用cat)将触发定时器功能,这将修改testhrarr_arr数组值,并将消息转储到/var/log/syslog。我们期望testhrarr_arr[0]运行过程中会改变3次;曾在testhrarr_startup,并两次testhrarr_timer_function(由于包装)。

using perf

构建模块后make,您可以使用以下命令加载它:

sudo insmod ./testhrarr.ko

在那时候,/var/log/syslog将包含:

kernel: [40277.199913] Init testhrarr: 0 ; HZ: 250 ; 1/HZ (ms): 4 ; hrres: 0.000000001
kernel: [40277.199930]  Addresses: _runcount 0xf84be22c ; _arr 0xf84be2a0 ; _arr[0] 0xed182a80 (0xed182a80) ; _timer_function 0xf84bc1c3 ; my_hrtimer 0xf84be260; my_hrt.f 0xf84be27c
kernel: [40277.220329] HW Breakpoint for testhrarr_arr write installed (0xf84be2a0)

请注意,只是通过testhrarr_arr作为硬件观察点的符号扫描该变量的地址(0xf84be2a0),而不是数组第一个元素的地址 (0xed182a80)!因此,硬件断点是not将要触发 - 因此行为就像硬件断点代码根本不存在一样(这可以通过取消定义来实现)HWDEBUG_STACK)!

因此,即使没有通过内核模块代码设置硬件断点,我们仍然可以使用perf观察内存地址的变化 - 在perf,我们指定我们想要观看的地址(这里是第一个元素的地址testhrarr_arr, 0xed182a80),以及应该运行的进程:这里我们运行bash,所以我们可以执行cat /proc/testhrarr_proc这将触发内核模块计时器,然后是sleep 0.5这将使计时器完成。这-a还需要参数,否则可能会错过一些事件:

$ sudo perf record -a --call-graph --event=mem:0xed182a80:w bash -c 'cat /proc/testhrarr_proc ; sleep 0.5'
testhrarr proc: startup
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.485 MB perf.data (~21172 samples) ]

在此刻,/var/log/syslog还会包含类似的内容:



[40822.114964]  testhrarr_timer_function: testhrarr_runcount 0 
[40822.114980]  testhrarr jiffies 10130528 ; ret: 1 ; ktnsec: 40822114975062
[40822.118956]  testhrarr_timer_function: testhrarr_runcount 1 
[40822.118977]  testhrarr jiffies 10130529 ; ret: 1 ; ktnsec: 40822118973195
[40822.122940]  testhrarr_timer_function: testhrarr_runcount 2 
[40822.122956]  testhrarr jiffies 10130530 ; ret: 1 ; ktnsec: 40822122951143
[40822.126962]  testhrarr_timer_function: testhrarr_runcount 3 
[40822.126978]  testhrarr jiffies 10130531 ; ret: 1 ; ktnsec: 40822126973583
[40822.130941]  testhrarr_timer_function: testhrarr_runcount 4 
[40822.130961]  testhrarr jiffies 10130532 ; ret: 1 ; ktnsec: 40822130955167
[40822.134940]  testhrarr_timer_function: testhrarr_runcount 5 
[40822.134962]  testhrarr jiffies 10130533 ; ret: 1 ; ktnsec: 40822134958888
[40822.138936]  testhrarr_timer_function: testhrarr_runcount 6 
[40822.138958]  testhrarr jiffies 10130534 ; ret: 1 ; ktnsec: 40822138955693
[40822.142940]  testhrarr_timer_function: testhrarr_runcount 7 
[40822.142962]  testhrarr jiffies 10130535 ; ret: 1 ; ktnsec: 40822142959345
[40822.146936]  testhrarr_timer_function: testhrarr_runcount 8 
[40822.146957]  testhrarr jiffies 10130536 ; ret: 1 ; ktnsec: 40822146954479
[40822.150949]  testhrarr_timer_function: testhrarr_runcount 9 
[40822.150970]  testhrarr jiffies 10130537 ; ret: 1 ; ktnsec: 40822150963438
[40822.154974]  testhrarr_timer_function: testhrarr_runcount 10 
[40822.154988] testhrarr [ 5, 7, 9, 11, 13, ]
  

读取捕获的perf(一个名为perf.data) 我们可以用:


$ sudo perf report --call-graph flat --stdio
No kallsyms or vmlinux with build-id 5031df4d8668bcc45a7bdb4023909c6f8e2d3d34 was found
[testhrarr] with build id 5031df4d8668bcc45a7bdb4023909c6f8e2d3d34 not found, continuing without symbols
Failed to open /bin/cat, continuing without symbols
Failed to open /usr/lib/libpixman-1.so.0.20.2, continuing without symbols
Failed to open /usr/lib/xorg/modules/drivers/intel_drv.so, continuing without symbols
Failed to open /usr/bin/Xorg, continuing without symbols
# Events: 5  unknown
#
# Overhead  Command  Shared Object                                Symbol
# ........  .......  .............  ....................................
#
    87.50%     Xorg  [testhrarr]    [k] testhrarr_timer_function
            87.50%
                testhrarr_timer_function
                __run_hrtimer
                hrtimer_interrupt
                smp_apic_timer_interrupt
                apic_timer_interrupt
                0x30185d
                0x2ed701
                0x2ed8cc
                0x2edba0
                0x9d0386
                0x8126fc8
                0x81217a1
                0x811bdd3
                0x8070aa7
                0x806281c
                __libc_start_main
                0x8062411

     6.25%      cat  [testhrarr]    [k] testhrarr_timer_function
             6.25%
                testhrarr_timer_function
                testhrarr_proc_show
                seq_read
                proc_reg_read
                vfs_read
                sys_read
                syscall_call
                0xaa2416
                0x8049f4d
                __libc_start_main
                0x8049081

     3.12%  swapper  [testhrarr]    [k] testhrarr_timer_function
             3.12%
                testhrarr_timer_function
                __run_hrtimer
                hrtimer_interrupt
                smp_apic_timer_interrupt
                apic_timer_interrupt
                cpuidle_idle_call
                cpu_idle
                start_secondary

     3.12%      cat  [testhrarr]    [k] 0x356   
             3.12%
                0xf84bc356
                0xf84bc3a7
                seq_read
                proc_reg_read
                vfs_read
                sys_read
                syscall_call
                0xaa2416
                0x8049f4d
                __libc_start_main
                0x8049081



#
# (For a higher level overview, try: perf report --sort comm,dso)
#
  

因此,由于我们正在构建内核模块并进行调试(-g in the Makefile),这不是问题perf找到该模块的符号,即使实时内核不是调试内核。所以它正确解释testhrarr_timer_function大多数时候作为二传手,尽管它不报告testhrarr_startup(但它确实报告testhrarr_proc_show这就是所谓的)。也有参考文献0xf84bc3a7 and 0xf84bc356它无法解决;但是,请注意该模块的加载时间为0xf84bc000:

$ sudo cat /proc/modules | grep testhr
testhrarr 13433 0 - Live 0xf84bc000

...该条目也以...[k] 0x356;如果我们看看objdump内核模块的:


$ objdump -S testhrarr.ko | less
...
00000323 :

static void testhrarr_startup(void)
{
...
    testhrarr_arr[0] = 0; //just the first element
 34b:   a1 80 00 00 00          mov    0x80,%eax
 350:   c7 00 00 00 00 00       movl   $0x0,(%eax)
    hrtimer_start(&my;_hrtimer, ktime_period_ns, HRTIMER_MODE_REL);
 356:   c7 04 24 01 00 00 00    movl   $0x1,(%esp)                     **********
 35d:   8b 15 1c 00 00 00       mov    0x1c,%edx
...
00000375 :


static int testhrarr_proc_show(struct seq_file *m, void *v) {
...
    seq_printf(m, "testhrarr proc: startup\n");
 38f:   c7 44 24 04 79 00 00    movl   $0x79,0x4(%esp)
 396:   00 
 397:   8b 45 fc                mov    -0x4(%ebp),%eax
 39a:   89 04 24                mov    %eax,(%esp)
 39d:   e8 fc ff ff ff          call   39e 
    testhrarr_startup();
 3a2:   e8 7c ff ff ff          call   323 
 3a7:   eb 1c                   jmp    3c5   **********
  } else {
    seq_printf(m, "testhrarr proc: (is running, %d)\n", testhrarr_runcount);
 3a9:   a1 0c 00 00 00          mov    0xc,%eax
...
  

... so 0xf84bc356显然是指hrtimer_start; and 0xf84bc3a7 -> 3a7指的是它的调用testhrarr_proc_show功能;值得庆幸的是,这是有道理的。 (请注意,我体验过不同版本的驱动程序,_start可以显示,并且timer_function用纯粹的地址来表达;不确定这是由于什么)。

有一个问题perf不过,它给了我这些函数发生的统计“开销”(不确定它指的是什么——可能是函数进入和退出之间花费的时间?)——但我真正想要的是堆栈日志跟踪是连续的。不知道perf可以为此进行设置 - 但它肯定是通过硬件断点的内核模块代码来完成的。

使用内核模块硬件断点

代码位于HWDEBUG_STACK实现硬件断点设置和处理。如前所述,符号的默认设置ksym_name(如果未指定),是testhrarr_arr,这根本不会触发硬件断点。这ksym_name可以在命令行中指定参数insmod;这里我们可以注意到:

$ sudo rmmod testhrarr    # remove module if still loaded
$ sudo insmod ./testhrarr.ko ksym=testhrarr_arr[0]

... 结果与HW Breakpoint for testhrarr_arr[0] write installed (0x (null)) in /var/log/syslog; - 这意味着我们不能使用带有括号表示法的符号进行数组访问;值得庆幸的是,这里的空指针仅仅意味着硬件断点将再次不会触发;它不会使操作系统完全崩溃:)

然而,有一个全局变量来引用第一个元素testhrarr_arr数组,称为testhrarr_arr_first- 注意这个全局变量在代码中是如何特殊处理的,需要解引用,这样才能获得正确的地址。所以我们这样做:

$ sudo rmmod testhrarr    # remove module if still loaded
$ sudo insmod ./testhrarr.ko ksym=testhrarr_arr_first

...并且系统日志通知:

kernel: [43910.509726] Init testhrarr: 0 ; HZ: 250 ; 1/HZ (ms): 4 ; hrres: 0.000000001
kernel: [43910.509765]  Addresses: _runcount 0xf84be22c ; _arr 0xf84be2a0 ; _arr[0] 0xedf6c5c0 (0xedf6c5c0) ; _timer_function 0xf84bc1c3 ; my_hrtimer 0xf84be260; my_hrt.f 0xf84be27c
kernel: [43910.538535] HW Breakpoint for testhrarr_arr_first write installed (0xedf6c5c0)

...我们可以看到硬件断点设置在0xedf6c5c0,这是地址testhrarr_arr[0]。现在,如果我们通过以下方式触发驱动程序/proc file:

$ cat /proc/testhrarr_proc 
testhrarr proc: startup

...我们得到syslog:


kernel: [44069.735695] testhrarr_arr_first value is changed
[44069.735711] Pid: 29320, comm: cat Not tainted 2.6.38-16-generic #67-Ubuntu
[44069.735719] Call Trace:
[44069.735737]  [] ? sample_hbp_handler+0x2d/0x3b [testhrarr]
[44069.735755]  [] ? __perf_event_overflow+0x90/0x240
[44069.735768]  [] ? proc_alloc_inode+0x23/0x90
[44069.735778]  [] ? proc_alloc_inode+0x23/0x90
[44069.735790]  [] ? perf_swevent_event+0x136/0x140
[44069.735801]  [] ? perf_bp_event+0x70/0x80
[44069.735812]  [] ? prep_new_page+0x110/0x1a0
[44069.735824]  [] ? get_page_from_freelist+0x12e/0x320
[44069.735836]  [] ? seq_open+0x3d/0xa0
[44069.735848]  [] ? hw_breakpoint_handler.clone.0+0x102/0x130
[44069.735861]  [] ? hw_breakpoint_exceptions_notify+0x22/0x30
[44069.735872]  [] ? notifier_call_chain+0x45/0x60
[44069.735883]  [] ? atomic_notifier_call_chain+0x22/0x30
[44069.735894]  [] ? notify_die+0x2d/0x30
[44069.735904]  [] ? do_debug+0x88/0x180
[44069.735915]  [] ? debug_stack_correct+0x30/0x38
[44069.735928]  [] ? testhrarr_startup+0x33/0x52 [testhrarr]
[44069.735940]  [] ? testhrarr_proc_show+0x32/0x57 [testhrarr]
[44069.735952]  [] ? seq_read+0x145/0x390
[44069.735963]  [] ? seq_read+0x0/0x390
[44069.735973]  [] ? proc_reg_read+0x64/0xa0
[44069.735985]  [] ? vfs_read+0x9f/0x160
[44069.735995]  [] ? proc_reg_read+0x0/0xa0
[44069.736003]  [] ? sys_read+0x42/0x70
[44069.736013]  [] ? syscall_call+0x7/0xb
[44069.736019] Dump stack from sample_hbp_handler
[44069.740132]  testhrarr_timer_function: testhrarr_runcount 0 
[44069.740146]  testhrarr jiffies 10942435 ; ret: 1 ; ktnsec: 44069740142485
[44069.740159] testhrarr_arr_first value is changed
[44069.740169] Pid: 4302, comm: gnome-terminal Not tainted 2.6.38-16-generic #67-Ubuntu
[44069.740176] Call Trace:
[44069.740195]  [] ? sample_hbp_handler+0x2d/0x3b [testhrarr]
[44069.740213]  [] ? __perf_event_overflow+0x90/0x240
[44069.740227]  [] ? perf_swevent_event+0x136/0x140
[44069.740239]  [] ? perf_bp_event+0x70/0x80
[44069.740253]  [] ? sched_clock_local+0xd3/0x1c0
[44069.740267]  [] ? format_decode+0x323/0x380
[44069.740280]  [] ? hw_breakpoint_handler.clone.0+0x102/0x130
[44069.740292]  [] ? hw_breakpoint_exceptions_notify+0x22/0x30
[44069.740302]  [] ? notifier_call_chain+0x45/0x60
[44069.740313]  [] ? atomic_notifier_call_chain+0x22/0x30
[44069.740324]  [] ? notify_die+0x2d/0x30
[44069.740335]  [] ? do_debug+0x88/0x180
[44069.740345]  [] ? debug_stack_correct+0x30/0x38
[44069.740364]  [] ? init_intel_cacheinfo+0x103/0x394
[44069.740379]  [] ? testhrarr_timer_function+0xed/0x160 [testhrarr]
[44069.740391]  [] ? __run_hrtimer+0x6f/0x190
[44069.740404]  [] ? testhrarr_timer_function+0x0/0x160 [testhrarr]
[44069.740416]  [] ? hrtimer_interrupt+0x108/0x240
[44069.740430]  [] ? smp_apic_timer_interrupt+0x56/0x8a
[44069.740441]  [] ? apic_timer_interrupt+0x31/0x38
[44069.740453]  [] ? _raw_spin_unlock_irqrestore+0x15/0x20
[44069.740465]  [] ? try_to_del_timer_sync+0x67/0xb0
[44069.740476]  [] ? del_timer_sync+0x29/0x50
[44069.740486]  [] ? flush_delayed_work+0x13/0x40
[44069.740500]  [] ? tty_flush_to_ldisc+0x12/0x20
[44069.740510]  [] ? n_tty_poll+0x4f/0x190
[44069.740523]  [] ? tty_poll+0x6d/0x90
[44069.740531]  [] ? n_tty_poll+0x0/0x190
[44069.740542]  [] ? do_poll.clone.3+0xd0/0x210
[44069.740553]  [] ? do_sys_poll+0x134/0x1e0
[44069.740563]  [] ? __pollwait+0x0/0xd0
[44069.740572]  [] ? pollwake+0x0/0x60
...
[44069.740742]  [] ? pollwake+0x0/0x60
[44069.740757]  [] ? rw_verify_area+0x6c/0x130
[44069.740770]  [] ? ktime_get_ts+0xf8/0x120
[44069.740781]  [] ? poll_select_set_timeout+0x64/0x70
[44069.740793]  [] ? sys_poll+0x5a/0xd0
[44069.740804]  [] ? syscall_call+0x7/0xb
[44069.740815]  [] ? init_intel_cacheinfo+0x23/0x394
[44069.740822] Dump stack from sample_hbp_handler
[44069.744130]  testhrarr_timer_function: testhrarr_runcount 1 
[44069.744143]  testhrarr jiffies 10942436 ; ret: 1 ; ktnsec: 44069744140055
[44069.748132]  testhrarr_timer_function: testhrarr_runcount 2 
[44069.748145]  testhrarr jiffies 10942437 ; ret: 1 ; ktnsec: 44069748141271
[44069.752131]  testhrarr_timer_function: testhrarr_runcount 3 
[44069.752145]  testhrarr jiffies 10942438 ; ret: 1 ; ktnsec: 44069752141164
[44069.756131]  testhrarr_timer_function: testhrarr_runcount 4 
[44069.756141]  testhrarr jiffies 10942439 ; ret: 1 ; ktnsec: 44069756138318
[44069.760130]  testhrarr_timer_function: testhrarr_runcount 5 
[44069.760141]  testhrarr jiffies 10942440 ; ret: 1 ; ktnsec: 44069760138469
[44069.760154] testhrarr_arr_first value is changed
[44069.760164] Pid: 4302, comm: gnome-terminal Not tainted 2.6.38-16-generic #67-Ubuntu
[44069.760170] Call Trace:
[44069.760187]  [] ? sample_hbp_handler+0x2d/0x3b [testhrarr]
[44069.760202]  [] ? __perf_event_overflow+0x90/0x240
[44069.760213]  [] ? perf_swevent_event+0x136/0x140
[44069.760224]  [] ? perf_bp_event+0x70/0x80
[44069.760235]  [] ? sched_clock_local+0xd3/0x1c0
[44069.760247]  [] ? format_decode+0x323/0x380
[44069.760258]  [] ? hw_breakpoint_handler.clone.0+0x102/0x130
[44069.760269]  [] ? hw_breakpoint_exceptions_notify+0x22/0x30
[44069.760279]  [] ? notifier_call_chain+0x45/0x60
[44069.760289]  [] ? atomic_notifier_call_chain+0x22/0x30
[44069.760299]  [] ? notify_die+0x2d/0x30
[44069.760308]  [] ? do_debug+0x88/0x180
[44069.760318]  [] ? debug_stack_correct+0x30/0x38
[44069.760334]  [] ? init_intel_cacheinfo+0x103/0x394
[44069.760345]  [] ? testhrarr_timer_function+0xed/0x160 [testhrarr]
[44069.760356]  [] ? __run_hrtimer+0x6f/0x190
[44069.760366]  [] ? send_to_group.clone.1+0xf8/0x150
[44069.760376]  [] ? testhrarr_timer_function+0x0/0x160 [testhrarr]
[44069.760387]  [] ? hrtimer_interrupt+0x108/0x240
[44069.760396]  [] ? fsnotify+0x1a5/0x290
[44069.760407]  [] ? smp_apic_timer_interrupt+0x56/0x8a
[44069.760416]  [] ? apic_timer_interrupt+0x31/0x38
[44069.760428]  [] ? mem_cgroup_resize_limit+0x108/0x1c0
[44069.760437]  [] ? fput+0x0/0x30
[44069.760446]  [] ? sys_write+0x67/0x70
[44069.760455]  [] ? syscall_call+0x7/0xb
[44069.760464]  [] ? init_intel_cacheinfo+0x23/0x394
[44069.760470] Dump stack from sample_hbp_handler
[44069.764134]  testhrarr_timer_function: testhrarr_runcount 6 
[44069.764147]  testhrarr jiffies 10942441 ; ret: 1 ; ktnsec: 44069764144141
[44069.768133]  testhrarr_timer_function: testhrarr_runcount 7 
[44069.768146]  testhrarr jiffies 10942442 ; ret: 1 ; ktnsec: 44069768142976
[44069.772134]  testhrarr_timer_function: testhrarr_runcount 8 
[44069.772148]  testhrarr jiffies 10942443 ; ret: 1 ; ktnsec: 44069772144121
[44069.776132]  testhrarr_timer_function: testhrarr_runcount 9 
[44069.776145]  testhrarr jiffies 10942444 ; ret: 1 ; ktnsec: 44069776141971
[44069.780133]  testhrarr_timer_function: testhrarr_runcount 10 
[44069.780141] testhrarr [ 5, 7, 9, 11, 13, ]  

...我们得到了三次堆栈跟踪 - 一次在testhrarr_startup,并两次testhrarr_timer_function: 一次runcount==0一次runcount==5,正如预期的那样。

好吧,希望这对某人有帮助,
Cheers!


Makefile

CONFIG_MODULE_FORCE_UNLOAD=y

# debug build:
# "CFLAGS was changed ... Fix it to use EXTRA_CFLAGS."
override EXTRA_CFLAGS+=-g -O0 

obj-m += testhrarr.o
#testhrarr-objs  := testhrarr.o

all:
    @echo EXTRA_CFLAGS = $(EXTRA_CFLAGS)
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

testhrarr.c

/*
 * [http://www.tldp.org/LDP/lkmpg/2.6/html/lkmpg.html#AEN189 The Linux Kernel Module Programming Guide]
 * https://stackoverflow.com/questions/16920238/reliability-of-linux-kernel-add-timer-at-resolution-of-one-jiffy/17055867#17055867
 * https://stackoverflow.com/questions/8516021/proc-create-example-for-kernel-module/18924359#18924359
 * http://lxr.free-electrons.com/source/samples/hw_breakpoint/data_breakpoint.c
 */


#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */
#include <linux/init.h>     /* Needed for the macros */
#include <linux/jiffies.h>
#include <linux/time.h>
#include <linux/proc_fs.h>  /* /proc entry */
#include <linux/seq_file.h> /* /proc entry */
#define ARRSIZE 5
#define MAXRUNS 2*ARRSIZE

#include <linux/hrtimer.h>

#define HWDEBUG_STACK 1

#if (HWDEBUG_STACK == 1)
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>

struct perf_event * __percpu *sample_hbp;
static char ksym_name[KSYM_NAME_LEN] = "testhrarr_arr";
module_param_string(ksym, ksym_name, KSYM_NAME_LEN, S_IRUGO);
MODULE_PARM_DESC(ksym, "Kernel symbol to monitor; this module will report any"
      " write operations on the kernel symbol");
#endif

static volatile int testhrarr_runcount = 0;
static volatile int testhrarr_isRunning = 0;

static unsigned long period_ms;
static unsigned long period_ns;
static ktime_t ktime_period_ns;
static struct hrtimer my_hrtimer;

static int* testhrarr_arr;
static int* testhrarr_arr_first;

static enum hrtimer_restart testhrarr_timer_function(struct hrtimer *timer)
{
  unsigned long tjnow;
  ktime_t kt_now;
  int ret_overrun;

  printk(KERN_INFO
    " %s: testhrarr_runcount %d \n",
    __func__, testhrarr_runcount);

  if (testhrarr_runcount < MAXRUNS) {
    tjnow = jiffies;
    kt_now = hrtimer_cb_get_time(&my_hrtimer);
    ret_overrun = hrtimer_forward(&my_hrtimer, kt_now, ktime_period_ns);
    printk(KERN_INFO
      " testhrarr jiffies %lu ; ret: %d ; ktnsec: %lld\n",
      tjnow, ret_overrun, ktime_to_ns(kt_now));
    testhrarr_arr[(testhrarr_runcount % ARRSIZE)] += testhrarr_runcount;
    testhrarr_runcount++;
    return HRTIMER_RESTART;
  }
  else {
    int i;
    testhrarr_isRunning = 0;
    // do not use KERN_DEBUG etc, if printk buffering until newline is desired!
    printk("testhrarr_arr [ ");
    for(i=0; i<ARRSIZE; i++) {
      printk("%d, ", testhrarr_arr[i]);
    }
    printk("]\n");
    return HRTIMER_NORESTART;
  }
}

static void testhrarr_startup(void)
{
  if (testhrarr_isRunning == 0) {
    testhrarr_isRunning = 1;
    testhrarr_runcount = 0;
    testhrarr_arr[0] = 0; //just the first element
    hrtimer_start(&my_hrtimer, ktime_period_ns, HRTIMER_MODE_REL);
  }
}


static int testhrarr_proc_show(struct seq_file *m, void *v) {
  if (testhrarr_isRunning == 0) {
    seq_printf(m, "testhrarr proc: startup\n");
    testhrarr_startup();
  } else {
    seq_printf(m, "testhrarr proc: (is running, %d)\n", testhrarr_runcount);
  }
  return 0;
}

static int testhrarr_proc_open(struct inode *inode, struct  file *file) {
  return single_open(file, testhrarr_proc_show, NULL);
}

static const struct file_operations testhrarr_proc_fops = {
  .owner = THIS_MODULE,
  .open = testhrarr_proc_open,
  .read = seq_read,
  .llseek = seq_lseek,
  .release = single_release,
};


#if (HWDEBUG_STACK == 1)
static void sample_hbp_handler(struct perf_event *bp,
             struct perf_sample_data *data,
             struct pt_regs *regs)
{
  printk(KERN_INFO "%s value is changed\n", ksym_name);
  dump_stack();
  printk(KERN_INFO "Dump stack from sample_hbp_handler\n");
}
#endif

static int __init testhrarr_init(void)
{
  struct timespec tp_hr_res;
  #if (HWDEBUG_STACK == 1)
  struct perf_event_attr attr;
  #endif

  period_ms = 1000/HZ;
  hrtimer_get_res(CLOCK_MONOTONIC, &tp_hr_res);
  printk(KERN_INFO
    "Init testhrarr: %d ; HZ: %d ; 1/HZ (ms): %ld ; hrres: %lld.%.9ld\n",
               testhrarr_runcount,      HZ,        period_ms, (long long)tp_hr_res.tv_sec, tp_hr_res.tv_nsec );

  testhrarr_arr = (int*)kcalloc(ARRSIZE, sizeof(int), GFP_ATOMIC);
  testhrarr_arr_first = &testhrarr_arr[0];

  hrtimer_init(&my_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
  my_hrtimer.function = &testhrarr_timer_function;
  period_ns = period_ms*( (unsigned long)1E6L );
  ktime_period_ns = ktime_set(0,period_ns);

  printk(KERN_INFO
    " Addresses: _runcount 0x%p ; _arr 0x%p ; _arr[0] 0x%p (0x%p) ; _timer_function 0x%p ; my_hrtimer 0x%p; my_hrt.f 0x%p\n",
    &testhrarr_runcount, &testhrarr_arr, &(testhrarr_arr[0]), testhrarr_arr_first, &testhrarr_timer_function, &my_hrtimer, &my_hrtimer.function);


  proc_create("testhrarr_proc", 0, NULL, &testhrarr_proc_fops);


  #if (HWDEBUG_STACK == 1)
  hw_breakpoint_init(&attr);
  if (strcmp(ksym_name, "testhrarr_arr_first") == 0) {
    // just for testhrarr_arr_first - interpret the found symbol address
    // as int*, and dereference it to get the "real" address it points to
    attr.bp_addr = *((int*)kallsyms_lookup_name(ksym_name));
  } else {
    // the usual - address is kallsyms_lookup_name result
    attr.bp_addr = kallsyms_lookup_name(ksym_name);
  }
  attr.bp_len = HW_BREAKPOINT_LEN_1;
  attr.bp_type = HW_BREAKPOINT_W ; //| HW_BREAKPOINT_R;

  sample_hbp = register_wide_hw_breakpoint(&attr, (perf_overflow_handler_t)sample_hbp_handler);
  if (IS_ERR((void __force *)sample_hbp)) {
    int ret = PTR_ERR((void __force *)sample_hbp);
    printk(KERN_INFO "Breakpoint registration failed\n");
    return ret;
  }

  // explicit cast needed to show 64-bit bp_addr as 32-bit address
  // https://stackoverflow.com/questions/11796909/how-to-resolve-cast-to-pointer-from-integer-of-different-size-warning-in-c-co/11797103#11797103
  printk(KERN_INFO "HW Breakpoint for %s write installed (0x%p)\n", ksym_name, (void*)(uintptr_t)attr.bp_addr);
  #endif

  return 0;
}

static void __exit testhrarr_exit(void)
{
  int ret_cancel = 0;
  kfree(testhrarr_arr);
  while( hrtimer_callback_running(&my_hrtimer) ) {
    ret_cancel++;
  }
  if (ret_cancel != 0) {
    printk(KERN_INFO " testhrarr Waited for hrtimer callback to finish (%d)\n", ret_cancel);
  }
  if (hrtimer_active(&my_hrtimer) != 0) {
    ret_cancel = hrtimer_cancel(&my_hrtimer);
    printk(KERN_INFO " testhrarr active hrtimer cancelled: %d (%d)\n", ret_cancel, testhrarr_runcount);
  }
  if (hrtimer_is_queued(&my_hrtimer) != 0) {
    ret_cancel = hrtimer_cancel(&my_hrtimer);
    printk(KERN_INFO " testhrarr queued hrtimer cancelled: %d (%d)\n", ret_cancel, testhrarr_runcount);
  }
  remove_proc_entry("testhrarr_proc", NULL);
  #if (HWDEBUG_STACK == 1)
  unregister_wide_hw_breakpoint(sample_hbp);
  printk(KERN_INFO "HW Breakpoint for %s write uninstalled\n", ksym_name);
  #endif
  printk(KERN_INFO "Exit testhrarr\n");
}

module_init(testhrarr_init);
module_exit(testhrarr_exit);

MODULE_LICENSE("GPL");
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

观察 Linux 内核中的变量(内存地址)变化,并在变化时打印堆栈跟踪? 的相关文章

  • 将 mmap 内核启动参数保留的内存映射到用户空间

    正如中所讨论的this https stackoverflow com q 1911473 143897问题 我在启动时使用内核启动参数保留一个内存块memmap 8G 64G 我写了一个字符驱动程序 http pete akeo ie 2
  • 是否从页面缓存中的脏页面进行文件读取?

    当字节写入文件时 内核不会立即将这些字节写入磁盘 而是将这些字节存储在页缓存中的脏页中 回写缓存 问题是 如果在脏页刷新到磁盘之前发出文件读取 则将从缓存中的脏页提供字节 还是首先将脏页刷新到磁盘 然后进行磁盘读取以提供字节 将它们存储在进
  • 具体什么是调试呢?

    什么是调试代码 我该如何进行 调试 http en wikipedia org wiki Debugging是确保您的代码不包含任何内容的过程bugs http en wikipedia org wiki Software bug 或至少尽
  • Intellij IDEA 断点停止在 JAR 中,而不是我的项目的源代码中

    我在 java 文件中设置了一个断点 我通过 maven jetty 插件启动了 jetty 我设置断点的java文件也打包成JAR了 Intellij 在断点处停止 但它显示了打包到 JAR 中的文件而不是 java 文件 它的行为就像我
  • QtCreator 调试暂停停在代码而不是汇编处

    如何配置 QtCreator 以便在调试并按下暂停时它会显示当前正在处理的代码 现在显示汇编 无法在任何地方找到有关此问题的答案 我使用的是 Windows 7 我在 Ubuntu 16 04 中使用 Qt Creator 4 2 2 时遇
  • VS2010 RC - 调试器中只有 100 个 std::map 元素

    当我想查看 std map 容器的所有元素时 在 VS 2010 RC 中调试我的应用程序时遇到一个小问题 当调试器到达断点并且我想检查元素检查器中的映射值 在 本地 窗口中以及用鼠标悬停变量名称后的弹出窗口中 并且我正在向下滚动列表元素它
  • Chrome 开发工具:无需切换到“源”选项卡即可进入调试器

    如果我把debugger https developer mozilla org en US docs JavaScript Reference Statements debugger当 Chrome 开发工具打开时 我的 JavaScri
  • GDB单步汇编并显示下一条将要执行的指令。 [复制]

    这个问题在这里已经有答案了 使用 gdb 调试器可以执行什么命令来单步执行并显示将要执行的下一条指令 我熟悉windbg这个操作非常简单 例如 我有以下函数 当我通过以下方式进入代码时si我想显示将要执行的下一条指令 而无需通过反汇编进行完
  • 同步 I/O 是否会使线程繁忙?

    假设我正在同步 I O 套接字上执行 I O 该套接字已准备好read or write手术 这意味着调用线程不会在操作上被阻塞 无论非阻塞 SOCK NONBLOCK 套接字的阻塞性质 但以下事情我不清楚 实际转移何时发生 当套接字标记为
  • 如何模拟ARM处理器运行环境并加载Linux内核模块?

    我尝试加载我的vmlinux into gdb并使用 ARM 内核模拟器 但我不明白为什么我会得到Undefined target command sim 这是外壳输出 arm eabi gdb vmlinux GNU gdb GDB 7
  • 两个程序对象运行时比较的方法

    我正在进行一种特定类型的代码测试 该测试相当麻烦并且可以自动化 但我不确定最佳实践 在描述问题之前 我想澄清一下 我正在寻找合适的术语和概念 以便我可以阅读有关如何实现它的更多信息 当然 欢迎就最佳实践提出建议 但我的目标很具体 这种方法叫
  • 检查应用程序是否从 Visual Studio 调试会话中启动

    我正在开发一个安装系统范围键盘的应用程序 钩 我不想在运行调试时安装这个钩子 从视觉工作室内部构建 否则它会挂起工作室 以及最终的系统 我可以通过检查是否 DEBUG 符号已定义 但是 当我调试时release应用程序的版本是 有一种方法可
  • UNIX系统调用监视器

    如何监控进程的系统调用 Check strace http linux die net man 1 strace 在最简单的情况下 strace 运行指定的命令直到退出 它拦截并记录进程调用的系统调用以及进程接收的信号 每个系统调用的名称
  • 当变量取特定值时如何使 PyCharm 中断?

    我有一本大字典 其中一些元素偶尔会出现非法值 我想弄清楚非法值从何而来 PyCharm 应该不断监视我的字典的值 一旦它们中的任何一个取了非法值 它就应该中断并让我检查程序的状态 我知道我可以通过为我的字典创建一个 getter sette
  • 无法在 Visual Studio 2010 (F5) 上启动调试器但是,“附加到进程”确实有效(速度慢)。怎么修?

    环境 全新 全新 安装 Windows 7 64 位 全新 新 安装 Visual Studio 2010 Professional 10 0 30319 1 Windows 更新已是最新的 问题 我无法在 Visual Studio 20
  • 设备树不匹配:.probe 从未被调用

    我无法理解设备树的工作原理 或者具体来说为什么该驱动程序无法初始化 这是在 android 版本 3 10 的 rockchip 供应商内核中 驱动程序 看门狗 rk29 wdt c 为了可读性而减少 static const struct
  • 接近语法错误(代码1)插入Android SQLite

    我正在创建一个通讯录应用程序 用户可以在其中输入姓名 电子邮件地址和号码 我希望将此数据保存在数据库中 但我似乎无法使插入方法起作用 我收到的错误是 android database sqlite SQLiteException near
  • 有谁知道在哪里定义硬件、版本和序列号。 /proc/cpuinfo 的字段?

    我想确保我的 proc cpuinfo 是准确的 目前它输出 Hardware am335xevm Revision 0000 Serial 0000000000000000 我可以在代码中的哪里更改它以给出实际值 这取决于 Linux 的
  • 在远程 Glassfish 上进行日志记录和/或调试

    我有个问题 我在远程 Glassfish 实例上部署了一个 Web 服务 现在我需要一些简单的日志或调试系统来纠正我的代码 使用简单的打印会更好 我的想法是使用 glassfish 日志文件 因为我可以从管理 GUI 访问它 但它不起作用
  • 是否可以在 Java 8 中调试 Lambda

    我刚刚开始使用 Java 8 Lambda 我注意到我无法在 NetBeans IDE 中调试它们 如果我尝试将断点附加到以下代码 我会得到一个变量断点 这绝对不是我想要的 private EventListener myListener

随机推荐

  • 我应该首先检查对象是否为 null 还是其次? [复制]

    这个问题在这里已经有答案了 举例来说 我创建了一只鸭子 Duck myDuck DuckFactory CreateDuck 过去我总是检查 myDuck 是否为空 if myDuck null Do stuff 我最近查看了一些首先检查
  • Javascript:在进入第三个函数之前测试两个异步调用?

    因此 我有两个异步调用发送到 Web 服务 我的第三种方法都需要它们 你们建议如何这样做 我尝试在我的一个方法中设置一个标志 另一个方法调用第三个方法并发送一些数据 所以在我的第三个方法中我尝试了这个 thirdMethod functio
  • Styled-Components:指定父母悬停时孩子的样式

    我有一个简单的组件 这是它的 2 个版本 带和不带样式组件 没有样式组件 div div div div container width 100px height 100px kid width 20px height 20px conta
  • Python:使用多个分割分隔符分割文件

    我有多个 CSV 文件 需要循环解析它们以收集信息 问题是 虽然它们的格式相同 但有些由 t 分隔 另一些则由 分隔 之后 我想删除字符串周围的双引号 python 可以通过多个可能的分隔符进行分割吗 目前 我可以使用以下方法将行拆分为一个
  • jQuery AJAX 没有错误或成功触发

    我有一个简单的联系表格 然后我就可以从中发布 我正在尝试使用 jQuery AJAX 数据 但由于某种原因 我没有收到处理程序的任何响应 这段代码正确吗 document on ready function contactButton on
  • UITableView titleForHeaderInSection 显示全部大写

    我正在使用 titleForHeaderInSection 显示 UITableView 部分的标题 它在 iOS6 SDK 中工作得很好 但 iOS7 SDK 显示的标题全部大写 我猜这是苹果更新的人机界面指南的一部分 那里的所有示例都显
  • Oracle Merge 删除子句其中数据在目标中但不在源中

    尝试从目标表中删除源表中不存在的记录 对象类型 CREATE OR REPLACE type dbo P REC AS OBJECT ATTR1 VARCHAR2 64 BYTE ATTR2 VARCHAR2 128 BYTE ATTR3
  • Tkinter - 如何将实例变量传递给另一个类?

    我用谷歌搜索了很多 但没有成功 上周发布了一个问题 但没有答案 因为似乎太长了 希望新问题更加清晰 这只是一小段代码 如果您运行它 您将能够重现该问题 我基本上需要做的是获取用户输入 来自 mainGUI 类的 E1 并将其传递给数据库类中
  • Firebase 数据库中“加载文档时出错”

    我在尝试访问时遇到错误Database内的菜单项Firebase 控制台 这是一个新的数据库 实际上有几条数据记录 但屏幕一直旋转很长时间 然后返回消息 加载文档时出错 同时我可以访问验证菜单项没有任何问题 Note 我使用的是配备 Cat
  • yii 执行自定义命令时出错

    当我运行自定义时 出现以下错误yiic终端中的命令 我似乎无法找出此错误的原因 任何人都可以指出我正确的方向 我知道数据库工作正常 应用程序的其他部分工作正常
  • 这段简单的代码的复杂性是多少?

    I m pasting this text from an ebook I have It says the complexity if O n2 and also gives an explanation for it but I fai
  • XSLT:没有属​​性且没有子元素的元素转换为父属性

    给定 xml 文件的结构 名称和值未知 对于每个具有简单结构 没有子节点 没有属性 但有文本且不为空 的非根元素 将其转换为父级的属性 我有 xml 文件
  • 房间和可用日期 PHP/MySQL

    因此 我正在设计一个适用于酒店的 Web 应用程序 它最终应该成为通过一些额外代码为每个酒店网站安装的简单方法 该应用程序是使用 php 和 mysql 因此 正如您可能猜到的那样 我对数据库结构非常困惑 不过 我做了一些研究 我不仅仅是要
  • Git clean 被忽略的文件夹

    我有一个文件夹foo包含在 gitignore 中 gitignore this contains foo foo bar 当我跑步时git clean f or git clean f x 这不会删除bar bar是一个普通文件 有没有办
  • boto3.Bucket.upload_file 是阻塞还是非阻塞?

    Is boto3 Bucket upload file http boto3 readthedocs io en latest reference services s3 html S3 Bucket upload file阻塞还是非阻塞
  • 插入多对多关系表

    简单场景 ClientTable ClientId ClientName Phone Age CityTable CityID CityName Country ClientCityTable ClientCityID ClientID C
  • 如何在 html 文本区域中显示以下文本?

    我有一个如下所示的字符串 请看一下 我把它作为图像 这里也不显示 如果我将该字符串放在这里 它就会变成以下内容 21154537878887GHE u0044 u0045 现在我的问题是 有没有办法将原始字符串放入 HTML 中
  • 有什么方法可以从 lme4 mer 模型拟合对象生成 LaTeX 表吗?

    有谁知道如何从 lme4 生成一个好的出版质量的 LaTeX 表mer目的 既没有xtable方法 包xtable 也不latex方法 包Hmisc 知道如何处理mer对象 例如 考虑到这种拟合 library lme4 fm1 lt lm
  • 如何更改 Rails 应用程序数据?

    我看过很多关于 ActiveRecord 迁移以及是否应该使用它们来更改应用程序中的数据的讨论 有些人说是 有些人说不 我的问题是 如果您不使用迁移来执行此操作 那么您使用什么 只是你写的另一个脚本 我正在寻求有关替代方法的建议 以及为什么
  • 观察 Linux 内核中的变量(内存地址)变化,并在变化时打印堆栈跟踪?

    我想以某种方式 监视 Linux 内核 确切地说是内核模块 驱动程序 中的变量 或者内存地址 并找出改变它的原因 基本上 当变量改变时打印出堆栈跟踪 例如 在内核模块中testjiffy hr c列于末尾这个答案 https stackov