前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >三分钟学会编写 eBPF 程序:使用 eBPF 程序监控打开文件路径并使用 Prometheus 可视化

三分钟学会编写 eBPF 程序:使用 eBPF 程序监控打开文件路径并使用 Prometheus 可视化

作者头像
云微
发布于 2023-02-24 12:00:50
发布于 2023-02-24 12:00:50
1K00
代码可运行
举报
运行总次数:0
代码可运行

背景

通过对 open 系统调用的监测,opensnoop可以展现系统内所有调用了 open 系统调用的进程信息。

使用 ecli 一键运行

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ # 下载安装 ecli 二进制
$ wget https://aka.pw/bpf-ecli -O ./ecli && chmod +x ./ecli
$ # 使用 url 一键运行
$ ./ecli run https://eunomia-bpf.github.io/eunomia-bpf/opensnoop/package.json

running and waiting for the ebpf events from perf event...
time ts pid uid ret flags comm fname 
00:58:08 0 812 0 9 524288 vmtoolsd /etc/mtab 
00:58:08 0 812 0 11 0 vmtoolsd /proc/devices 
00:58:08 0 34351 0 24 524288 ecli /etc/localtime 
00:58:08 0 812 0 9 0 vmtoolsd /sys/class/block/sda5/../device/../../../class 
00:58:08 0 812 0 -2 0 vmtoolsd /sys/class/block/sda5/../device/../../../label 
00:58:08 0 812 0 9 0 vmtoolsd /sys/class/block/sda1/../device/../../../class 
00:58:08 0 812 0 -2 0 vmtoolsd /sys/class/block/sda1/../device/../../../label 
00:58:08 0 812 0 9 0 vmtoolsd /run/systemd/resolve/resolv.conf 
00:58:08 0 812 0 9 0 vmtoolsd /proc/net/route 
00:58:08 0 812 0 9 0 vmtoolsd /proc/net/ipv6_route 

实现

使用 eunomia-bpf 可以帮助你只需要编写内核态应用程序,不需要编写任何用户态辅助框架代码;需要编写的代码由两个部分组成:

  • 头文件 opensnoop.bpf.h 里面定义需要导出的 C 语言结构体:
  • 源文件 opensnoop.bpf.c 里面定义 BPF 代码:

头文件 opensnoop.bpf.h

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __OPENSNOOP_H
#define __OPENSNOOP_H

#define TASK_COMM_LEN 16
#define NAME_MAX 255
#define INVALID_UID ((uid_t)-1)

// used for export event
struct event {
	/* user terminology for pid: */
	unsigned long long ts;
	int pid;
	int uid;
	int ret;
	int flags;
	char comm[TASK_COMM_LEN];
	char fname[NAME_MAX];
};

#endif /* __OPENSNOOP_H */

opensnoop 的实现逻辑比较简单,它在 sys_enter_opensys_enter_openat 这两个追踪点下 加了执行函数,当有 open 系统调用发生时,执行函数便会被触发。同样在,在对应的 sys_exit_opensys_exit_openat 系统调用下,opensnoop 也加了执行函数。

源文件 opensnoop.bpf.c

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2019 Facebook
// Copyright (c) 2020 Netflix
#include <vmlinux.h>
#include <bpf/bpf_helpers.h>
#include "opensnoop.bpf.h"

struct args_t {
	const char *fname;
	int flags;
};

const volatile pid_t targ_pid = 0;
const volatile pid_t targ_tgid = 0;
const volatile uid_t targ_uid = 0;
const volatile bool targ_failed = false;

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(max_entries, 10240);
	__type(key, u32);
	__type(value, struct args_t);
} start SEC(".maps");

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

static __always_inline bool valid_uid(uid_t uid) {
	return uid != INVALID_UID;
}

