Skip to content

Commit

Permalink
Merge pull request #605 from zhangzihengya/develop
Browse files Browse the repository at this point in the history
process_image 项目:完成多个.bpf.c一个.c的进程画像框架,并在其中复现资源使用情况,提交系统调用序列功能代码
  • Loading branch information
chenamy2017 authored Dec 1, 2023
2 parents 6820581 + 4a78834 commit d895800
Show file tree
Hide file tree
Showing 8 changed files with 457 additions and 38 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/eBPF_proc_image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ jobs:
cd eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image
make proc_image
sudo ./proc_image -t 1
- name: Run new_proc_image
run: |
cd eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing
make proc_image
sudo ./proc_image -r -t 1
- name: Run mutex_test
run: |
Expand Down
2 changes: 1 addition & 1 deletion eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ VMLINUX := ../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC)
INCLUDES := -I$(OUTPUT) -I../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC)
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)

Expand Down
2 changes: 1 addition & 1 deletion eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

本项目是一个Linux进程生命周期画像工具,通过该工具可以清晰展示出一个进程从创建到终止的完整生命周期,并可以额外展示出进程/线程持有锁的区间画像、进程/线程上下文切换原因的标注、线程之间依赖关系(线程)、进程关联调用栈信息标注等。在这些功能的前提下,加入了更多的可视化元素和交互方式,使得整个画像更加直观、易于理解。

运行环境:Ubuntu 22.04,内核版本5.19.0-46-generic
运行环境:Ubuntu 22.04,内核版本 6.2

## 二、安装依赖

Expand Down
133 changes: 133 additions & 0 deletions eBPF_Supermarket/CPU_Subsystem/eBPF_proc_image/developing/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# Copyright 2023 The LMP Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# author: [email protected]
#
# compile the current folder code

# SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
OUTPUT := .output
CLANG ?= clang
LIBBPF_SRC := $(abspath ../../libbpf/src)
BPFTOOL_SRC := $(abspath ../../bpftool/src)
LIBBPF_OBJ := $(abspath $(OUTPUT)/libbpf.a)
BPFTOOL_OUTPUT ?= $(abspath $(OUTPUT)/bpftool)
BPFTOOL ?= $(BPFTOOL_OUTPUT)/bootstrap/bpftool
LIBBLAZESYM_SRC := $(abspath ../../blazesym/)
LIBBLAZESYM_INC := $(abspath $(LIBBLAZESYM_SRC)/include)
LIBBLAZESYM_OBJ := $(abspath $(OUTPUT)/libblazesym.a)
ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' \
| sed 's/arm.*/arm/' \
| sed 's/aarch64/arm64/' \
| sed 's/ppc64le/powerpc/' \
| sed 's/mips.*/mips/' \
| sed 's/riscv64/riscv/' \
| sed 's/loongarch64/loongarch/')
VMLINUX := ../../vmlinux/$(ARCH)/vmlinux.h
# Use our own libbpf API headers and Linux UAPI headers distributed with
# libbpf to avoid dependency on system-wide headers, which could be missing or
# outdated
INCLUDES := -I$(OUTPUT) -I../../libbpf/include/uapi -I$(dir $(VMLINUX)) -I$(LIBBLAZESYM_INC) -I./include
CFLAGS := -g -Wall
ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS)

APPS = resource_image #syscall_image
TARGETS = proc_image

# Get Clang's default includes on this system. We'll explicitly add these dirs
# to the includes list when compiling with `-target bpf` because otherwise some
# architecture-specific dirs will be "missing" on some architectures/distros -
# headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h,
# sys/cdefs.h etc. might be missing.
#
# Use '-idirafter': Don't interfere with include mechanics except where the
# build would have failed anyways.
CLANG_BPF_SYS_INCLUDES ?= $(shell $(CLANG) -v -E - </dev/null 2>&1 \
| sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }')

ifeq ($(V),1)
Q =
msg =
else
Q = @
msg = @printf ' %-8s %s%s\n' \
"$(1)" \
"$(patsubst $(abspath $(OUTPUT))/%,%,$(2))" \
"$(if $(3), $(3))";
MAKEFLAGS += --no-print-directory
endif

define allow-override
$(if $(or $(findstring environment,$(origin $(1))),\
$(findstring command line,$(origin $(1)))),,\
$(eval $(1) = $(2)))
endef

$(call allow-override,CC,$(CROSS_COMPILE)cc)
$(call allow-override,LD,$(CROSS_COMPILE)ld)

.PHONY: all
all: $(TARGETS)

.PHONY: clean
clean:
$(call msg,CLEAN)
$(Q)rm -rf $(OUTPUT) $(TARGETS)

