Преглед изворни кода

Fixed #TASK_GK-2944 支持 Dotnet Aot

roger.wang пре 1 година
родитељ
комит
ce793e39f4
3 измењених фајлова са 137 додато и 11 уклоњено
  1. 89 2
      ebpftracer/ebpf/utrace/go/net/stack.probe.bpf.c
  2. 47 9
      ebpftracer/stack.go
  3. 1 0
      ebpftracer/tracer/uprobe.go

+ 89 - 2
ebpftracer/ebpf/utrace/go/net/stack.probe.bpf.c

@@ -105,7 +105,7 @@ int ent(struct pt_regs *ctx)
 
 	cw_bpf_debug("[Go] [uprobe/ent]: goid: %llu", e->goid);
 
-	cw_bpf_debug("[Go] [uprobe/ent]: event: location:%x,ip:%x,time_ns_start:%lld\n", e->location, e->ip, e->time_ns_start);
+	cw_bpf_debug("[Go] [uprobe/ent]: event: location:%x,ip:%lx,time_ns_start:%lld\n", e->location, e->ip, e->time_ns_start);
 	cw_bpf_debug("[Go] [uprobe/ent]: event: bp:%x,caller_bp:%x,caller_ip:%x\n", e->bp,e->caller_bp,e->caller_ip);
 
 	struct trace_stack_entry_key_t trace_key = {};
@@ -173,7 +173,7 @@ int ret(struct pt_regs *ctx)
 	e->bp = PT_REGS_SP(ctx) - 8;
 
 	__u64 caller_bp = PT_REGS_FP(ctx);
-	cw_bpf_debug("[Go] [uprobe/ret]: e->ip:%x,bp:%x,caller_bp:%x", e->ip, e->bp, caller_bp);
+	cw_bpf_debug("[Go] [uprobe/ret]: e->ip:%lx,bp:%x,caller_bp:%x", e->ip, e->bp, caller_bp);
 	cw_bpf_debug("[Go] [uprobe/ret]: sp:%x, goid: %llu, goid:0x:%x", PT_REGS_SP(ctx), e->goid, e->goid);
 
 	cw_bpf_debug("[Go] [uprobe/ret]: event: location:%x,ip:%x,time_ns_end:%lld\n", e->location, e->ip, e->time_ns_end);
@@ -210,3 +210,90 @@ int ret(struct pt_regs *ctx)
 	cw_bpf_debug("[Go] [uprobe/ret] end");
 	return 1;
 }
