比不过差之API(ETW)更不比的API(LTTng)是哪炼成的, 谈如何勾勒一个好之接口

后端开发:

近期随即几天在赞助柠檬在押她底APM系统而怎么样收集.Net运行时的各种风波,
这些事件包括线程开始, JIT执行, GC触发等等.
.Net以windows上(NetFramework, CoreCLR)通过ETW(Event Tracing for
Windows), 在linux上(CoreCLR)是透过LTTng跟踪事件.

1、高级java软件架构师实战培训视频教程
2、大型SpringMVC,Mybatis,Redis,Solr,Nginx,SSM分布式电商项目视频教程
3、Spark
Streaming实时流处理项目实战
4、Java校招面试 Google面试官亲授
5、Java开发公司级权限管理体系
6、Java大牛
带你从0到上线开发公司级电商项目
7、Java
SSM淘淘商城12天电商项目
8、Java
SSM快速支付仿慕课网在线教育平台
9、Java
SSM开发大众点评后端
10、Java
Spring带前后端支出总体电商平台
11、Java
Spring 技术栈构建前后台团购网站
12、java
Spring Security开发安全之REST服务
13、java
Spring Boot企业微信点餐系统
14、java
Spring Boot带前后端 渐进式开发企业级博客系统
15、java
c++算法与数据结构

ETW的API设计都给众总人口熊,
微软推出的类库krabsetw中直指ETW是极度差之API而将操作ETW的文件命名也噩梦.hpp.
而且及时篇稿子蒙, Casey
Muratori解释了为什么ETW是最好差之API, 原盖包括:

16、ThinkPHP5.0第二季
17、ThinkPHP5.0基础视频教程
18、前端到后台ThinkPHP开发整站
19、PHP入门:基础语法到实在应用
20、PHP秒杀系统-高并发高性能的顶挑战(完整版本)
21、PHP开发大可用大安全app后端
22、php从基础及原生开发
23、PHP7+WEB+Mysql+thinkphp+laravel
24、PHP+Ajax+jQuery网站开发项目式教程
25、PHP
高性能 高价值的PHP API接口
26、PHP
thinkphp实战开发企业站
27、PHP
Thinkphp 5.0 仿百度过糯米开发多店电商平台
28、PHP
360格外牛全面解读PHP面试
29、Laravel5.4快速开简书网站
30、PHP项目的微信支付接口视频讲解
31、微信小程序入门与实战 常用组件 API 开发技巧
项目实战
32、微信小序
ThinkPHP5.0+小序商城构建全栈应用
33、微信服务号+Yii
2.0构建商城系统全栈应用
34、Yii
2.0支一个仿京东商城平台
35、Yii
2.0上阶版 高级组件 ES + Redis + Sentry 优化京东平台

  • 事件类应用了各类标志(最多只能有32只), 没有设想到未来的状况
  • 今非昔比之接口共用一个深的构造体, 接口的输入和出口不明明
  • 受调用者写无论怎么看都是多余的代码
  • 被调用者使用魔法数字(而休是供一个枚举值)
  • 命名带有误导性
  • 返回值的含义不合并
  • 使过度复杂, 没有先行想吓用例
  • 文档中没完的演示代码, 只能于零星的代码拼凑

36、python_进阶强化
37、机器上启蒙
38、Python高效编程技巧实战
39、Python操作三不胜主流数据库
40、python分布式爬虫打造搜索引擎
41、Python
Flask 构建微电影视频网站
42、Vue+Django REST framework
打造清新电商项目
43、强力Django+杀手级Xadmin打造上线标准的在线教育平台
44、Python3.6
强力Django+杀手级Xadmin打造上线标准的在线教育平台
45、Python3
全网最热之Python3切帮派+进阶 比自学更快上手实际开支

可是Casey Muratori的文章对我扶特别要命,
我只是所以了1天时间哪怕形容来了利用ETW收集.Net运行时事件之以身作则代码.
随后我起看怎么行使LTTng收集这些事件,
按照我往之经验linux上的类库api通常会比windows的好用, 但LTTng是只章外.

