how to get goroutine id with ebpf

huangapple go评论118阅读模式
英文:

how to get goroutine id with ebpf

问题

我使用cilium ebpf包来编写一个用于获取goroutine id的ebpf程序,但是失败了。我的uprobe.c文件如下所示:

  1. SEC("uprobe/runtime.newproc1")
  2. int uprobe_runtime_newproc1(struct pt_regs *ctx) {
  3. u32 key = 2;
  4. u64 initval = 1, *valp;
  5. valp = bpf_map_lookup_elem(&uprobe_map, &key);
  6. if (!valp) {
  7. bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
  8. return 0;
  9. }
  10. __sync_fetch_and_add(valp, 1);
  11. struct g* goroutine_struct = (void *)PT_REGS_PARM4(ctx);
  12. // retrieve output parameter
  13. s64 goid = 0;
  14. bpf_probe_read(&goid, sizeof(goid), &goroutine_struct->goid);
  15. bpf_printk("bpf_printk bpf_probe_read goroutine_struct->goid: %lld", goid);
  16. struct g gs;
  17. bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM4(ctx));
  18. bpf_printk("bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);
  19. // test
  20. void* ptr = (void *)PT_REGS_PARM4(ctx);
  21. s64 goid2 = 0;
  22. bpf_probe_read(&goid2, sizeof(goid2), (void *)(ptr+152));
  23. bpf_printk("bpf_printk bpf_probe_read goid2: %lld", goid2);
  24. return 0;
  25. }

goroutine.h文件如下所示:

  1. #include "common.h"
  2. struct stack {
  3. u64 lo;
  4. u64 hi;
  5. };
  6. struct gobuf {
  7. u64 sp;
  8. u64 pc;
  9. u64 g;
  10. u64 ctxt;
  11. u64 ret;
  12. u64 lr;
  13. u64 bp;
  14. };
  15. struct g {
  16. struct stack stack;
  17. u64 stackguard0;
  18. u64 stackguard1;
  19. u64 _panic;
  20. u64 _defer;
  21. u64 m;
  22. struct gobuf sched ;
  23. u64 syscallsp;
  24. u64 syscallpc;
  25. u64 stktopsp;
  26. u64 param;
  27. u32 atomicstatus;
  28. u32 stackLock;
  29. s64 goid; // 这里就是goroutine id!
  30. };

当我运行程序时,cat /sys/kernel/debug/tracing/trace_pipe输出如下,得到了错误的goroutine id:

  1. <...>-1336127 [000] d... 20113210.986990: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct->goid: 4938558469562467144
  2. <...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct.goid: 4938558469562467144
  3. <...>-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goid2: 4938558469562467144

根据你的描述,你认为问题的关键在于将golang结构体g转换为goroutine.h时出错。你是否需要帮助?

英文:

I use cilium ebpf pakage to write a ebpf program for getting the goroutine id.
but failed. my uprobe.c like this :
I think the key problem is that golang struct g trans to goroutine.h is wrong. can anyone help?

uprobe.c

  1. SEC(&quot;uprobe/runtime.newproc1&quot;)
  2. int uprobe_runtime_newproc1(struct pt_regs *ctx) {
  3. u32 key = 2;
  4. u64 initval = 1, *valp;
  5. valp = bpf_map_lookup_elem(&amp;uprobe_map, &amp;key);
  6. if (!valp) {
  7. bpf_map_update_elem(&amp;uprobe_map, &amp;key, &amp;initval, BPF_ANY);
  8. return 0;
  9. }
  10. __sync_fetch_and_add(valp, 1);
  11. struct g* goroutine_struct = (void *)PT_REGS_PARM4(ctx);
  12. // retrieve output parameter
  13. s64 goid = 0;
  14. bpf_probe_read(&amp;goid, sizeof(goid), &amp;goroutine_struct-&gt;goid);
  15. bpf_printk(&quot;bpf_printk bpf_probe_read goroutine_struct-&gt;goid: %lld&quot;, goid);
  16. struct g gs;
  17. bpf_probe_read(&amp;gs, sizeof(gs), (void *)PT_REGS_PARM4(ctx));
  18. bpf_printk(&quot;bpf_printk bpf_probe_read goroutine_struct.goid: %lld&quot;, gs.goid);
  19. // test
  20. void* ptr = (void *)PT_REGS_PARM4(ctx);
  21. s64 goid2 = 0;
  22. bpf_probe_read(&amp;goid2, sizeof(goid2), (void *)(ptr+152));
  23. bpf_printk(&quot;bpf_printk bpf_probe_read goid2: %lld&quot;, goid2);
  24. return 0;
  25. }