+
+
+
+SEC("uprobe/dotnetent")
+int dotnetent(struct pt_regs *ctx)
+{
+    cw_bpf_debug("[Go] [uprobe/ent] enter");
+	__u64 pid_tgid = bpf_get_current_pid_tgid();
+    __u64 pid = pid_tgid >> 32;
+
+	__u64 trace_id = get_apm_trace_id(pid, pid_tgid);
+
+	__u32 key = 0;
+	struct event *e = bpf_map_lookup_elem(&event_stack, &key);
+	if (!e)
+	{
+		cw_bpf_debug("[Go] [uprobe/ret]: conot get event");
+		return 0;
+	}
+	__builtin_memset(e, 0, sizeof(*e));
+
+	// e->goid = get_goid();
+	e->goid = get_current_goroutine();
+
+	// e->ip = ctx->ip;
+	e->ip = PT_REGS_IP(ctx); // 当前函数的地址
+	if (!bpf_map_lookup_elem(&should_trace_goid, &e->goid))
+	{
+		__u64 should_trace = true;
+		bpf_map_update_elem(&should_trace_goid, &e->goid, &should_trace, BPF_ANY);
+	}
+	cw_bpf_debug("[Go] [uprobe/ent]: e->goid: %llu", e->goid);
+	cw_bpf_debug("[Go] [uprobe/ent]: e->ip: %d", e->ip);
+	cw_bpf_debug("[Go] [uprobe/ent]: yes");
+
+	__u64 asm_;
+	bpf_probe_read_user(&asm_, sizeof(asm_), (void*)e->ip);
+
+	asm_ = asm_ >> 32;
+	cw_bpf_debug("[Go] [uprobe/ent]: e->asm: %x", asm_);
+
+	e->pid = pid;
+	e->trace_id = trace_id;
+	e->location = ENTPOINT; // 标识入口还是出口
+	e->time_ns_start = bpf_ktime_get_ns();
+	e->bp = PT_REGS_SP(ctx) + asm_; // 因为 ebpf 程序执行时实际上还没进行堆栈的切换,所以此处是需要在父函数中获取子函数的基地址,新的堆栈会将父函数的 rbp 入栈,一个 rbp 在 64位机器上占用 8 个字节,所以需要减 8,获取到子函数的基地址,低地址的需要减8,如果到高地址的cpu 上就得+8
+	e->caller_bp = PT_REGS_FP(ctx); // 父函数的基地址
+
+	void *ra;
+	ra = (void *)PT_REGS_SP(ctx); // 父函数的 sp,此时 sp 指向父函数的最后一个地址
+	bpf_probe_read_user(&e->caller_ip, sizeof(e->caller_ip), ra);
+
+	cw_bpf_debug("[Go] [uprobe/ent]: goid: %llu", e->goid);
+
+	cw_bpf_debug("[Go] [uprobe/ent]: event: location:%x,ip:%lx,time_ns_start:%lld\n", e->location, e->ip, e->time_ns_start);
+	cw_bpf_debug("[Go] [uprobe/ent]: event: bp:%x,caller_bp:%x,caller_ip:%x\n", e->bp,e->caller_bp,e->caller_ip);
+
+	struct trace_stack_entry_key_t trace_key = {};
+	trace_key.caller_bp = e->caller_bp % 0x800 + (e->goid << 12);
+	trace_key.bp = e->bp % 0x800 + (e->goid << 12);
+
+	// trace_key.caller_bp = e->caller_bp;
+	// trace_key.bp = e->bp;
+	// cw_bpf_debug("[Go] [uprobe/ent]: trace_keytrace_keytrace_key: bp: %x", trace_key.bp);
+	cw_bpf_debug("[Go] [uprobe/ent]: trace_keytrace_keytrace_key: caller_bp: %x, bp: %x", trace_key.caller_bp, trace_key.bp);
+
+	struct event event_current = {};
+	event_current.bp = e->bp,
+	event_current.caller_bp = e->caller_bp,
+	event_current.caller_ip = e->caller_ip,
+	event_current.pid = e->pid,
+	event_current.trace_id = e->trace_id,
+	event_current.goid = e->goid,
+	event_current.ip = e->ip,
+	event_current.time_ns_start = e->time_ns_start,
+	event_current.location = e->location,
+	event_current.nid = ((e->ip % 0x1000) << 24) + e->bp % 0x800 + (e->goid << 12),
+	event_current.fpid = e->caller_bp % 0x800 + (e->goid << 12),
+
+	bpf_map_update_elem(&trace_stack_entry, &trace_key, &event_current, BPF_ANY);
+
+	// return bpf_map_push_elem(&event_queue, e, BPF_EXIST);
+	// bpf_perf_event_output(ctx, &event_queue, BPF_F_CURRENT_CPU, e, sizeof(*e));
+
+	cw_bpf_debug("[Go] [uprobe/ent] end");
+	return 1;
+}

+ 47 - 9
ebpftracer/stack.go

@@ -17,6 +17,8 @@ import (
 	"github.com/cilium/ebpf/link"
 	"github.com/coroot/coroot-node-agent/ebpftracer/tracer"
 	"github.com/coroot/coroot-node-agent/proc"
+	"golang.org/x/arch/arm64/arm64asm"
+	"golang.org/x/arch/x86/x86asm"
 	"golang.org/x/sync/semaphore"
 )
 
