成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

內核調測工具Kprobe之實踐篇

系統
debug內核函數變量的時候最常用的是添加log,用printk看下相關的信息,但是這種方式往往需要重新編譯內核,然后再啟動設備。

[[381473]]

本文轉載自微信公眾號「人人都是極客」,作者布道師Peter。轉載本文請聯系人人都是極客公眾號。   

 Kprobe介紹

debug內核函數變量的時候最常用的是添加log,用printk看下相關的信息,但是這種方式往往需要重新編譯內核,然后再啟動設備。

而Kprobe可以在運行的內核中動態插入探測點,執行你預定義的操作。可以跟蹤內核幾乎所有的代碼地址,并且當斷點被擊中后會響應處理函數。

使用kprobe最常用的就是查詢函數調用的參數和返回值。

目前,使用kprobe可以通過兩種方式:

  • 第一種是開發人員自行編寫內核模塊,向內核注冊探測點,探測函數可根據需要自行定制,使用靈活方便;
  • 第二種方式是使用kprobes on trace,這種方式是kprobe和Ftrace結合使用,即可以通過kprobe來優化Ftrace來跟蹤函數的調用。

編寫kprobe探測模塊

Kprobe結構體與API介紹

  1. struct hlist_node hlist:被用于kprobe全局hash,索引值為被探測點的地址; 
  2. struct list_head list:用于鏈接同一被探測點的不同探測kprobe; 
  3. kprobe_opcode_t *addr:被探測點的地址; 
  4. const char *symbol_name:被探測函數的名字; 
  5. unsigned int offset:被探測點在函數內部的偏移,用于探測函數內部的指令,如果該值為0表示函數的入口; 
  6. kprobe_pre_handler_t pre_handler:在被探測點指令執行之前調用的回調函數; 
  7. kprobe_post_handler_t post_handler:在被探測指令執行之后調用的回調函數; 
  8. kprobe_fault_handler_t fault_handler:在執行pre_handler、post_handler或單步執行被探測指令時出現內存異常則會調用該回調函數; 
  9. kprobe_break_handler_t break_handler:在執行某一kprobe過程中觸發了斷點指令后會調用該函數,用于實現jprobe; 
  10. kprobe_opcode_t opcode:保存的被探測點原始指令; 
  11. struct arch_specific_insn ainsn:被復制的被探測點的原始指令,用于單步執行,架構強相關(可能包含指令模擬函數); 
  12. u32 flags:狀態標記。 
  1. int register_kprobe(struct kprobe *kp)      //向內核注冊kprobe探測點 
  2. void unregister_kprobe(struct kprobe *kp)   //卸載kprobe探測點 
  3. int register_kprobes(struct kprobe **kps, int num)     //注冊探測函數向量,包含多個探測點 
  4. void unregister_kprobes(struct kprobe **kps, int num)  //卸載探測函數向量,包含多個探測點 
  5. int disable_kprobe(struct kprobe *kp)       //臨時暫停指定探測點的探測 
  6. int enable_kprobe(struct kprobe *kp)        //恢復指定探測點的探測 

用例kprobe_example.c分析與演示

linux內核源碼中提供了kprobe的用例 samples/kprobes/kprobe_example.c

  1. /* For each probe you need to allocate a kprobe structure */ 
  2. static struct kprobe kp = { 
  3.  .symbol_name = "do_fork"
  4. }; 
  5.   
  6. static int __init kprobe_init(void) 
  7.  int ret; 
  8.  kp.pre_handler = handler_pre; 
  9.  kp.post_handler = handler_post; 
  10.  kp.fault_handler = handler_fault; 
  11.   
  12.  ret = register_kprobe(&kp); 
  13.  if (ret < 0) { 
  14.   printk(KERN_INFO "register_kprobe failed, returned %d\n", ret); 
  15.   return ret; 
  16.  } 
  17.  printk(KERN_INFO "Planted kprobe at %p\n", kp.addr); 
  18.  return 0; 
  19.   
  20. static void __exit kprobe_exit(void) 
  21.  unregister_kprobe(&kp); 
  22.  printk(KERN_INFO "kprobe at %p unregistered\n", kp.addr); 
  23.   
  24. module_init(kprobe_init) 
  25. module_exit(kprobe_exit) 
  26. MODULE_LICENSE("GPL"); 

程序中定義了一個struct kprobe結構實例kp并初始化其中的symbol_name字段為“do_fork”,表明它將要探測do_fork函數。在模塊的初始化函數中,注冊了 pre_handler、post_handler和fault_handler這3個回調函數分別為handler_pre、handler_post和handler_fault,最后調用register_kprobe注冊。在模塊的卸載函數中調用unregister_kprobe函數卸載kp探測點。

  1. static int handler_pre(struct kprobe *p, struct pt_regs *regs) 
  2. ...... 
  3. #ifdef CONFIG_ARM64 
  4.  pr_info("<%s> pre_handler: p->addr = 0x%p, pc = 0x%lx," 
  5.    " pstate = 0x%lx\n"
  6.   p->symbol_name, p->addr, (long)regs->pc, (long)regs->pstate); 
  7. #endif 
  8.  
  9.  /* A dump_stack() here will give a stack backtrace */ 
  10.  return 0; 

