赞
踩
本文根据开源C语言库Melon的最新特性,讲述使用该库做用户态动态追踪,以及根据追踪内容进行计算,并将结果用于反馈给程序,同时对程序的处理流程进行影响。
说到动态追踪,大家可能第一印象是bpf、dtrace、systemtap等等,但是本文介绍的动态追踪不依赖于这些内容。Melon中提供的功能更加倾向于让程序在用户态内完成对自身的动态追踪,而不依赖于内核态,也不依赖于uprobe和usdt等内容。
关于 Melon 库,这是一个开源的 C 语言库,它具有:开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。
Github: https://github.com/Water-Melon/Melon
简单来说,Melon库的动态追踪也是在程序中加入跟踪点。只是在Melon中,一个应用程序被划分为两个层面(但都运行在同一个进程中):
两个代码层面可以运行在同一线程,也可以运行在不同线程中,但需要运行于同一进程下。
换言之:跟踪点信息会在C层被抛出,传入给指定的脚本任务,然后脚本任务接收信息并进行处理。
这么看,似乎和在程序中记录日志,然后额外写一个程序来读取日志进行处理没有什么区别,那么这么做的好处是什么呢?
优势:
下面来看一个非常简单的例子:
#include <stdio.h> #include "mln_log.h" #include "mln_core.h" #include "mln_trace.h" #include "mln_conf.h" #include "mln_event.h" int timeout = 100; static void timeout_handler(mln_event_t *ev, void *data) { mln_trace("sir", "Hello", getpid(), 3.1); mln_event_timer_set(ev, timeout, NULL, timeout_handler); } static int recv_handler(mln_lang_ctx_t *ctx, mln_lang_val_t *val) { timeout += val->data.i; return 0; } int main(int argc, char *argv[]) { mln_event_t *ev; struct mln_core_attr cattr; cattr.argc = argc; cattr.argv = argv; cattr.global_init = NULL; cattr.main_thread = NULL; cattr.master_process = NULL; cattr.worker_process = NULL; if (mln_core_init(&cattr) < 0) { fprintf(stderr, "Melon init failed.\n"); return -1; } if ((ev = mln_event_new()) == NULL) { mln_log(error, "event new error\n"); return -1; } if (mln_trace_init(ev, mln_trace_path()) < 0) { mln_log(error, "trace init error\n"); return -1; } mln_trace_recv_handler_set(recv_handler); mln_event_timer_set(ev, 1000, NULL, timeout_handler); mln_event_dispatch(ev); return 0; }
简单来描述下程序流程:
mln_core_init
)在超时处理函数timeout_handler
中,我们利用mln_trace
向脚本任务发送了三个不同类型的数据,然后继续设置超时事件。
超时时长是一个全局变量timeout
,初始为100,即100毫秒。
当脚本层发来数据时,这里我们约定脚本层一定发来的是一个整数,那么在接收函数recv_handler
中,我们将这一数值与timeout
进行累加,作为随后的超时时长。
由此,可以猜测,程序中每秒向脚本层投递的数据量会越来越少。
下面给出脚本层代码:
sys = Import('sys'); if (MASTER) sys.print('master process'); else sys.print('worker process'); Pipe('subscribe'); while (1) { ret = Pipe('recv'); if (ret) { for (i = 0; i < sys.size(ret); ++i) { sys.print(ret[i]); } Pipe('send', 100); } fi sys.msleep(1000); } Pipe('unsubscribe');
简单描述下脚本层逻辑就是,每秒钟从C层接收一批数据,然后向终端输出,且在输出后,向C层发送一个整数100
。
下面来看下程序运行结果:
...
[Hello, 72173, 3.100000, ]
[Hello, 72173, 3.100000, ]
[Hello, 72173, 3.100000, ]
...
会看到很多上述输出,但是读者若自己运行则会发现,每秒钟的输出行数会越来越少,这与我们的程序逻辑是相符合的。
从这一例子中,我们可以看到,我们既可以在脚本侧做跟踪统计,也可以向C层施加影响。而最关键的三点是:
为了简化演示代码,上面的例子中没有给出在脚本层做网络通信和数据入库,但这些功能Melang脚本全部支持,感兴趣的读者可以参考Melang官网。
最后,对于Melon库感兴趣的读者可以访问其Github仓库获取更多信息。
感谢阅读!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。