@@ -189,15 +191,6 @@ func (t *Tracer) getJavaAOTUprobes(binType, path string, dbgpath string, MatchSt
 	for _, v := range listEntry {
 		fmt.Printf("Need Attach Function %s address: %x, %x\n", v.Name, v.EntAddress, v.RetAddress)
 
-		// 函数入口加入待 attach 列表
-		uprobes = append(uprobes, tracer.Uprobe{
-			Funcname:  v.Name,         // 函数名
-			Location:  tracer.AtEntry, // 入口
-			Address:   v.EntAddress,   // 函数地址
-			AbsOffset: 0,              // 函数相对 ELF 偏移
-			RelOffset: 0,              // 函数真实偏移
-		})
-
 		sStart := v.EntAddress - textSection.Addr
 		sSize := v.RetAddress
 		if v.RetAddress > v.EntAddress {
@@ -208,8 +201,28 @@ func (t *Tracer) getJavaAOTUprobes(binType, path string, dbgpath string, MatchSt
 			continue
 		}
 		sBytes := textSectionData[sStart:sEnd]
+		rbpOffsets := getRbpEnterOffsets(elfFile.Machine, sBytes)
 		returnOffsets := getReturnOffsets(elfFile.Machine, sBytes)
 
+		if rbpOffsets != 0 {
+			uprobes = append(uprobes, tracer.Uprobe{
+				Funcname:  v.Name,               // 函数名
+				Location:  tracer.AtDotNetEntry, // 入口
+				Address:   v.EntAddress,         // 函数地址
+				AbsOffset: uint64(rbpOffsets),   // 函数相对 ELF 偏移
+				RelOffset: 0,                    // 函数真实偏移
+			})
+		} else {
+			// 函数入口加入待 attach 列表
+			uprobes = append(uprobes, tracer.Uprobe{
+				Funcname:  v.Name,         // 函数名
+				Location:  tracer.AtEntry, // 入口
+				Address:   v.EntAddress,   // 函数地址
+				AbsOffset: 0,              // 函数相对 ELF 偏移
+				RelOffset: 0,              // 函数真实偏移
+			})
+		}
+
 		for _, offset := range returnOffsets {
 			uprobes = append(uprobes, tracer.Uprobe{
 				Funcname:  v.Name,
@@ -345,6 +358,8 @@ func (t *Tracer) attachUprobes(path string, uprobes []tracer.Uprobe) []link.Link
 			prog = t.uprobes["ent"]
 		case tracer.AtRet:
 			prog = t.uprobes["ret"]
+		case tracer.AtDotNetEntry:
+			prog = t.uprobes["dotnetent"]
 		}
 		uplink, err := ex.Uprobe(up.Funcname, prog, &link.UprobeOptions{Address: up.Address, Offset: up.AbsOffset})
 		if err != nil {
@@ -369,3 +384,26 @@ func (t *Tracer) detachUprobes(links []link.Link) {
 	}
 	fmt.Println()
 }
+
+func getRbpEnterOffsets(machine elf.Machine, instructions []byte) int {
+	switch machine {
+	case elf.EM_X86_64:
+		for i := 0; i < len(instructions); {
+			ins, err := x86asm.Decode(instructions[i:], 64)
+			if err == nil && ins.Op == x86asm.LEA && ins.Args[0].String() == "RBP" {
+				fmt.Printf("getRbpEnterOffsets: %v, %s, %s\n", ins, ins.Args[0].String(), ins.Args[1].String())
+				return i
+			}
+			i += ins.Len
+		}
+	case elf.EM_AARCH64:
+		for i := 0; i < len(instructions); {
+			ins, err := arm64asm.Decode(instructions[i:])
+			if err == nil && ins.Op == arm64asm.RET {
+				return i
+			}
+			i += 4
+		}
+	}
+	return 0
+}

+ 1 - 0
ebpftracer/tracer/uprobe.go

@@ -5,6 +5,7 @@ type UprobeLocation int
 const (
 	AtEntry UprobeLocation = iota
 	AtRet
+	AtDotNetEntry
 	AtGoroutineExit
 	AtGoroutineNewproc1
 	AtGoroutineNewproc1Ret