handler_pre回調函數的第一個入參是注冊的struct kprobe探測實例,第二個參數是保存的觸發斷點前的寄存器狀態,它在do_fork函數被調用之前被調用,該函數僅僅是打印了被探測點的地址,保存的個別寄存器參數。

  1. static void handler_post(struct kprobe *p, struct pt_regs *regs, 
  2.     unsigned long flags) 
  3. ...... 
  4. #ifdef CONFIG_ARM64 
  5.  pr_info("<%s> post_handler: p->addr = 0x%p, pstate = 0x%lx\n"
  6.   p->symbol_name, p->addr, (long)regs->pstate); 
  7. #endif 

handler_post回調函數的前兩個入參同handler_pre,第三個參數目前尚未使用,全部為0;該函數在do_fork函數調用之后被調用,這里打印的內容同handler_pre類似。

  1. static int handler_fault(struct kprobe *p, struct pt_regs *regs, int trapnr) 
  2.  pr_info("fault_handler: p->addr = 0x%p, trap #%dn", p->addr, trapnr); 
  3.  /* Return 0 because we don't handle the fault. */ 
  4.  return 0; 

handler_fault回調函數會在執行handler_pre、handler_post或單步執行do_fork時出現錯誤時調用,這里第三個參數時具體發生錯誤的trap number,與架構相關。

加載到內核中后,隨便在終端上敲一個命令,可以看到dmesg中打印如下信息:

  1. <6>pre_handler: p->addr = 0xc0439cc0, ip = c0439cc1, flags = 0x246 
  2. <6>post_handler: p->addr = 0xc0439cc0, flags = 0x246 
  3. <6>pre_handler: p->addr = 0xc0439cc0, ip = c0439cc1, flags = 0x246 
  4. <6>post_handler: p->addr = 0xc0439cc0, flags = 0x246 
  5. <6>pre_handler: p->addr = 0xc0439cc0, ip = c0439cc1, flags = 0x246 
  6. <6>post_handler: p->addr = 0xc0439cc0, flags = 0x246 

可以看到被探測點的地址為0xc0439cc0,用以下命令確定這個地址就是do_fork的入口地址。

  1. echo 0 > /proc/sys/kernel/kptr_restrict 
  2. cat /proc/kallsyms | grep do_fork 
  3. c0439cc0 T do_fork 

kprobes on trace

  • /sys/kernel/debug/kprobes/list: 列出內核中已經設置kprobe斷點的函數
  • /sys/kernel/debug/kprobes/enabled: kprobe開啟/關閉開關
  • /sys/kernel/debug/kprobes/blacklist: kprobe黑名單(無法設置斷點函數)
  • /proc/sys/debug/kprobes-optimization: Turn kprobes optimization ON/OFF

Documentation/trace/kprobetrace.txt

使用前確定內核CONFIG打開:CONFIG_KPROBE_EVENT=y

/sys/kernel/debug/tracing/kprobe_events:添加斷點接口 

/sys/kernel/debug/tracing/events/kprobes/enabled:斷點使能開關 

/sys/kernel/debug/tracing/trace:查看trace日志接口

規則:

  1. Synopsis of kprobe_events------------------------- 
  2.   p[:[GRP/]EVENT] [MOD:]SYM[+offs]|MEMADDR [FETCHARGS]  : Set a probe 
  3.   r[:[GRP/]EVENT] [MOD:]SYM[+0] [FETCHARGS]             : Set a return probe 
  4.   -:[GRP/]EVENT                                         : Clear a probe 
  5.  
  6.  GRP            : Group name. If omitted, use "kprobes" for it. 
  7.  EVENT          : Event name. If omitted, the event name is generated 
  8.                   based on SYM+offs or MEMADDR. 
  9.  MOD            : Module name which has given SYM. 
  10.  SYM[+offs]     : Symbol+offset where the probe is inserted. 
  11.  MEMADDR        : Address where the probe is inserted. 
  12.  
  13.  FETCHARGS      : Arguments. Each probe can have up to 128 args. 
  14.   %REG          : Fetch register REG 
  15.   @ADDR         : Fetch memory at ADDR (ADDR should be in kernel) 
  16.   @SYM[+|-offs] : Fetch memory at SYM +|- offs (SYM should be a data symbol) 
  17.   $stackN       : Fetch Nth entry of stack (N >= 0) 
  18.   $stack        : Fetch stack address. 
  19.   $retval       : Fetch return value.(*) 
  20.   $comm         : Fetch current task comm. 
  21.   +|-offs(FETCHARG) : Fetch memory at FETCHARG +|- offs address.(**) 
  22.   NAME=FETCHARG : Set NAME as the argument name of FETCHARG. 
  23.   FETCHARG:TYPE : Set TYPE as the type of FETCHARG. Currently, basic types 
  24.                   (u8/u16/u32/u64/s8/s16/s32/s64), hexadecimal types 
  25.                   (x8/x16/x32/x64), "string" and bitfield are supported. 
  26.  
  27.   (*) only for return probe. 
  28.   (**) this is useful for fetching a field of data structures. 