46、玩转算法
47、算法和数据结构
48、玩转算法面试 leetcode
49、看得见的算法 7只经应用诠释算法精髓
50、互联网架构原版
51、Sass
基础教程

自己先是项做的事体是错过寻觅怎样在c程序里面LTTng的接口,
我打开了她们的文档然后开浏览.
很快我发觉了他们的文档只提了哪运用代码发送事件,
却没其他说明如何用代码吸纳事件, 我发觉及自家当去押源代码.

前端开发:

初始化LTTng

采用LTTng跟踪事件首先要创造一个对话, 启用事件以及加加上下文参数,
然后启用跟踪, 在命令行里面是这么的调用:

lttng create --live
lttng enable-event --userspace --tracepoint DotNETRuntime:GCStart_V2
lttng add-context --userspace --type vpid
lttng add-context --userspace --type vtid
lttng start

lttng这个命令的源代码在github及,
通过几分钟的搜寻自己发现lttng的一一命令的贯彻还是保存在本条文件夹下的.
打开create.c继而发现了创建会话调用的凡lttng_create_session函数,
lttng_create_session函数可以透过引用lttng.h调用.
再度过了几乎分钟我形容有了第一履代码

int ret = lttng_create_session_live("example-session", "net://127.0.0.1", 1000000);

运转后随即就报错了, 错误是”No session daemon is available”.
原因是lttng-sessiond这个程序没有启动,
lttng是经过一个单独服务来管理会话的, 而这个服务要手动启动.

动独立服务本身没有错, 但是lttng-sessiond夫顺序提供了不少参数,
如若一个止想跟用户事件之顺序启动了之服务并点名了忽略内核事件之参数,
然后另外一个跟内核事件的主次用不能够健康运作.
科学的做法是使systemd来启动之服务, 让系统管理员决定用什么参数,
而无是深受调用者去启动它.

缓解这题目只需要简单粗暴的少数推行, 启动时假如已起步过新进程会败,
没有任何影响:

system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));

现在lttng_create_session_live会晤回到成功了, 但是又发现了初的问题,
创办的对话是由一个单身的劳务管理之, 即使当前经过退出会话也会见存在,
第二潮创的时节会回来一个曾是的错误.
其一题目及ETW的题目同样模一样, 解决方法吗一律,
在开创会话前关闭它就是得了.

于是乎代码变成了如此:

system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));
lttng_destroy_session(SessionName);
int ret = lttng_create_session_live("example-session", "net://127.0.0.1", 1000000);

由此一段时间后, 我于是代码实现了和下令执行一样的效力:

// start processes, won't replace exists
system("lttng-sessiond --daemonize");
std::this_thread::sleep_for(std::chrono::seconds(1));