goroutine.h

  1. #include &quot;common.h&quot;
  2. struct stack {
  3. u64 lo;
  4. u64 hi;
  5. };
  6. struct gobuf {
  7. u64 sp;
  8. u64 pc;
  9. u64 g;
  10. u64 ctxt;
  11. u64 ret;
  12. u64 lr;
  13. u64 bp;
  14. };
  15. /*
  16. go version go1.17.2 linux/amd64
  17. type stack struct {
  18. lo uintptr
  19. hi uintptr
  20. }
  21. type gobuf struct {
  22. sp uintptr
  23. pc uintptr
  24. g uintptr
  25. ctxt uintptr
  26. ret uintptr
  27. lr uintptr
  28. bp uintptr
  29. }
  30. type g struct {
  31. stack stack // offset known to runtime/cgo
  32. stackguard0 uintptr // offset known to liblink
  33. stackguard1 uintptr // offset known to liblink
  34. _panic *_panic // innermost panic - offset known to liblink
  35. _defer *_defer // innermost defer
  36. m *m // current m; offset known to arm liblink
  37. sched gobuf
  38. syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
  39. syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
  40. stktopsp uintptr // expected sp at top of stack, to check in traceback
  41. param unsafe.Pointer
  42. atomicstatus uint32
  43. stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
  44. goid int64
  45. }
  46. */
  47. struct g {
  48. struct stack stack;
  49. u64 stackguard0;
  50. u64 stackguard1;
  51. u64 _panic;
  52. u64 _defer;
  53. u64 m;
  54. struct gobuf sched ;
  55. u64 syscallsp;
  56. u64 syscallpc;
  57. u64 stktopsp;
  58. u64 param;
  59. u32 atomicstatus;
  60. u32 stackLock;
  61. s64 goid; // Here it is!
  62. };

When I run my program , cat /sys/kernel/debug/tracing/trace_pipe output like this , get the wrong go id:

  1. &lt;...&gt;-1336127 [000] d... 20113210.986990: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct-&gt;goid: 4938558469562467144
  2. &lt;...&gt;-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goroutine_struct.goid: 4938558469562467144
  3. &lt;...&gt;-1336127 [000] d... 20113210.986998: bpf_trace_printk: bpf_printk bpf_probe_read goid2: 4938558469562467144
  4. Blockquote

答案1

得分: 1

我找到了一个解决方案:

  1. 我的 Golang 版本是 1.17.2,amd64 架构。amd64 架构使用以下 9 个寄存器来传递整数参数和结果:RAX、RBX、RCX、RDI、RSI、R8、R9、R10、R11。

  2. 在 Go 1.17.2 中,runtime.newproc1 函数有 5 个参数。callergp *g 是第 4 个参数。当我使用 gdb 调试我的用户空间程序时,它使用 rdi 寄存器来保存 callergp *g 的指针地址。所以使用 PT_REGS_PARM1 是正确的方式,因为 (#define PT_REGS_PARM1(x) ((x)->rdi))。

  3. 最后,代码如下:

  1. SEC("uprobe/runtime.newproc1")
  2. int uprobe_runtime_newproc1(struct pt_regs *ctx) {
  3. u32 key = 2;
  4. u64 initval = 1, *valp;
  5. valp = bpf_map_lookup_elem(&uprobe_map, &key);
  6. if (!valp) {
  7. bpf_map_update_elem(&uprobe_map, &key, &initval, BPF_ANY);
  8. return 0;
  9. }
  10. __sync_fetch_and_add(valp, 1);
  11. // retrieve output parameter
  12. struct g gs;
  13. bpf_probe_read(&gs, sizeof(gs), (void *)PT_REGS_PARM1(ctx));
  14. bpf_printk("uprobe_runtime_newproc1 bpf_printk bpf_probe_read goroutine_struct.goid: %lld", gs.goid);
  15. return 0;
  16. }
英文:

I found a solution:

  1. my golang version is 1.17.2, amd64. and the amd64 architecture uses the following sequence of 9 registers for integer arguments and results:
    RAX, RBX, RCX, RDI, RSI, R8, R9, R10, R11

  2. runtime.newproc1 which in go 1.17.2 has 5 args. callergp *g is the 4th. when I gdb my userspace program, it use rdi register to save ptr addr of callergp *g.
    so use PT_REGS_PARM1 is the right way. because (#define PT_REGS_PARM1(x) ((x)->rdi))

  3. after all, the code like this :

  1. SEC(&quot;uprobe/runtime.newproc1&quot;)
  2. int uprobe_runtime_newproc1(struct pt_regs *ctx) {
  3. u32 key = 2;
  4. u64 initval = 1, *valp;
  5. valp = bpf_map_lookup_elem(&amp;uprobe_map, &amp;key);
  6. if (!valp) {
  7. bpf_map_update_elem(&amp;uprobe_map, &amp;key, &amp;initval, BPF_ANY);
  8. return 0;
  9. }
  10. __sync_fetch_and_add(valp, 1);
  11. // retrieve output parameter
  12. struct g gs;
  13. bpf_probe_read(&amp;gs, sizeof(gs), (void *)PT_REGS_PARM1(ctx));
  14. bpf_printk(&quot;uprobe_runtime_newproc1 bpf_printk bpf_probe_read goroutine_struct.goid: %lld&quot;, gs.goid);
  15. return 0;
  16. }

huangapple
  • 本文由 发表于 2022年11月10日 11:27:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/74383816.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定