开源 C 语言库 Melon:用户态动态追踪与控制反馈 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
monkeyNik
V2EX    C

开源 C 语言库 Melon:用户态动态追踪与控制反馈

  •  
  •   monkeyNik 2023-02-03 20:13:14 +08:00 1231 次点击
    这是一个创建于 1034 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    本文根据开源 C 语言库 Melon 的最新特性,讲述使用该库做用户态动态追踪,以及根据追踪内容进行计算,并将结果用于反馈给程序,同时对程序的处理流程进行影响。

    说到动态追踪,大家可能第一印象是 bpf 、dtrace 、systemtap 等等,但是本文介绍的动态追踪不依赖于这些内容。Melon 中提供的功能更加倾向于让程序在用户态内完对自身的动态追踪,而不依赖于内核态,也不依赖于 uprobe 和 usdt 等内容

    关于 Melon 库,这是一个开源的 C 语言库,它具有:开箱即用、无第三方依赖、安装部署简单、中英文文档齐全等优势。

    Github: https://github.com/Water-Melon/Melon

    原理

    简单来说,Melon 库的动态追踪也是在程序中加入跟踪点。只是在 Melon 中,一个应用程序被划分为两个层面(但都运行在同一个进程中):

    • C 代码层
    • Melang 脚本代码层

    两个代码层面可以运行在同一线程,也可以运行在不同线程中,但需要运行于同一进程下。

    换言之:跟踪点信息会在 C 层被抛出,传入给指定的脚本任务,然后脚本任务接收信息并进行处理。

    这么看,似乎和在程序中记录日志,然后额外写一个程序来读取日志进行处理没有什么区别,那么这么做的好处是什么呢?

    优势

    • 不需要对日志的格式进行解析,即可直接拿到对应类型的数据
    • 脚本侧运算后可进行远程传输或者入库(脚本库函数保证)
    • 即使在同一线程下,脚本任务执行也不会长期中断 C 层逻辑,脚本和 C 逻辑是自动分时处理的
    • 利用脚本侧的反馈机制,可以将运算结果传回 C 层,让 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; } 

    简单来描述下程序流程:

    1. 对 Melon 库进行全局初始化(mln_core_init)
    2. 初始化事件对象
    3. 初始化跟踪脚本
    4. 设置用于处理脚本层发来数据的函数
    5. 设置超时事件
    6. 事件分发,超时事件会被触发

    在超时处理函数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 层施加影响。而最关键的三点是:

    1. C 层中不需要增加额外的统计变量和结构
    2. C 层与脚本层在代码管理层面上是分离的,互不干扰
    3. 两个层面的代码运行在同一个线程下

    为了简化演示代码,上面的例子中没有给出在脚本层做网络通信和数据入库,但这些功能 Melang 脚本全部支持,感兴趣的读者可以参考Melang 官网

    最后,对于 Melon 库感兴趣的读者可以访问其Github 仓库获取更多信息。

    感谢阅读!

    tairan2006
        1
    tairan2006  
       2023-02-04 09:03:29 +08:00 via Android
    这种现在不是都用 OpenTelemetry 么
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2829 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 22ms UTC 14:15 PVG 22:15 LAX 06:15 JFK 09:15
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86