$(OUTPUT) $(OUTPUT)/libbpf $(BPFTOOL_OUTPUT):
$(call msg,MKDIR,$@)
$(Q)mkdir -p $@

# Build libbpf
$(LIBBPF_OBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)/libbpf
$(call msg,LIB,$@)
$(Q)$(MAKE) -C $(LIBBPF_SRC) BUILD_STATIC_ONLY=1 \
OBJDIR=$(dir $@)/libbpf DESTDIR=$(dir $@) \
INCLUDEDIR= LIBDIR= UAPIDIR= \
install

# Build bpftool
$(BPFTOOL): | $(BPFTOOL_OUTPUT)
$(call msg,BPFTOOL,$@)
$(Q)$(MAKE) ARCH= CROSS_COMPILE= OUTPUT=$(BPFTOOL_OUTPUT)/ -C $(BPFTOOL_SRC) bootstrap

# Build BPF code
$(OUTPUT)/%.bpf.o: bpf/%.bpf.c $(LIBBPF_OBJ) $(wildcard %.h) $(VMLINUX) | $(OUTPUT) $(BPFTOOL)
$(call msg,BPF,$@)
$(Q)$(CLANG) -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) \
$(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) \
-c $(filter %.c,$^) -o $(patsubst %.bpf.o,%.tmp.bpf.o,$@)
$(Q)$(BPFTOOL) gen object $@ $(patsubst %.bpf.o,%.tmp.bpf.o,$@)

# Generate BPF skeletons
.PHONY: $(APPS)
$(APPS): %: $(OUTPUT)/%.bpf.o | $(OUTPUT) $(BPFTOOL)
$(call msg,GEN-SKEL,$@)
$(Q)$(BPFTOOL) gen skeleton $< > $(OUTPUT)/$@.skel.h

# Build user-space code
$(OUTPUT)/$(TARGETS).o: $(TARGETS).c $(APPS) | $(OUTPUT)
$(call msg,CC,$@)
$(Q)$(CC) $(CFLAGS) $(INCLUDES) -c $(filter %.c,$^) -o $@

# Build application binary
$(TARGETS): %: $(OUTPUT)/%.o $(LIBBPF_OBJ) | $(OUTPUT)
$(call msg,BINARY,$@)
$(Q)$(CC) $^ $(ALL_LDFLAGS) -lstdc++ -lelf -lz -o $@

# delete failed targets
.DELETE_ON_ERROR:

# keep intermediate (.skel.h, .bpf.o, etc) targets
.SECONDARY:
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "include/proc_image.h"
#include <linux/version.h>
#include "proc_image.h"

const volatile pid_t target_pid = -1;
const volatile int target_cpu_id = -1;
Expand Down Expand Up @@ -55,7 +56,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx)
(target_pid==0 && prev_pid==target_pid && prev_cpu==target_cpu_id)){
struct proc_id prev_pd = {0};
prev_pd.pid = prev_pid;
prev_pd.cpu_id = prev_cpu;
if(prev_pid == 0) prev_pd.cpu_id = prev_cpu;

if(bpf_map_lookup_elem(&start,&prev_pd) != NULL){
struct start_rsc *prev_start = bpf_map_lookup_elem(&start,&prev_pd);
Expand All @@ -65,15 +66,23 @@ int kprobe__finish_task_switch(struct pt_regs *ctx)

if(bpf_map_lookup_elem(&total,&prev_pd) == NULL){
struct total_rsc prev_total = {0};
struct mm_rss_stat rss = {};
long long *c;
long unsigned int memused;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)

#else
/*
struct mm_rss_stat rss = {};
long long *c;
rss = BPF_CORE_READ(prev, mm, rss_stat);
c = (long long *)(rss.count);
if(!c) return 0;
memused = *c + *(c + 1) + *(c + 3);
*/
#endif

prev_total.pid = prev_pd.pid;
prev_total.cpu_id = prev_cpu;
prev_total.time = bpf_ktime_get_ns() - prev_start->time;
prev_total.memused = memused;
prev_total.readchar = BPF_CORE_READ(prev,ioac.rchar) - prev_start->readchar;
Expand All @@ -86,15 +95,22 @@ int kprobe__finish_task_switch(struct pt_regs *ctx)
return 0;
}

struct mm_rss_stat rss = {};
long long *c;
long unsigned int memused;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 2, 0)

#else
/*
struct mm_rss_stat rss = {};
long long *c;
rss = BPF_CORE_READ(prev, mm, rss_stat);
c = (long long *)(rss.count);
if(!c) return 0;
memused = *c + *(c + 1) + *(c + 3);
*/
#endif