static __always_inline
bool trace_allowed(u32 tgid, u32 pid)
{
	u32 uid;

	/* filters */
	if (targ_tgid && targ_tgid != tgid)
		return false;
	if (targ_pid && targ_pid != pid)
		return false;
	if (valid_uid(targ_uid)) {
		uid = (u32)bpf_get_current_uid_gid();
		if (targ_uid != uid) {
			return false;
		}
	}
	return true;
}

SEC("tracepoint/syscalls/sys_enter_open")
int tracepoint__syscalls__sys_enter_open(struct trace_event_raw_sys_enter* ctx)
{
	u64 id = bpf_get_current_pid_tgid();
	/* use kernel terminology here for tgid/pid: */
	u32 tgid = id >> 32;
	u32 pid = id;

	/* store arg info for later lookup */
	if (trace_allowed(tgid, pid)) {
		struct args_t args = {};
		args.fname = (const char *)ctx->args[0];
		args.flags = (int)ctx->args[1];
		bpf_map_update_elem(&start, &pid, &args, 0);
	}
	return 0;
}

SEC("tracepoint/syscalls/sys_enter_openat")
int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter* ctx)
{
	u64 id = bpf_get_current_pid_tgid();
	/* use kernel terminology here for tgid/pid: */
	u32 tgid = id >> 32;
	u32 pid = id;

	/* store arg info for later lookup */
	if (trace_allowed(tgid, pid)) {
		struct args_t args = {};
		args.fname = (const char *)ctx->args[1];
		args.flags = (int)ctx->args[2];
		bpf_map_update_elem(&start, &pid, &args, 0);
	}
	return 0;
}

static __always_inline
int trace_exit(struct trace_event_raw_sys_exit* ctx)
{
	struct event event = {};
	struct args_t *ap;
	int ret;
	u32 pid = bpf_get_current_pid_tgid();

	ap = bpf_map_lookup_elem(&start, &pid);
	if (!ap)
		return 0;	/* missed entry */
	ret = ctx->ret;
	if (targ_failed && ret >= 0)
		goto cleanup;	/* want failed only */

	/* event data */
	event.pid = bpf_get_current_pid_tgid() >> 32;
	event.uid = bpf_get_current_uid_gid();
	bpf_get_current_comm(&event.comm, sizeof(event.comm));
	bpf_probe_read_user_str(&event.fname, sizeof(event.fname), ap->fname);
	event.flags = ap->flags;
	event.ret = ret;

	/* emit event */
	bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
			      &event, sizeof(event));

cleanup:
	bpf_map_delete_elem(&start, &pid);
	return 0;
}

SEC("tracepoint/syscalls/sys_exit_open")
int tracepoint__syscalls__sys_exit_open(struct trace_event_raw_sys_exit* ctx)
{
	return trace_exit(ctx);
}

SEC("tracepoint/syscalls/sys_exit_openat")
int tracepoint__syscalls__sys_exit_openat(struct trace_event_raw_sys_exit* ctx)
{
	return trace_exit(ctx);
}

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

在 enter 环节,opensnoop 会记录调用者的pid, comm等基本信息,并存入map中。在 exit 环节,opensnoop 会根据pid读出之前存入的数据,再结合捕获的其他数据,输出到用户态处理函数中,展现给用户。

完整示例代码请参考:https://github.com/eunomia-bpf/eunomia-bpf/tree/master/bpftools/examples/opensnoop

把头文件和源文件放在独立的目录里面,编译运行:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ # 使用容器进行编译,生成一个 package.json 文件,里面是已经编译好的代码和一些辅助信息
$ docker run -it -v /path/to/opensnoop:/src yunwei37/ebpm:latest
$ # 运行 eBPF 程序(root shell)
$ sudo ecli run package.json  

Prometheus 可视化

编写 yaml 配置文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
programs:
- name: opensnoop
  metrics:
    counters:
    - name: eunomia_file_open_counter
      description: test
      labels:
      - name: pid
      - name: comm
      - name: filename
        from: fname
  compiled_ebpf_filename: package.json

