一、目录
文章目录
二、目标
分为两个方面,
- 提取Trace 数据中我需要的指标。用于 开发自己设计的监控程序。(本机目标 优先)
- LTTng Live下进行数据分析和提取。(远端目标 其次)
三、工具 概述
需要学习研究下面两个方面:
babeltrace2
CTF “Common Trace Format”
研究发现 CTF也是他们自己搞出来的格式。而且是二进制的数据格式。学习意义不大 感觉、、、
babeltrace2
babeltrace2是LTTng项目独立出来的一套Trace log 文件处理工具。
我研究了一下感觉像是要翻车啊。这一套东西感觉人气不旺啊。在网上连问问题的人都很少。更别说有例子可以参考了。
唯一找到一篇中文博客还是骂他API设计的烂的。。。如果想要完成任务可能只有靠自己了。
工具切入点
babeltrace2 也是很努力的提供了三套工具来给人用。:
1、CLI 也就是命令行。可以直接通过终端来构建一个所谓的trace processing graph。然后来处理数据。这个先不管。
2、C API。这个是我关注的重点。因为我的目标是 在嵌入式设备中把Trace数据转换格式。虽然有Live模式可以配置,但是嵌入式设备的网络环境相比堪忧。那么还是在本地简单的处理一下,把东西走大家都用的通道传出来吧。不要让我单独再给LTTng live 配置个TCP的通道了。
3、Python API。这个备选吧。据他自己的说法是,操作起来比C简单。但是运行慢。
babeltrace2 工具的架构
babeltrace2 可以这样看:
plugins 插件:由一组class 构成。可以分成三类:source filter sink。
source: 负责读取数据 针对不同的格式和数据源进行定制
filter: 负责对数据进行处理
sink: 导出数据,针对不同的格式需求和输出目标进行定制。
graph: 由各种class 的实例构成
connections: class 之间的链接线
目前有一些提供的class, 也可以使用API自己开发class 和 plugins。
四、使用Babeltrace 2 C API
1、Graph
Graph 的概念相当于是顶层的架构设计。我的计划是先使用提供的现有库做一个简单的demo。然后再根据我的任务去开发自己的component。那么先从graph入手。理想的话可以先run起来。
图中的.so 文件我没有找到,可能在编译库的时候需要特殊的configure。不过如果不需要动态链接可能也不需要。
后面我找到了 在/usr/local/lib/babeltrace2/plugins下面 名字和图中不一样 babeltrace后面不带2
Graph 的生命周期和API
graph 的类型是 bt_graph
.
Create a default trace processing graph with bt_graph_create()
. You need to pass a Message Interchange Protocol (MIP) version number when you call this function. 这个函数要传MIP version 这个参数。这个参数类似VLAN号,只有参数一样构建的模块才能通messages。
创建Graph
创建Graph
bt_graph* bt_graph_create ( uint64_t mip_version )
As of Babeltrace 2.0, the only available MIP version is 0.现在是已经舍弃了MIP机制了? 只能传值0?
在建立Graph之后,可以使用 add component系列function添加component。
Add components
在Graph上实例化 component class。
必须通过下列六个API添加:
// Alias of bt_graph_add_XXXX_component_with_initialize_method_data() with the initialize_method_data parameter set to NULL.
// Creates a XXXX component from the class component_class with the initialization parameters params, the initialization user data initialize_method_data, and the initial logging level logging_level, adds it to the trace processing graph graph with the name name, and sets *component to the resulting component.
bt_graph_add_component_status bt_graph_add_source_component (bt_graph *graph, const bt_component_class_source *component_class, const char *name, const bt_value *params, bt_logging_level logging_level, const bt_component_source **component)
bt_graph_add_component_status bt_graph_add_source_component_with_initialize_method_data (bt_graph *graph, const bt_component_class_source *component_class, const char *name, const bt_value *params, void *initialize_method_data, bt_logging_level logging_level, const bt_component_source **component)
bt_graph_add_component_status bt_graph_add_filter_component (bt_graph *graph, const bt_component_class_filter *component_class, const char *name, const bt_value *params, bt_logging_level logging_level, const bt_component_filter **component)
bt_graph_add_component_status bt_graph_add_filter_component_with_initialize_method_data (bt_graph *graph, const bt_component_class_filter *component_class, const char *name, const bt_value *params, void *initialize_method_data, bt_logging_level logging_level, const bt_component_filter **component)
bt_graph_add_component_status bt_graph_add_sink_component (bt_graph *graph, const bt_component_class_sink *component_class, const char *name, const bt_value *params, bt_logging_level logging_level, const bt_component_sink **component)
bt_graph_add_component_status bt_graph_add_sink_component_with_initialize_method_data (bt_graph *graph, const bt_component_class_sink *component_class, const char *name, const bt_value *params, void *initialize_method_data, bt_logging_level logging_level, const bt_component_sink **component)
Parameters
[in] graph Trace processing graph to which to add the created source component.
[in] component_class Class to instantiate within graph.
[in] name Unique name, within graph, of the component to create.
[in] params Initialization parameters to use when creating the source component. Can be NULL, in which case the created source component’s initialization method receives an empty map value as its params parameter.
[in] initialize_method_data User data passed as is to the created source component’s initialization method.
[in] logging_level Initial logging level of the source component to create.
[out] component On success, if not NULL, *component is a borrowed reference of the created source component.
Connect component ports
bt_graph_connect_ports_status bt_graph_connect_ports (
bt_graph * graph,
const bt_port_output * upstream_port,
const bt_port_input * downstream_port,
const bt_connection ** connection
)
Parameters
[in] graph Trace processing graph containing the components to which belong upstream_port and downstream_port.
[in] upstream_port Output port to connect to downstream_port.
[in] downstream_port Input port to connect to upstream_port.
[in] connection On success, if not NULL, *connection is a borrowed reference of the resulting connection.
在Run
之前 ,添加component和连线可以反复进行,没说要先添加所有的再连线。
但是一旦Run
了,就不要再改设计了。
Run
//Runs the trace processing graph graph, calling each sink component's consuming method in a round robin fashion until they are all done consuming or an exception occurs.
bt_graph_run_status bt_graph_run ( bt_graph * graph )
//Calls the consuming method of the next non-ended sink component to make consume within the trace processing graph graph.
bt_graph_run_once_status bt_graph_run_once ( bt_graph * graph )
once是只处理一个message吗?
另一个是会RR形式将所有喂给sink的message做完?
使用API构建的简单Demo
真正上手写一写代码,我理解了两件事
1、为什么LTTng这个项目没人用,还专门被人写博客骂
C API里关于使用API的代码例子实在是太少了,或者说根本就没有使用示例。接口根本不知道怎么用。
而且LTTng也好,Babeltrace也好,感觉生怕别人理解不了,拼命想把理念说明白,自己整了很多新概念出来。更是让人云里雾里。
2、为什么很多程序员离开了github就写不了代码
我在github上找到了一个老哥基于(可能是全世界唯一一个)babeltrace的库写的小项目。在里面找到了接口调用。虽然他觉得很难用,又用C++封了一层,但是我还是看懂了。想必很多程序员拿到库和需求,可以直接上github去抄(如果东西比较热门的话)。那真是省事多了。//stack overflow都搜不到。。
下面是成果:读取LTTng生成的默认trace 数据,然后打印到文件里:
#include <babeltrace2/babeltrace.h>
int main(int argc, char const *argv[])
{
//create graph
bt_graph * my_graph = bt_graph_create(0);//看手册似乎必须通过0构造。
/**
*Add source component to read ctf files
*
*/
//find plugin, and borrow component class from plugin
const bt_plugin * my_source_ctf_fs_plugin;
bt_plugin_find("ctf",BT_FALSE,BT_FALSE,BT_TRUE,BT_FALSE,BT_TRUE,&my_source_ctf_fs_plugin);//先是通过查找插件的名字来找到插件
//我觉得可能在这里链接了.so库
const bt_component_class_source *my_source_ctf_fs_class =
bt_plugin_borrow_source_component_class_by_name_const(my_source_ctf_fs_plugin,"fs");
//然后因为插件、类是所谓的共享对象,必须“借”一个来使用。根据名字从插件里借出类
//add components from class
bt_value * my_ctf_path=bt_value_array_create();
bt_value_array_append_string_element(my_ctf_path,"/home/userhome/lttng-traces/my_session/kernel");//存放trace数据的目录地址,能看到metadata index 之类的地方。
bt_value * my_source_ctf_fs_parameter=bt_value_map_create();
bt_value_map_insert_entry(my_source_ctf_fs_parameter,"inputs",my_ctf_path);
//这里是构造模块的初始化参数。定义的是参数是map形式的bt_value这是一种类似json的格式。
const bt_component_source* my_ctf_reader;
bt_graph_add_source_component(my_graph,
my_source_ctf_fs_class,
"my_reader",
my_source_ctf_fs_parameter,
BT_LOGGING_LEVEL_ERROR,//这里根据选择的不同在运行时候会在终端打印不同等级的信息。改成debug真的有助于开发的时候查错。
&my_ctf_reader
);
/**
* Add sink component to write trace log in file
*/
//find plugin, and borrow component class from plugin
const bt_plugin * my_sink_text_pretty_plugin;
bt_plugin_find("text",BT_FALSE,BT_FALSE,BT_TRUE,BT_FALSE,BT_TRUE,&my_sink_text_pretty_plugin);
const bt_component_class_sink *my_sink_text_pretty_class =
bt_plugin_borrow_sink_component_class_by_name_const(my_sink_text_pretty_plugin,"pretty");
//add components from class
bt_value * my_writer_parameter=bt_value_map_create();
bt_value_map_insert_string_entry(my_writer_parameter,"path","my_log");
const bt_component_sink* my_pretty_writer;
bt_graph_add_sink_component (my_graph,
my_sink_text_pretty_class,
"my_writer",
my_writer_parameter,
BT_LOGGING_LEVEL_DEBUG,
&my_pretty_writer
);
/**
* I find that the source conponent has 4 output ports.
* so maybe, I need a muxer component.
*/
const bt_plugin * my_filter_utils_muxer_plugin;
bt_plugin_find("utils",BT_FALSE,BT_FALSE,BT_TRUE,BT_FALSE,BT_TRUE,&my_filter_utils_muxer_plugin);
const bt_component_class_filter *my_filter_utils_muxer_class =
bt_plugin_borrow_filter_component_class_by_name_const(my_filter_utils_muxer_plugin,"muxer");
const bt_component_filter * my_muxer;
bt_graph_add_filter_component(my_graph,
my_filter_utils_muxer_class,
"my_muxer",
NULL,
BT_LOGGING_LEVEL_ERROR,
&my_muxer
);
/**
* Now, let`s connect all the port.
*/
//source
const bt_port_output * ctf_fs_0 =
bt_component_source_borrow_output_port_by_index_const(my_ctf_reader,0);
const bt_port_output * ctf_fs_1 =
bt_component_source_borrow_output_port_by_index_const(my_ctf_reader,1);
const bt_port_output * ctf_fs_2 =
bt_component_source_borrow_output_port_by_index_const(my_ctf_reader,2);
const bt_port_output * ctf_fs_3 =
bt_component_source_borrow_output_port_by_index_const(my_ctf_reader,3);
//filter
//NOTE: After you connecting one port, the component will create the next one. So,you should borrow and connect one by one.
const bt_port_input * muxer_in_0 =
bt_component_filter_borrow_input_port_by_name_const(my_muxer,"in0");
bt_graph_connect_ports(my_graph,ctf_fs_0,muxer_in_0,NULL);
const bt_port_input * muxer_in_1 =
bt_component_filter_borrow_input_port_by_name_const(my_muxer,"in1");
bt_graph_connect_ports(my_graph,ctf_fs_1,muxer_in_1,NULL);
const bt_port_input * muxer_in_2 =
bt_component_filter_borrow_input_port_by_name_const(my_muxer,"in2");
bt_graph_connect_ports(my_graph,ctf_fs_2,muxer_in_2,NULL);
const bt_port_input * muxer_in_3 =
bt_component_filter_borrow_input_port_by_name_const(my_muxer,"in3");
bt_graph_connect_ports(my_graph,ctf_fs_3,muxer_in_3,NULL);
const bt_port_output * muxer_out =
bt_component_filter_borrow_output_port_by_name_const(my_muxer,"out");
//sink
const bt_port_input * pretty_in =
bt_component_sink_borrow_input_port_by_name_const(my_pretty_writer,"in");
//connections
bt_graph_connect_ports(my_graph,muxer_out,pretty_in,NULL);
//run
bt_graph_run(my_graph);
return 0;
}