//prev_total->pid = prev_pd.pid;
prev_total->cpu_id = prev_cpu;
prev_total->time += bpf_ktime_get_ns() - prev_start->time;
prev_total->memused = memused;
prev_total->readchar += BPF_CORE_READ(prev,ioac.rchar) - prev_start->readchar;
Expand All @@ -111,7 +127,7 @@ int kprobe__finish_task_switch(struct pt_regs *ctx)
struct start_rsc next_start={0};

next_pd.pid = next_pid;
next_pd.cpu_id = next_cpu;
if(next_pid == 0) next_pd.cpu_id = next_cpu;

next_start.time = bpf_ktime_get_ns();
next_start.readchar = BPF_CORE_READ(next,ioac.rchar);
Expand All @@ -121,4 +137,4 @@ int kprobe__finish_task_switch(struct pt_regs *ctx)
}

return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2023 The LMP Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://github.com/linuxkerneltravel/lmp/blob/develop/LICENSE
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// author: [email protected]
//
// eBPF kernel-mode code that collects process syscalls

#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "proc_image.h"

char LICENSE[] SEC("license") = "Dual BSD/GPL";

const volatile pid_t target_pid = -1;

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10); // 可根据自己的CPU核心数进行设置,这里设置为10
__type(key, pid_t);
__type(value,struct syscall_seq);
} proc_syscall SEC(".maps");

struct {
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
__uint(key_size, sizeof(u32));
__uint(value_size, sizeof(u32));
} syscalls SEC(".maps");

// 记录进程的系统调用序列
SEC("tracepoint/raw_syscalls/sys_enter")
int sys_enter(struct trace_event_raw_sys_enter *args)
{
pid_t pid = bpf_get_current_pid_tgid();

if(target_pid==-1 || pid==target_pid){
struct syscall_seq * syscall_seq;

syscall_seq = bpf_map_lookup_elem(&proc_syscall, &pid);
if(!syscall_seq){
return 0;
}

if(syscall_seq->count < MAX_SYSCALL_COUNT-1 && syscall_seq->count >= 0){
if((syscall_seq->record_syscall+syscall_seq->count) <= (syscall_seq->record_syscall+MAX_SYSCALL_COUNT)){
syscall_seq->record_syscall[syscall_seq->count] = args->id;
syscall_seq->count ++;
}
}else if(syscall_seq->count == MAX_SYSCALL_COUNT-1){
syscall_seq->record_syscall[syscall_seq->count] = -1;
syscall_seq->count = MAX_SYSCALL_COUNT;
}
}

return 0;
}


// 以进程on_cpu为单位输出系统调用序列
SEC("tp_btf/sched_switch")
int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next)
{
pid_t next_pid = BPF_CORE_READ(next,pid);
pid_t prev_pid = BPF_CORE_READ(prev,pid);
u64 current_time = bpf_ktime_get_ns();

// 输出prev进程的syscall_seq事件
if(target_pid==-1 || prev_pid==target_pid){
struct syscall_seq * prev_syscall_seq;

prev_syscall_seq = bpf_map_lookup_elem(&proc_syscall, &prev_pid);
if(prev_syscall_seq){
prev_syscall_seq->offcpu_time = current_time;

bpf_perf_event_output(ctx, &syscalls, BPF_F_CURRENT_CPU, prev_syscall_seq, sizeof(*prev_syscall_seq));
bpf_map_delete_elem(&proc_syscall, &prev_pid);
}
}

// 记录next进程的开始时间
if(target_pid==-1 || next_pid==target_pid){
struct syscall_seq next_syscall_seq = {};

next_syscall_seq.pid = next_pid;
next_syscall_seq.oncpu_time = current_time;

bpf_map_update_elem(&proc_syscall, &next_pid, &next_syscall_seq, BPF_ANY);
}

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
#ifndef __PROC_IMAGE_H
#define __PROC_IMAGE_H

#define MAX_SYSCALL_COUNT 58

// resource_image
struct proc_id{
long unsigned int pid;
long unsigned int cpu_id;
int pid;
int cpu_id;
};

struct start_rsc{
Expand All @@ -32,11 +34,21 @@ struct start_rsc{
};

struct total_rsc{
long unsigned int pid;
int pid;
int cpu_id;
long long unsigned int time;
long unsigned int memused;
long long unsigned int readchar;
long long unsigned int writechar;
};

//syscall_image
struct syscall_seq{
int pid;
long long unsigned int oncpu_time;
long long unsigned int offcpu_time;
int count; // 若count值超过MAX_SYSCALL_COUNT,则record_syscall数组最后一个元素的值用-1表示以作说明
long int record_syscall[MAX_SYSCALL_COUNT];
};

#endif /* __PROCESS_H */
Loading

0 comments on commit d895800

Please sign in to comment.