查看對應的模塊:

  1. 130|mek_8q:/sys/kernel/debug/tracing # cat /proc/devices 
  2. Character devices: 
  3.   1 mem 
  4.   4 /dev/vc/0 
  5.   4 tty 
  6.   4 ttyS 
  7.   5 /dev/tty 
  8.   5 /dev/console 
  9.   5 /dev/ptmx 
  10.   7 vcs 
  11.  10 misc 
  12.  13 input 
  13.  29 fb 
  14.  81 video4linux 
  15.  89 i2c 
  16.  90 mtd 
  17. 108 ppp 
  18. 116 alsa 

可以在System.map文件里找一下有沒有你要觀察的內核函數方法。這個文件其實相當于內核的符號表(symbol table)。如果拿不準內核方法名的時候可以在這里面grep一下看看。

  1. mek_8q:/ # cat /proc/kallsyms | grep do_sys_open 
  2. 0000000000000000 T do_sys_open 

以do_sys_open為例添加kprobe為例:

  1. 添加kprobe: 
  2. echo 'p:myprobe do_sys_open' > /sys/kernel/debug/tracing/kprobe_events 
  3. 添加kretprobe,返回值是數字: 
  4. echo 'r:myretprobe do_sys_open $retval' > /sys/kernel/debug/tracing/kprobe_events 
  5. 添加kretprobe,返回值是字符串: 
  6. echo 'r:myprobe getname +0($retval):string' > /sys/kernel/debug/tracing/kprobe_events 
  7. 刪除添加的kprobe: 
  8. echo '-:myprobe' > /sys/kernel/debug/tracing/events/kprobe_events 

執行:

  1. cd /sys/kernel/debug/tracing 
  2. echo 'p:myprobe do_sys_open' > kprobe_events 
  3. echo 'r:myretprobe do_sys_open $retval' > kprobe_events 
  4. echo 1 > tracing_on 
  5. echo 1 > events/kprobes/myprobe/enable 

結果為:

刪除注冊的kprobe:

  1. echo 0 > /sys/kernel/debug/tracing/events/kprobes/myprobe/enable 
  2. echo 0 > /sys/kernel/debug/tracing/events/kprobes/myretprobe/enable 
  3. echo '-:myprobe' > /sys/kernel/debug/tracing/events/kprobe_events 
  4. echo '-:myretprobe' > /sys/kernel/debug/tracing/events/kprobe_events 

 

責任編輯:武曉燕 來源: 人人都是極客
相關推薦

2021-02-20 20:51:24

工具內核kprobe

2022-03-21 09:40:48

TektonJenkinsPipeline

2022-04-08 09:53:56

TektonJenkinsKubesphere

2010-07-29 09:57:34

SCVMM虛擬機

2009-12-07 14:53:07

RHEL網絡安裝

2022-03-10 13:57:23

TektonJenkinsPipeline

2022-04-14 07:51:39

TektonTaskRun

2010-07-20 08:49:00

Objective C

2017-10-17 14:02:30

jvm調優工具

2019-06-12 10:15:19

運維開發Linux

2023-06-27 15:02:47

2011-03-18 11:21:48

2021-03-04 08:39:21

SparkRDD調優

2021-11-15 04:00:07

Linux 內核動態

2023-11-29 20:19:35

實踐云計算

2021-11-07 23:49:19

SQL數據庫工具

2009-08-07 10:28:03

2025-05-27 08:20:00

Linux內核參數調優系統

2011-07-27 14:10:43

javascript

2010-11-30 11:26:49

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 伦理一区二区 | 污免费网站 | 在线视频 中文字幕 | 亚洲区一区二 | 亚洲一区二区三区视频 | 国产精品视频中文字幕 | 亚洲精品9999 | 美女久久| 成人av电影天堂 | 婷婷五月色综合香五月 | 精品国产色 | 国产精品区二区三区日本 | 黄视频网站在线 | 日韩欧美国产成人一区二区 | 天天插天天狠天天透 | www.久| 日本不卡高清视频 | 日韩伦理电影免费在线观看 | 奇米超碰 | 91精品国产综合久久久久久 | 超碰地址 | 天天操天天干天天爽 | 麻豆成人在线视频 | 日日摸日日碰夜夜爽亚洲精品蜜乳 | 成人在线免费 | 91久久精品一区二区三区 | 亚洲精品视频久久 | 波多野结衣一区二区 | 亚洲精品一区中文字幕乱码 | 欧美精品成人 | 岛国视频 | 视频在线h| 午夜影晥| 国产一区二区三区色淫影院 | 在线国产视频 | 日韩在线h | 一区二区免费视频 | 成人国产精品色哟哟 | 国产资源在线播放 | 精品国产乱码久久久久久88av | 欧美极品在线播放 |