// create new session
lttng_destroy_session(SessionName);
int ret = lttng_create_session_live(SessionName, SessionUrl, LiveSessionInterval);
if (ret != 0) {
    std::cerr << "lttng_create_session: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// create handle from session
lttng_domain domain = {};
domain.type = LTTNG_DOMAIN_UST;
lttng_handle* handle = lttng_create_handle(SessionName, &domain);
if (handle == nullptr) {
    std::cerr << "lttng_create_handle: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// enable event
lttng_event event = {};
event.type = LTTNG_EVENT_TRACEPOINT;
memcpy(event.name, EventName.c_str(), EventName.size());
event.loglevel_type = LTTNG_EVENT_LOGLEVEL_ALL;
event.loglevel = -1;
ret = lttng_enable_event_with_exclusions(handle, &event, nullptr, nullptr, 0, nullptr);
if (ret < 0) {
    std::cerr << "lttng_enable_event_with_exclusions: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// add context
lttng_event_context contextPid = {};
contextPid.ctx = LTTNG_EVENT_CONTEXT_VPID;
ret = lttng_add_context(handle, &contextPid, nullptr, nullptr);
if (ret < 0) {
    std::cerr << "lttng_add_context: " << lttng_strerror(ret) << std::endl;
    return -1;
}

// start tracing
ret = lttng_start_tracing(SessionName);
if (ret < 0) {
    std::cerr << "lttng_start_tracing: " << lttng_strerror(ret) << std::endl;
    return -1;
}

到此处了是无是甚简短? 尽管从不文档, 但是这些api都是非常简单的api,
看源代码就足以想见如何调用.

52、webapp书城出
53、组件方式开 Web App全站
54、Web
App用组件方式开发都站

得到事件

于报LTTng启用跟踪后, 我还得取得发送至LTTng的风波,
在ETW中得事件是由此注册回调获取之:

EVENT_TRACE_LOGFILE trace = { };
trace.LoggerName = (char*)mySessionName.c_str();
trace.EventRecordCallback = (PEVENT_RECORD_CALLBACK)(StaticRecordEventCallback);
trace.BufferCallback = (PEVENT_TRACE_BUFFER_CALLBACK)(StaticBufferEventCallback);
trace.ProcessTraceMode = PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_REAL_TIME;
TRACEHANDLE sessionHandle = ::OpenTrace(&trace);
if (sessionHandle == INVALID_PROCESSTRACE_HANDLE) {
    // ...
}
ULONG processStatus = ::ProcessTrace(&sessionHandle, 1, nullptr, nullptr);

自己寻思lttng有无产生这么的建制,
首先我看看的凡lttng.h中的lttng_register_consumer函数,
这个函数的笺注如下:

This call registers an "outside consumer" for a session and an lttng domain.
No consumer will be spawned and all fds/commands will go through the socket path given (socket_path).

翻译出就给会话注册一个外表的买主, 听上去和我的要求特别像吧?
这函数的次只参数是一个字符串, 我想来是unix socket, lttng会通过unix
socket发送事件过来.
乃自己勾勒了这样的代码:

ret = lttng_register_consumer(handle, "/tmp/custom-consumer");

如出一辙行及时报错, 错误是Command undefined, 也尽管是令未定义,
服务端不支持此命令.
由此查找发现lttng的源代码中从未另外调用这个函数的地方,
也就是说这个函数是单装饰.
在押起是法执行未通.


透过一番寻觅,
我发现了live-reading-howto本条文档,
里面的内容非常少但足以视使用lttng-relayd这个服务好读取事件.
读取事件时只支持TCP, 使用TCP传输事件数量不仅复杂而效率很没有,
相对ETW直接通过内存传递数据这活脱脱是单愚蠢的办法.
尽管如此笨拙但是还是如果继续写, 我开看即TCP传输用底凡呀协议.

本着传输协议的说明文档在live-reading-protocol.txt,
这首文档写的那个不好, 但总比没有好.
lttng-relayd进展相互使用的是一个lttng自己创办的半双工二进制协议,
设计如下:

客户端发送命令于lttng-relayd欲遵守以下的格式

[data_size: unsigned 64 bit big endian int, 命令体大小]
[cmd: unsigned 32 bit big endian int, 命令类型]
[cmd_version: unsigned 32 bit big endian int, 命令版本]
[命令体, 大小是data_size]

出殡命令的宏图没有问题, 大部分亚上前制协议还是这样设计的,
问题在接收命令的设计.
接收命令的格式完全依赖让发送命令的门类,
例如LTTNG_VIEWER_CONNECT这令发送过去会晤接到以下的多寡:

[viewer_session_id: unsigned 64 bit big endian int, 服务端指定的会话ID]
[major: unsigned 32 bit big endian int, 大版本]
[minor: unsigned 32 bit big endian int, 中版本]
[type: 客户端的类型]

可以看接收的数据不曾数据头, 没有数据头如何控制接多少数量吧?
这就算要求客户端定义之报大小要和服务端完全一致, 一个字段都不可知漏.
服务端在事后的创新中莫可知叫返回数据随意添加字段,
返回多少字段需要在发送过来的cmd_version,
保持api的兼容性将会充分之麻烦.
目前在lttng中cmd_version举凡一个雁过拔毛字段,
也便是他俩没仔细的想过api的创新问题.
对的做法应该是返数据也应该提供一个数据头,
然后同意客户端忽略多出去的数据.


在押罢协议后, 我在思念既用了次进制协议,
应该也会见提供一个sdk来减少解析的工作量吧?
经过一番搜索找到了一个头文件lttng-viewer-abi.h,
包含了与lttng-relayd相使用的数据结构体定义.
其一腔文件于源代码里面来, 但是倒无在LTTng发布之软件包中,
这代表使用它们需要复制它到项目里面.
复制别人的源代码到花色内匪可知那么不论是,
看了瞬间LTTng的开源协议,
include/lttng/*src/lib/lttng-ctl/*产之文书是LGPL,
其余文件是GPL,
啊就是端要把这个腔文件复制到自己的色内,
自己的色要以GPL协议开始源
,
不思就此GPL的话语只能将内部的情好一行行重新描绘, 还免能够写的太像.

既是是测试就随便这样多了, 把这个腔文件之代码复制过来就开持续写,
首先是连接受lttng-relayd:

int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
    perror("socket");
    return -1;
}
sockaddr_in address = {};
address.sin_addr.s_addr = inet_addr("127.0.0.1");
address.sin_family = AF_INET;
address.sin_port = htons(5344);
ret = connect(fd, (sockaddr*)&address, sizeof(address));
if (ret < 0) {
    perror("connect");
    return -1;
}

连年成功之后的互流程在翻阅者的说道文档以后可整理如下:

初始化
    客户端发送命令 LTTNG_VIEWER_CLIENT_COMMAND + 构造体 lttng_viewer_connect
    服务端返回构造体 lttng_viewer_connect
    客户端发送命令 LTTNG_VIEWER_CREATE_SESSION + 构造体 lttng_viewer_create_session_response
    服务端返回构造体 lttng_viewer_create_session_response
列出会话
    客户端发送命令 LTTNG_VIEWER_LIST_SESSIONS, 不带构造体
    服务端返回构造体 lttng_viewer_list_sessions + 指定长度的 lttng_viewer_session
附加到会话
    客户端发送命令 LTTNG_VIEWER_ATTACH_SESSION + 构造体 lttng_viewer_attach_session_request
    服务端返回构造体 lttng_viewer_attach_session_response + 指定长度的 lttng_viewer_stream
循环 {
    如果需要获取新的流 {
        客户端发送命令 LTTNG_VIEWER_GET_NEW_STREAMS + 构造体 lttng_viewer_new_streams_request
        服务端返回构造体 lttng_viewer_new_streams_response + 指定长度的 lttng_viewer_stream
    }
    如果需要获取新的元数据(metadata) {
        枚举现存的metadata流列表 {
            客户端发送命令 LTTNG_VIEWER_GET_METADATA + 构造体 lttng_viewer_get_metadata
            服务端返回构造体 lttng_viewer_metadata_packet + 指定长度的payload
        }
    }
    枚举现存的trace流列表 {
        客户端发送命令 LTTNG_VIEWER_GET_NEXT_INDEX + 构造体 lttng_viewer_get_next_index
        服务端返回构造体 lttng_viewer_index
        检查返回的 index.flags, 如果服务端出现了新的流或者元数据, 需要先获取新的流和元数据才可以继续
        客户端发送命令 LTTNG_VIEWER_GET_PACKET + 构造体 lttng_viewer_trace_packet
        服务端返回构造体 lttng_viewer_trace_packet + 指定长度的payload
        根据metadata packet和trace packet分析事件的内容然后记录事件
    }
}

大凡匪是当十分复杂?
以商决定了服务端发给客户端的数额没有数据头,
所以服务端不可知主动推送数据及客户端, 客户端必须积极的错过进行轮询.
若你注意到建造体的称,
会发现有的构造体后面有request和response而有没,
如果不看上下文只看构造体的号很不便猜到它的作用.
是的做法是所有请求与归的布局体名称末尾都添加request和response,
不若去大概这些假名而浪费思考的时间.


为了发送命令和接收构造体我勾勒了部分支援函数, 它们并无复杂,
使用TCP交互的程序还见面时有发生相近之代码:

int sendall(int fd, const void* buf, std::size_t size) {
    std::size_t pos = 0;
    while (pos < size) {
        auto ret = send(fd,
            reinterpret_cast<const char*>(buf) + pos, size - pos, 0);
        if (ret <= 0) {
            return -1;
        }
        pos += static_cast<std::size_t>(ret);
    }
    return 0;
}

int recvall(int fd, void* buf, std::size_t size) {
    std::size_t pos = 0;
    while (pos < size) {
        auto ret = recv(fd,
            reinterpret_cast<char*>(buf) + pos, size - pos, 0);
        if (ret <= 0) {
            return -1;
        }
        pos += static_cast<std::size_t>(ret);
    }
    return 0;
}

template <class T>
int sendcmd(int fd, std::uint32_t type, const T& body) {
    lttng_viewer_cmd cmd = {};
    cmd.data_size = htobe64(sizeof(T));
    cmd.cmd = htobe32(type);
    if (sendall(fd, &cmd, sizeof(cmd)) < 0) {
        return -1;
    }
    if (sendall(fd, &body, sizeof(body)) < 0) {
        return -1;
    }
    return 0;
}

初始化连接的代码如下:

lttng_viewer_connect body = {};
body.major = htobe32(2);
body.minor = htobe32(9);
body.type = htobe32(LTTNG_VIEWER_CLIENT_COMMAND);
if (sendcmd(fd, LTTNG_VIEWER_CONNECT, body) < 0) {
    return -1;
}
if (recvall(fd, &body, sizeof(body)) < 0) {
    return -1;
}
viewer_session_id = be64toh(body.viewer_session_id);

末端的代码比较单调我虽大概了,
想看完整代码的可以看这里.


进入循环后会见起lttng-relayd收获两栽中的多寡:

  • 头数据(metadata), 定义了跟数据的格式
  • 跟数据(trace), 包含了风波信息例如GC开始与了结等

取得元数据应用的是LTTNG_VIEWER_GET_METADATA命令,
获取到的首批数据内容如下:

Wu@"Jtf@oe/* CTF 1.8 */

typealias integer { size = 8; align = 8; signed = false; } := uint8_t;
typealias integer { size = 16; align = 8; signed = false; } := uint16_t;
typealias integer { size = 32; align = 8; signed = false; } := uint32_t;
typealias integer { size = 64; align = 8; signed = false; } := uint64_t;
typealias integer { size = 64; align = 8; signed = false; } := unsigned long;
typealias integer { size = 5; align = 1; signed = false; } := uint5_t;
typealias integer { size = 27; align = 1; signed = false; } := uint27_t;

trace {
    major = 1;
    minor = 8;
    uuid = "a3df4090-0722-4a74-97a4-81e066406f03";
    byte_order = le;
    packet.header := struct {
        uint32_t magic;
        uint8_t  uuid[16];
        uint32_t stream_id;
        uint64_t stream_instance_id;
    };
};

env {
    hostname = "ubuntu-virtual-machine";
    domain = "ust";
    tracer_name = "lttng-ust";
    tracer_major = 2;
    tracer_minor = 9;
};

clock {
    name = "monotonic";
    uuid = "f397e532-4837-402b-8cc9-700ed92a339d";
    description = "Monotonic Clock";
    freq = 1000000000; /* Frequency, in Hz */
    /* clock value offset from Epoch is: offset * (1/freq) */
    offset = 1514336042565610080;
};

typealias integer {
    size = 27; align = 1; signed = false;
    map = clock.monotonic.value;
} := uint27_clock_monotonic_t;

typealias integer {
    size = 32; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint32_clock_monotonic_t;

typealias integer {
    size = 64; align = 8; signed = false;
    map = clock.monotonic.value;
} := uint64_clock_monotonic_t;

struct packet_context {
    uint64_clock_monotonic_t timestamp_begin;
    uint64_clock_monotonic_t timestamp_end;
    uint64_t content_size;
    uint64_t packet_size;
    uint64_t packet_seq_num;
    unsigned long events_discarded;
    uint32_t cpu_id;
};

struct event_header_compact {
    enum : uint5_t { compact = 0 ... 30, extended = 31 } id;
    variant <id> {
        struct {
            uint27_clock_monotonic_t timestamp;
        } compact;
        struct {
            uint32_t id;
            uint64_clock_monotonic_t timestamp;
        } extended;
    } v;
} align(8);

struct event_header_large {
    enum : uint16_t { compact = 0 ... 65534, extended = 65535 } id;
    variant <id> {
        struct {
            uint32_clock_monotonic_t timestamp;
        } compact;
        struct {
            uint32_t id;
            uint64_clock_monotonic_t timestamp;
        } extended;
    } v;
} align(8);

stream {
    id = 0;
    event.header := struct event_header_compact;
    packet.context := struct packet_context;
    event.context := struct {
        integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vpid;
        integer { size = 32; align = 8; signed = 1; encoding = none; base = 10; } _vtid;
    };
};

event {
    name = "DotNETRuntime:GCStart_V2";
    id = 0;
    stream_id = 0;
    loglevel = 13;
    fields := struct {
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Count;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Depth;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Reason;
        integer { size = 32; align = 8; signed = 0; encoding = none; base = 10; } _Type;
        integer { size = 16; align = 8; signed = 0; encoding = none; base = 10; } _ClrInstanceID;
        integer { size = 64; align = 8; signed = 0; encoding = none; base = 10; } _ClientSequenceNumber;
    };
};

斯第一数据的格式是CTF Metadata,
这个格式看上去像json但是并无是, 是LTTng的店温馨创建的一个文本格式.
babeltrace受含了解析这个文本格式的代码,
但是没有开任何解析其的接口,
也尽管是如果您想协调分析只好写一个词法分析器.
这些格式其实可以应用json表示, 体积不见面多多少,
但是及时局就是发明了一个初的格式增加使用者的负担.
形容一个词法分析器需要1上时间以及1000行代码, 这里我便先行跳了了.


紧接下去获取跟踪数据,
使用的是LTTNG_VIEWER_GET_NEXT_INDEX和LTTNG_VIEWER_GET_PACKET命令.
LTTNG_VIEWER_GET_NEXT_INDEX返回了脚下流动的offset和而得之content_size,
这里的content_size单位是个(bit),
也就是内需除以8才足以算出可以拿走多少字节,
关于content_size的单位LTTng中从未任何文档和注释说明其是个,
只来一个测试代码里面的某行写了/ CHAR_BIT.
使用LTTNG_VIEWER_GET_PACKET命令,
传入offset和content_size/8可以获取跟踪数据(如果无/8碰头沾到剩余的数码要返回ERR).
实际上返回的跟踪数据如下:

000000: c1 1f fc c1 29 82 6b fe 24 10 4c 6b 97 91 4d c3  ....).k.$.Lk..M.
000010: ed d4 41 8f 00 00 00 00 03 00 00 00 00 00 00 00  ..A.............
000020: 92 91 49 96 08 0a 00 00 07 a0 58 b9 08 0a 00 00  ..I.......X.....
000030: 50 05 00 00 00 00 00 00 00 80 00 00 00 00 00 00  P...............
000040: 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
000050: 03 00 00 00 1f 00 00 00 00 92 91 49 96 08 0a 00  ...........I....
000060: 00 e1 1b 00 00 03 00 00 00 02 00 00 00 01 00 00  ................
000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 1f  ................
000080: 00 00 00 00 4d ae a7 af 08 0a 00 00 e1 1b 00 00  ....M...........
000090: 04 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00  ................
0000a0: 00 00 00 00 00 00 00 00 00 00                    ..........

跟数据的格式是CTF Stream
Packet,
也是一个自定义的老二迈入制格式, 需要配合元数据解析.
babeltrace遭受一样没有开解析其的接口(有python
binding但是没有解析数据的函数), 也就是得协调写二前进制数据解析器.

操作LTTng + 和relayd通讯 + 元数据词法分析器 +
跟踪数据解析器全部加以起预计得2000行代码,
而这总体使ETW只所以了100大抵行代码.
坏的统筹, 复杂的采用, 落后的文档, 各种各样的自定义协议和数目格式,
不提供SDK把LTTng打招了一个比ETW更难用的跟系统.
当下在github上LTTng只生100几近星而babeltrace只来20大多,
也说明了从未多少人以用它们.
本人未了解为什么CoreCLR要为此LTTng, 但欣慰之是CoreCLR
2.1见面出新的跟踪机制EventPipe,
到下可又简约的贯彻超过平台捕获CoreCLR跟踪事件.

本身手上勾勒的调用ETW的代码放在了这里,
调用LTTng的代码放在了这里,
有趣味之可以错过参考.

55、vue2.0智能社
56、vue.js零基础
57、全网稀缺Vue 2.0高等实战
独立开发专属音乐WebAPP
58、Vue、Node、MongoDB高级技术栈全覆盖
59、vue2.0带动你抱门Vue 2.0和案例开发
60、Vue2.0+Node.js+MongoDB 打造商城系统
61、Vue.js高仿饿了么外卖App 前端框架Vue.js
1.0升任2.0
62、Vue+Django REST framework
打造清新电商项目

教训

极端差之API(ETW)和还不比之API(LTTng)都扣留了了, 那么该如何避免他们之错误,
编写一个吓之API呢?

Casey
Muratori关联的教训有:

63、node.JS
线上服务器部署
64、Node.js微信公众号开
65、NodeJs实战+mongodb+聊天系统
66、全栈最后一公里 – Nodejs
项目之丝及服务器部署与公布

设计API的率先条以及亚久规则: “永远都由编写用例开始”

筹一个API时, 首先使做的凡立在调用者的立场, 想想调用者需要什么,
如何才会顶简便易行的达这个需求.
编排一个简易的用例代码永远是设计API中必的同步.
不用过多之失思内部贯彻, 如果内部贯彻机制让API变得复杂,
应该想办法去抽象它.

67、React框架课程套装
68、贯穿全栈React Native开发App
69、React.js大众点评案例完整版本
70、React
Native开发过平台Github App
71、React
Native开发App狗狗说
72、React
native 快速开发App
73、React.js入门与实战
开发适配PC端及移动端新闻头长条平台

设想到未来的壮大

为需求会不断变化, 设计API的时光应该吗未来的转变预留空间,
保证为后相当性.
例如ETW中监听的轩然大波类.aspx)使用了位标记,
也尽管是参数是32员时最多只能发出32种事件,
考虑到未来生更多事件应该把事件类型定义为总是的数值并提供额外的API启用事件.
现在出许多接口在统筹时会见考虑到本, 例如用v1和v2区分,
这是一个大好之策略.

74、ionic2飞速上手的跨平台App开发
75、Angular单页应用 仿拉钩
76、AngularJS全栈开发知乎
77、Angular
打造企业级协作平台  288
78、Angular
4.0自入门到实战 打造股票管理网站  199

引人注目接口的输入和出口

不用为省去代码去让一个接口接收或者返回多余的信息.
以ETW中众接口都同用了一个大构造体EVENT_TRACE_PROPERTIES,
调用者很麻烦下手明白接口使用了建筑造体里面的什么样值, 又影响了哪些值.
计划API时该明了接口的目的, 让接口接收及归必要且极其少的信息.

79、前端小白入门课程
80、前端
所向披靡的响应式开发
81、响应式开发同招给强
82、前端跳槽面试必备技巧
83、JavaScript面试技巧全套
84、ES6碎片基础教学解析彩票品类
85、手把手从0打过去企业级电商平台
86、腾讯大牛教你web前后端漏洞分析与防卫
87、对连片真实数据
从0开发前后端分离企业级及线项目
88、Javascript 让你页面速度出乎意料起来 –
Web前端性能优化

供整体的示范代码

本着调用者来说, 100推行的言传身教代码通常比1000履行之文档更有意义.
为接口的设计者和调用者拥有的知识量通常不对等,
调用者在没有看出实际的例证之前, 很可能无法了解设计者编写的文档.

移步端支出:

不要动魔法数字

随即是众多接口都会犯的左, 例如ETW中控制事件附加的音经常, 1表示时间戳,
2表示系统时, 3代表CPU周期计数.
倘您用传递具有某种意义的数字让接口,
请务必于SDK中为该数字定义枚举类型.

自我自LTTng中收到的训有:

89、贯穿全栈React Native开发App
90、React
Native开发过平台Github App
91、React
native 快速支付轻量级App

写文档

99%底调用者没有看源代码的兴味或能力,
不写文档没有丁会见分晓如何错过调整用你的接口.
本发生无数自动生成文档的工具, 用这些家伙得以减掉过多底工作量,
但是你照样当手动去编写一个入门的文档.

92、零基础入门安卓和界面
93、基于okhttp 3 的 Android
网络层架构设计实战
94、带领新手快速开发Android App完整版本
95、Kotlin系统入门与进阶
96、Android自动化测试-java篇
97、Android专项测试-Python篇
98、Android通用框架设计与整体电商APP开发
99、Android应用发展趋势必备武器
热修复及插件化
100、Android常用框架教程Retrofit2 OKhttp3 Dagger2
RxJava2

毫无任意之失去创造一个磋商

始建一个初的说道表示要编制新的代码去分析其,
而且每个程序语言都要双重编排一不好.
只有您十分有活力, 可以吧主流的程序语言都提供一个SDK, 否则未引进这样做.
过剩项目都提供了REST API, 这是不行好的来头,
因为几每个语言都发备的类库可以好地调用REST API.

数据库:

小心之去定义二进制协议

概念一个好之二进制协议要好老的功, LTTng定义的商谈明显考虑的极端少.
推荐的做法是不言而喻区分请求和报, 请求和回应都应有发一个富含长度的头,
支持都双工通信.
使您想设计一个二进制协议,
强烈建议参考Cassandra数据库的共谋文档,
这个协议无论是设计要文档都是甲级的水平.
但只要您没有针对传输性能有很苛刻的求, 建议利用现成的说道加json或者xml.

103、MySQL性能管理及架构设计
104、打造扛得住的MySQL数据库架构
105、Redis从入门到高可用,分布式实践
106、高性能MySql 可扩大MySQL数据库设计以及架构优化
电商项目

免设错过创造一个DSL(Domain-specific language)

这里我从没写轻易, 如果你发出一个数据结构需要代表成文本,
请使用重复通用的格式.
LTTng表示正数据时采用了一个要好创立的DSL,
但里面的情节用json表示也非会见多多少体积,
也就是说创造一个DSL没有另外好处.
剖析DSL需要好编写词法分析器,
即使是更老到的程序员编写一个乎用多多年华(包含单元测试更多),
如果使用json等通用格式那么编写解析的代码只待几分钟.

Linux:

写于最后

虽然就篇稿子将LTTng批评了同样海,
但这或是当前环球唯一一篇涉嫌如何通过代码调用LTTng和吸收事件数量的文章.
但愿看罢就篇稿子的筹划API时差不多吧调用者着想,
你偷懒省下几分钟数会促成别人浪费几龙的时间.

107、快速直达手Linux 玩转典型以
108、Nginx
企业级刚用Nginx入门

人工智能/大数额:

109、深度上
110、机器上
111、10钟头入门大数量
112、Hadoop大数目零基础实战培训课唯一高清完版本第一季

详情请 咨询  QQ    759104513