使用 eunomia-exporter 实现导出信息到 Prometheus

  • 通过 https://github.com/eunomia-bpf/eunomia-bpf/releases 下载 eunomia-exporter
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ls
config.yaml  eunomia-exporter package.json
$ sudo ./eunomia-exporter

Running ebpf program opensnoop takes 46 ms
Listening on http://127.0.0.1:8526
running and waiting for the ebpf events from perf event...
Receiving request at path /metrics

总结和参考资料

opensnoop 通过对 open 系统调用的追踪,使得用户可以较为方便地掌握目前系统中调用了 open 系统调用的进程信息。

参考资料:

  • libbpf 参考代码:https://github.com/iovisor/bcc/blob/master/libbpf-tools/opensnoop.bpf.c
  • eunomia-bpf 手册:https://eunomia-bpf.github.io/
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
Nginx静态资源服务的配置
上面配置中的http、server、location等都是指令块。指令块配置项之后是否如参数(例如 location /),取决于解析这个块配置项的模块。
mazhen
2023/11/24
3.6K0
Nginx静态资源服务的配置
保姆级Nginx日志配置文件总结
Nginx服务器日志相关指令主要有两条:一条是log_format,用来设置日志格式;另外一条是access_log,用来指定日志文件的存放路径、格式和缓存大小,可以参加ngx_http_log_module。一般在Nginx的配置文件的日志配置(/usr/local/nginx/conf/nginx.conf)。
兔云小新LM
2023/03/24
2.9K0
03 . Nginx日志配置及日志切割
access_log /usr/local/nginx/logs/b_test/access.log;
iginkgo18
2020/09/27
2.7K0
nginx负载均衡
Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的Web平台。
超蛋lhy
2018/08/31
1.2K0
nginx负载均衡
nginx日志配置
日志对于统计排错来说非常有利的。本文总结了nginx日志相关的配置如access_log、log_format、open_log_file_cache、log_not_found、log_subrequest、rewrite_log、error_log。 nginx有一个非常灵活的日志记录模式。每个级别的配置可以有各自独立的访问日志。日志格式通过log_format命令来定义。ngx_http_log_module是用来定义请求日志格式的。
用户5640963
2019/07/26
1.3K0
Nginx状态监控及日志分析
【转载请注明出处】:https://cloud.tencent.com/developer/article/1636526
后端老鸟
2020/06/01
3K0
Nginx状态监控及日志分析
Nginx 日志格式配置介绍
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
授客
2019/09/11
2.9K0
如何自定义 Nginx日志?
开始之前 为什么要自定义nginx日志? 这里有个例子。示例中希望 nginx 能够记录 php-fpm (上游服务器)执行程序所花费的时间,以便为优化服务器端(程序)响应时间提供支持,nginx 提
用户1560186
2019/11/19
2.4K0
Nginx日志配置
众所周知,线上如果出现事故我们通常都是查看日志去进行问题定位并且进行修复。使用好Nginx日志有利于我们线上进行修复异常问题。在Nginx中日志主要分为两种:access_log(访问日志)和error_log(错误日志)。通过查看access_log我们可以查看用户ip,浏览器信息及请求时间等信息,通过查看error_log我们可以查看线上出错的具体信息,可以帮助我们定位异常的原因。本篇文章主要带领大家详细了解Nginx如何配置日志。本文将会涉及到的日志配置指令:
创译科技
2019/09/06
1.4K0
Nginx日志配置
nginx Access日志格式「建议收藏」
默认,access日志路径是./logs/access.log, 默认的日志格式为combined格式; 使用log_format指令可以自定义日志格式;
全栈程序员站长
2022/06/28
1.6K0
004.Nginx日志配置及状态监控
对于HTTP而言,客户端负责发起request请求,服务端负责response响应。
木二
2020/07/10
1.4K0
004.Nginx日志配置及状态监控
NGINX日志配置总结
本来准备讲解nginx和apache的日志的,但是个人不太推荐apache(纯属个人爱好),这里就不介绍apache的日志了。
CrazyCodes
2018/09/18
1.3K0
【Nginx26】Nginx学习:日志与镜像流量复制
总算到了日志模块,其实这个模块的指令之前我们就用过了,而且也是是非常常见的指令。相信这一块的学习大家应该不会有什么难度。另一个则是镜像功能,这个估计用过的同学就比较少了,不过也并不是特别的复杂,一会讲到的时候咱们再详细说哦。
硬核项目经理
2023/09/18
1.3K0
【Nginx26】Nginx学习:日志与镜像流量复制
玩转 Nginx 之:使用 Lua 扩展 Nginx 功能
1、Nginx 简介 Nginx 作为一款面向性能设计的HTTP服务器,相较于Apache、lighttpd具有占有内存少,稳定性高等优势。其流行度越来越高,应用也越来越广泛,常见的应用有:网页服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器,高并发大流量站点常用来做接入层的负载均衡,还有非常常见的用法是作为日志采集服务器等。 Nginx 整体采用模块化设计,有丰富的模块库和第三方模块库,配置灵活。其中模块化设计是nginx的一大卖点,甚至http服务器核心功能也是一个模块。要注意的是:n
用户1177713
2018/02/24
23.5K0
玩转 Nginx 之:使用 Lua 扩展 Nginx 功能
Nginx日志管理——了解Nginx日志选项配置以及自定义日志格式使用「建议收藏」
不管什么程序,一般都会有日志的。哪怕你在浏览器上网访问了一个网站,也会有记录保存的。在这个里互联网时代,想在网上不留下痕迹那是很难的。在我们开发一个程序,日志功能往往也是不可缺少的,今天我们就来讲讲这个Nginx的日志是怎么样来玩的。
全栈程序员站长
2022/09/07
1.9K0
Nginx日志管理——了解Nginx日志选项配置以及自定义日志格式使用「建议收藏」
Nginx(2)——通用配置
user 设置Nginx服务的系统用户 worker_processes 工作进程数 和硬件CPU核数一致 error_log nginx的错误日志 pid nginx服务启动时候pid woker_connections 每个进程允许最大连接数 use 内核模型select epoll
羊羽shine
2019/05/29
4970
nginx日志模块源码分析
请求在处理结束时,会按请求路径的配置上下文记访问日志。 如果在请求处理期间产生了内部跳转(参考另一篇nginx跳转讲述), 请求结束时的路径可能不同于原始的请求路径。
stan1ey
2021/06/07
1.6K0
nginx日志模块源码分析
Nginx配置中的log_format用法梳理(设置详细的日志格式)
nginx服务器日志相关指令主要有两条:一条是log_format,用来设置日志格式;另外一条是access_log,用来指定日志文件的存放路径、格式和缓存大小,可以参加ngx_http_log_module。一般在nginx的配置文件中日记配置(/usr/local/nginx/conf/nginx.conf)。 log_format指令用来设置日志的记录格式,它的语法如下: log_format name format {format ...} 其中name表示定义的格式名称,format表示定义的格式
洗尽了浮华
2018/01/22
4.4K0
Nginx的日志管理和用定时任务完成日志切割
#log_format main '$remote_addr(远程IP) - $remote_user(远程用户) [$time_local](访问时间) "$request"(请求方式) '
星哥玩云
2022/07/04
7050
Nginx的日志管理和用定时任务完成日志切割
初识nginx基础篇-日志管理和切割
Nginx日志主要分为两种,访问日志和错误日志。两种日志可以在http和server模块中配置,nginx有一个非常灵活的日志记录模式。每个级别的配置可以有各自独立的访问日志。日志格式通过log_format命令来定义
后端技术探索
2018/08/09
1.3K0
相关推荐
Nginx静态资源服务的配置
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档