SkyWalking APM作为服务遥测的关键技术点,为了能够更好地运用这项技术,我们需要拥有把握这项技术的底层能力。目前公司在PHP领域存活不少业务系统,针对PHP领域的APM技术,我们首先从分析这款PHP内核扩展程序下手。
一. 总体架构
PHP内核在php-fpm运行模式下是短生命周期,短生命周期的脚本运行如果直接连接SkyWalking的oap-server会造成大量的性能损耗,而且php也不擅长grpc通信,因此借助mesh架构思想为PHP-FPM进程池增加一个数据SideCar,主要的结构如下图所示:从上图可以看出,PHP内核扩展程序拦截内核运行数据(主要是关键的外部IO调用)、数据被发送给SideCar,SideCar流转数据到SkyWalking-Server,数据流还可以被SkyWalking进行分析、从而通过WebHook流转报警时间到相关后续平台里。
二. PHP内核扩展源码分析
针对目前开源社区的SkyWalking-PHP内核源码进行分析,源码的分析主要包括以下几部分:
- 工程结构分析
- 关键生命周期分析
- 关键运行函数Hook分析
2.1 工程结构分析
SkyWalking PHP内核组件工程结构比较简单,主要是站在PHP内核基础上进行扩展设计与实现,主要包含的扩展文件有:
- b64.h:base64编码函数的头文件、主要包含内存分配、base64字符表、b64_encode及b64_decode、b64_decode_ex的函数声明。
- decode.c:base64序列化的函数具体实现。
- encode.c:base64反序列化的函数具体实现。
- components.h:针对skywalking协议中的component部分进行宏定义、这部分是apm协议的一部分,例如:tomcat、httpclient、dubbo、okhttp、grpc、jedis、更多查看附录1。
- php_skywalking.h:关键的内核扩展声明部分,主要包括:APM协议宏定义、Redis指令类别、memcache指令类别、ContextCarrier上下文结构体、apm拦截所需的关键函数定义(具体见附录二),apm关键函数hook定义(具体见附录三),全局变量定义(具体见附录四)。
- skywalking.c:具体内核扩展实现文件,里面包含了MI-MS、RI-RS、关键函数Hook等处理逻辑。
2.2 关键生命周期分析
这块将针对内核扩展关键生命周期进行分析。
2.2.1 关键生命期函数Hook定义
1static void (*ori_execute_ex)(zend_execute_data *execute_data); //PHP内核原始PHP层执行流程函数指针
2static void (*ori_execute_internal)(zend_execute_data *execute_data, zval *return_value);//PHP原始内核执行函数指针
3ZEND_API void sky_execute_ex(zend_execute_data *execute_data);//skywalking针对PHP层执行函数的替换指针
4ZEND_API void sky_execute_internal(zend_execute_data *execute_data, zval *return_value);//skywalking针对原始内核执行函数的替换指针
2.2.2 php.ini配置解析周期
1PHP_INI_BEGIN()
2#if SKY_DEBUG
3 STD_PHP_INI_BOOLEAN("skywalking.enable", "1", PHP_INI_ALL, OnUpdateBool, enable, zend_skywalking_globals, skywalking_globals) //读取skywalking.enable配置项
4#else
5 STD_PHP_INI_BOOLEAN("skywalking.enable", "0", PHP_INI_ALL, OnUpdateBool, enable, zend_skywalking_globals, skywalking_globals) //读取skywalking.enable配置项
6#endif
7 STD_PHP_INI_ENTRY("skywalking.version", "8", PHP_INI_ALL, OnUpdateLong, version, zend_skywalking_globals, skywalking_globals) //读取skywalking 版本配置项
8 STD_PHP_INI_ENTRY("skywalking.app_code", UNKNOW_SERVICE_NAME, PHP_INI_ALL, OnUpdateString, app_code, zend_skywalking_globals, skywalking_globals) //读取微服务-服务名配置项
9 STD_PHP_INI_ENTRY("skywalking.app_code_env_key", "APM_APP_CODE", PHP_INI_ALL, OnUpdateString, app_code_env_key, zend_skywalking_globals, skywalking_globals) //读取微服务-服务名环境变量 配置项
10 STD_PHP_INI_ENTRY("skywalking.sock_path", "/tmp/sky-agent.sock", PHP_INI_ALL, OnUpdateString, sock_path, zend_skywalking_globals, skywalking_globals) //读取微服务-agent通信unix路径配置项
11PHP_INI_END()
2.2.3 PHP内核模块初始化周期 - MI周期
MI周期是PHP进程的模块加载周期,这个周期内部会逐个加载内核扩展,并调用内核扩展MI周期的函数Hook,APM内核扩展对于这块的处理逻辑如下:
1/* {{{ PHP_MINIT_FUNCTION
2 */
3PHP_MINIT_FUNCTION (skywalking) {
4 ZEND_INIT_MODULE_GLOBALS(skywalking, php_skywalking_init_globals, NULL); //初始化模块变量
5 //data_register_hashtable();
6 REGISTER_INI_ENTRIES();
7 /* If you have INI entries, uncomment these lines
8 */
9 if (SKYWALKING_G(enable)) { //如果内核扩展功能开启了,则进行关键内核函数指针替换。
10 //屏蔽php的cli运行模式的APM监控
11 if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
12 return SUCCESS;
13 }
14
15 // 用户自定义函数执行器(php脚本定义的类、函数)
16 ori_execute_ex = zend_execute_ex;
17 zend_execute_ex = sky_execute_ex;
18
19 // 内部函数执行器(c语言定义的类、函数)
20 ori_execute_internal = zend_execute_internal;
21 zend_execute_internal = sky_execute_internal;
22
23 // 托管curl内核函数:从zend函数表寻找内核函数、并使用内部的函数钩子进行拦截:自定函数钩子->exec、setopt、setopt_array、close
24 zend_function *old_function;
25 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_exec", sizeof("curl_exec") - 1)) != NULL) { //替换curl_exec函数
26 orig_curl_exec = old_function->internal_function.handler;
27 old_function->internal_function.handler = sky_curl_exec_handler;
28 }
29 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_setopt", sizeof("curl_setopt")-1)) != NULL) { //替换curl_setopt函数
30 orig_curl_setopt = old_function->internal_function.handler;
31 old_function->internal_function.handler = sky_curl_setopt_handler;
32 }
33 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_setopt_array", sizeof("curl_setopt_array")-1)) != NULL) { //替换curl_setopt_array函数
34 orig_curl_setopt_array = old_function->internal_function.handler;
35 old_function->internal_function.handler = sky_curl_setopt_array_handler;
36 }
37 if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_close", sizeof("curl_close")-1)) != NULL) { //替换curl_close函数
38 orig_curl_close = old_function->internal_function.handler;
39 old_function->internal_function.handler = sky_curl_close_handler;
40 }
41 }
42
43 return SUCCESS;
44}
45/* }}} */
2.2.4 PHP内核请求初始化阶段 - RI周期
这个周期是PHP进程针对每次PHP请求入口进行的周期设计,RI周期会针对每次PHP请求均会执行,也是PHP-FPM模式的短生命周期的根源,针对这块源码的解读如下:
1/* Remove if there's nothing to do at request start */
2/* {{{ PHP_RINIT_FUNCTION7
3 */
4PHP_RINIT_FUNCTION(skywalking)
5{
6#if defined(COMPILE_DL_SKYWALKING) && defined(ZTS)
7 ZEND_TSRMLS_CACHE_UPDATE();
8#endif
9 if (SKYWALKING_G(enable)) {
10 if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
11 return SUCCESS;
12 }
13 sky_register(); //向APM-Agent进行服务注册逻辑,这个逻辑仅仅当application_instance == 0条件下执行,
14 if (application_instance == 0) {
15 return SUCCESS;
16 }
17 sky_increment_id++; //因为PHP-FPM进程是线程安全的,所以此处可以单调的递加,变量主要用于构建Trace-Id
18 if (sky_increment_id >= 9999) {
19 sky_increment_id = 0;
20 }
21 request_init(); //初始化当前请求的APM数据,里面进行初始上下文、创建Trace、Span信息,及必要的父子Trace关系
22 }
23 return SUCCESS;
24}
25/* }}} */
2.2.5 PHP内核请求结束阶段 - RS周期
这个周期是PHP进程针对每次PHP请求结束进行的周期设计,RS周期会针对每次PHP请求的结束进行拦截,这个阶段一般进行内核资源回收操作:
1/* Remove if there's nothing to do at request end */
2/* {{{ PHP_RSHUTDOWN_FUNCTION
3 */
4PHP_RSHUTDOWN_FUNCTION(skywalking)
5{
6
7 if(SKYWALKING_G(enable)){
8 if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
9 return SUCCESS;
10 }
11 if (application_instance == 0) {
12 return SUCCESS;
13 }
14 sky_flush_all();//发送APM数据给Agent程序
15 zval_dtor(&SKYWALKING_G(context)); //析构context hashtable、释放内存
16 zval_dtor(&SKYWALKING_G(curl_header));//析构curl_header hashtable、释放内存
17 zval_dtor(&SKYWALKING_G(curl_header_send));//析构curl_header_send hashtable、释放内存
18 zval_dtor(&SKYWALKING_G(UpstreamSegment));//析构UpstreamSegment hashtable、释放内存
19 }
20 return SUCCESS;
21}
22/* }}} */
三. 关键运行函数Hook分析
除了PHP生命期的关键环节,APM主要就是针对关键IO进行监控,因为这些IO是外部调用依赖的关键,接下来会分析不同的函数Hook的具体实现:
3.1 PHP用户态函数拦截
APM内核组件会对用户态函数进行执行拦截,通过拦截执行过程并构建APM需要的数据。
1/* PHP 内核函数执行相关的核心内核数据结构
2 struct _zend_execute_data {
3 const zend_op *opline; executed opline
4 zend_execute_data *call; current call
5 zval *return_value;
6 zend_function *func; executed function
7 zval This; this + call_info + num_args
8 zend_execute_data *prev_execute_data;
9 zend_array *symbol_table;
10 #if ZEND_EX_USE_RUN_TIME_CACHE
11 void **run_time_cache; cache op_array->run_time_cache
12 #endif
13 #if ZEND_EX_USE_LITERALS
14 zval *literals; cache op_array->literals
15 #endif
16 }
17*/
18// sky-apm 内核扩展注入的函数执行钩子 : 用户态函数拦截
19ZEND_API void sky_execute_ex(zend_execute_data *execute_data) {
20 if (application_instance == 0) { //如果当前请求周期不需要APM采集,则直接走原内核函数钩子
21 ori_execute_ex(execute_data);
22 return;
23 }
24
25 zend_function *zf = execute_data->func; //拦截执行的函数
26 const char *class_name = (zf->common.scope != NULL && zf->common.scope->name != NULL) ? ZSTR_VAL(
27 zf->common.scope->name) : NULL; //获取class名称
28 const char *function_name = zf->common.function_name == NULL ? NULL : ZSTR_VAL(zf->common.function_name); //获取function name
29
30 char *operationName = NULL; //定义操作名称
31 char *peer = NULL;
32 int componentId = 0; //组件Id
33 if (class_name != NULL) {
34 if (strcmp(class_name, "Predis\\Client") == 0 && strcmp(function_name, "executeCommand") == 0) {
35 // 检测predis组件的executeCommand函数
36 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
37 if (arg_count) {
38 zval *p = ZEND_CALL_ARG(execute_data, 1);
39
40 zval *id = (zval *) emalloc(sizeof(zval));
41 zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getid"), id, 0, NULL, NULL);
42
43 if (Z_TYPE_P(id) == IS_STRING) { //构建predis函数类->函数方法的 操作名。
44 operationName = (char *) emalloc(strlen(class_name) + strlen(Z_STRVAL_P(id)) + 3);
45 componentId = COMPONENT_JEDIS;
46 strcpy(operationName, class_name);
47 strcat(operationName, "->");
48 strcat(operationName, Z_STRVAL_P(id));
49 }
50 efree(id);
51 }
52 } else if (strcmp(class_name, "Grpc\\BaseStub") == 0) { //拦截Grpc组件的关键方法。
53 if (strcmp(function_name, "_simpleRequest") == 0
54 || strcmp(function_name, "_clientStreamRequest") == 0
55 || strcmp(function_name, "_serverStreamRequest") == 0
56 || strcmp(function_name, "_bidiRequest") == 0
57 ) {
58 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
59 if (SKYWALKING_G(version) == 5) {
60 componentId = COMPONENT_GRPC;
61 } else {
62 componentId = COMPONENT_RPC;
63 }
64 strcpy(operationName, class_name);
65 strcat(operationName, "->");
66 strcat(operationName, function_name);
67 }
68 }
69 }
70
71 if (operationName != NULL) { //如果操作名称不为空,证明成功拦截到函数
72 zval tags;
73 array_init(&tags);
74
75 //构建APM的tags属性,增加数据库类型、客户端组件类型
76 if (strcmp(class_name, "Predis\\Client") == 0 && strcmp(function_name, "executeCommand") == 0) {
77 add_assoc_string(&tags, "db.type", "redis");
78 add_assoc_string(&tags, "component", "predis");
79 zval *p = ZEND_CALL_ARG(execute_data, 1);
80 zval *id = (zval *) emalloc(sizeof(zval));
81 zval *arguments = (zval *) emalloc(sizeof(zval));
82 zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getid"), id, 0, NULL, NULL);
83 zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getarguments"), arguments, 0, NULL, NULL);
84
85 // 丰富APM的predis的连接peer信息
86 zval *connection = sky_read_property(&(execute_data->This),"connection", 0);
87 if (connection != NULL && Z_TYPE_P(connection) == IS_OBJECT && strcmp(sky_get_class_name(connection), "Predis\\Connection\\StreamConnection") == 0) {
88 zval *parameters = sky_read_property(connection, "parameters", 0);
89 if (parameters != NULL && Z_TYPE_P(parameters) == IS_OBJECT && strcmp(sky_get_class_name(parameters), "Predis\\Connection\\Parameters") == 0) {
90 zval *parameters_arr = sky_read_property(parameters, "parameters", 0);
91 if (Z_TYPE_P(parameters_arr) == IS_ARRAY) {
92 zval *predis_host = zend_hash_str_find(Z_ARRVAL_P(parameters_arr), "host", sizeof("host") - 1);
93 zval *predis_port = zend_hash_str_find(Z_ARRVAL_P(parameters_arr), "port", sizeof("port") - 1);
94 zval *port;
95 ZVAL_COPY(&port, predis_port);
96 if (Z_TYPE_P(port) != IS_LONG) {
97 convert_to_long(port);
98 }
99
100 if (Z_TYPE_P(predis_host) == IS_STRING && Z_TYPE_P(port) == IS_LONG) {
101 const char *host = ZSTR_VAL(Z_STR_P(predis_host));
102 peer = (char *) emalloc(strlen(host) + 10);
103 bzero(peer, strlen(host) + 10);
104 sprintf(peer, "%s:%" PRId3264, host, Z_LVAL_P(port));
105 }
106 }
107 }
108 }
109 // peer end
110
111 if (Z_TYPE_P(arguments) == IS_ARRAY) {
112 zend_ulong num_key;
113 zval *entry, str_entry;
114 smart_str command = {0};
115 smart_str_appends(&command, Z_STRVAL_P(id));
116 smart_str_appends(&command, " ");
117 ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(arguments), num_key, entry)
118 {
119 switch (Z_TYPE_P(entry)) {
120 case IS_STRING:
121 smart_str_appends(&command, Z_STRVAL_P(entry));
122 smart_str_appends(&command, " ");
123 break;
124 case IS_ARRAY:
125 break;
126 default:
127 ZVAL_COPY(&str_entry, entry);
128 convert_to_string(&str_entry);
129 smart_str_appends(&command, Z_STRVAL_P(&str_entry));
130 smart_str_appends(&command, " ");
131 break;
132 }
133 }
134 ZEND_HASH_FOREACH_END();
135
136 // store command to tags
137 if (command.s) {
138 smart_str_0(&command);
139 add_assoc_string(&tags, "redis.command", ZSTR_VAL(command.s));
140 smart_str_free(&command);
141 }
142 }
143 zval_ptr_dtor(id);
144 zval_ptr_dtor(arguments);
145 efree(id);
146 efree(arguments);
147 } else if (strcmp(class_name, "Grpc\\BaseStub") == 0) {
148 add_assoc_string(&tags, "rpc.type", "grpc");
149 add_assoc_string(&tags, "component", "php-grpc");
150 zval *p = ZEND_CALL_ARG(execute_data, 1);
151 if (Z_TYPE_P(p) == IS_STRING) {
152 add_assoc_string(&tags, "rpc.method", Z_STRVAL_P(p));
153 }
154
155 zval *hostname = sky_read_property(&(execute_data->This), "hostname", 1);
156 zval *hostname_override = sky_read_property(&(execute_data->This), "hostname_override", 1);
157
158 const char *host = NULL;
159 if (hostname_override != NULL && Z_TYPE_P(hostname_override) == IS_STRING) {
160 host = ZSTR_VAL(Z_STR_P(hostname_override));
161 } else if (hostname != NULL && Z_TYPE_P(hostname) == IS_STRING) {
162 host = ZSTR_VAL(Z_STR_P(hostname));
163 }
164
165 if (host != NULL) {
166 peer = (char *) emalloc(strlen(host) + 10);
167 bzero(peer, strlen(host) + 10);
168 sprintf(peer, "%s", host);
169 }
170 }
171
172 zval temp;
173 zval *spans = NULL;
174 zval *span_id = NULL;
175 zval *last_span = NULL;
176 char *l_millisecond;
177 long millisecond;
178 array_init(&temp);
179 spans = get_spans();
180 last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1);
181 span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
182
183 add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
184 add_assoc_long(&temp, "parentSpanId", 0);
185 l_millisecond = get_millisecond();
186 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
187 efree(l_millisecond);
188 add_assoc_long(&temp, "startTime", millisecond);
189 add_assoc_long(&temp, "spanType", 1);
190 add_assoc_long(&temp, "spanLayer", 1);
191 add_assoc_long(&temp, "componentId", componentId);
192 add_assoc_string(&temp, "operationName", operationName);
193 add_assoc_string(&temp, "peer", peer == NULL ? "" : peer);
194 efree(operationName);
195 if (peer != NULL) {
196 efree(peer);
197 }
198
199 ori_execute_ex(execute_data); //前面已经注入了APM原始数据、正式执行原函数钩子
200
201 l_millisecond = get_millisecond();
202 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
203 efree(l_millisecond);
204
205 add_assoc_zval(&temp, "tags", &tags); //执行完后,注入请求监控的APM-tags
206 add_assoc_long(&temp, "endTime", millisecond);
207 add_assoc_long(&temp, "isError", 0);
208
209 zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
210 } else {
211 ori_execute_ex(execute_data);
212 }
213}
3.2 PHP内核态函数拦截
APM内核组件会对PHP内核态函数进行执行拦截,通过拦截执行过程并构建APM需要的数据,相关源码解释如下:
1//PHP非用户态函数拦截:例如PHP内核、PHP其他内核扩展
2ZEND_API void sky_execute_internal(zend_execute_data *execute_data, zval *return_value) {
3
4 if (application_instance == 0) {
5 if (ori_execute_internal) {
6 ori_execute_internal(execute_data, return_value);
7 } else {
8 execute_internal(execute_data, return_value);
9 }
10 return;
11 }
12
13 zend_function *zf = execute_data->func;
14 const char *class_name = (zf->common.scope != NULL && zf->common.scope->name != NULL) ? ZSTR_VAL(
15 zf->common.scope->name) : NULL; //获取函数所属的class名称
16 const char *function_name = zf->common.function_name == NULL ? NULL : ZSTR_VAL(zf->common.function_name);//获取函数名称
17 int is_procedural_mysqli = 0; // "Procedural style" or "Object oriented style" ?
18 char *operationName = NULL;
19 char *peer = NULL;
20 char *component = NULL;
21 int componentId = COMPONENT_MYSQL_JDBC_DRIVER;
22 if (class_name != NULL) {
23 if (strcmp(class_name, "PDO") == 0) { //拦截PDO类
24 if (strcmp(function_name, "exec") == 0
25 || strcmp(function_name, "query") == 0
26 || strcmp(function_name, "prepare") == 0
27 || strcmp(function_name, "commit") == 0) {
28 component = (char *) emalloc(strlen("PDO") + 1);
29 strcpy(component, "PDO");
30 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3); //构建PDO操作名称
31 strcpy(operationName, class_name);
32 strcat(operationName, "->");
33 strcat(operationName, function_name);
34 }
35 } else if (strcmp(class_name, "PDOStatement") == 0) { //拦截PDOStatement类操作
36 if (strcmp(function_name, "execute") == 0) {
37 component = (char *) emalloc(strlen("PDOStatement") + 1);
38 strcpy(component, "PDOStatement");
39 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3); //构建PDOStatement操作名称
40 strcpy(operationName, class_name);
41 strcat(operationName, "->");
42 strcat(operationName, function_name);
43 }
44 } else if (strcmp(class_name, "mysqli") == 0) {//拦截mysqli类操作
45 if (strcmp(function_name, "query") == 0) {
46 component = (char *) emalloc(strlen("mysqli") + 1);
47 strcpy(component, "mysqli");
48 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
49 strcpy(operationName, class_name);
50 strcat(operationName, "->");
51 strcat(operationName, function_name);
52 }
53 } else if (strcmp(class_name, "Yar_Client") == 0) { //拦截Yar_Client类操作
54 if (strcmp(function_name, "__call") == 0) {
55 if (SKYWALKING_G(version) == 5) {
56 componentId = COMPONENT_GRPC;
57 } else {
58 componentId = COMPONENT_RPC;
59 }
60
61 component = (char *) emalloc(strlen("Yar_Client") + 1);
62 strcpy(component, "Yar_Client");
63 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
64 if (arg_count) {
65 zval *p = ZEND_CALL_ARG(execute_data, 1);
66 if (Z_TYPE_P(p) == IS_STRING) {
67 operationName = (char *) emalloc(strlen(class_name) + strlen(Z_STRVAL_P(p)) + 3);
68 strcpy(operationName, class_name);
69 strcat(operationName, "->");
70 strcat(operationName, Z_STRVAL_P(p));
71 }
72 }
73 }
74 } else if (strcmp(class_name, "Redis") == 0 || strcmp(class_name, "RedisCluster") == 0) { //拦截phpredis内核组件
75 char *fnamewall = sky_redis_fnamewall(function_name);
76 if (sky_redis_opt_for_string_key(fnamewall) == 1) {
77 componentId = COMPONENT_JEDIS;
78 component = (char *) emalloc(strlen("Redis") + 1);
79 strcpy(component, "Redis");
80 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
81 strcpy(operationName, class_name);
82 strcat(operationName, "->");
83 strcat(operationName, function_name);
84 }
85 efree(fnamewall);
86 } else if (strcmp(class_name, "Memcached") == 0) {//拦截Memcached内核组件
87 char *fnamewall = sky_memcached_fnamewall(function_name);
88 if (sky_memcached_opt_for_string_key(fnamewall) == 1) {
89 componentId = COMPONENT_XMEMCACHED;
90 component = (char *) emalloc(strlen("memcached") + 1);
91 strcpy(component, "memcached");
92 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
93 strcpy(operationName, class_name);
94 strcat(operationName, "->");
95 strcat(operationName, function_name);
96 }
97 efree(fnamewall);
98 }
99 } else if (function_name != NULL) {
100 if (strcmp(function_name, "mysqli_query") == 0) {//拦截mysqli_query函数
101 class_name = "mysqli";
102 function_name = "query";
103 component = (char *) emalloc(strlen(class_name) + 1);
104 strcpy(component, class_name);
105 operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
106 strcpy(operationName, class_name);
107 strcat(operationName, "->");
108 strcat(operationName, function_name);
109
110 is_procedural_mysqli = 1;
111 }
112 }
113
114 //如果在控制范围内的函数行为
115 if (operationName != NULL) {
116
117 zval tags;
118 array_init(&tags);
119
120 if (strcmp(class_name, "PDO") == 0) {//丰富PDO span tags
121 add_assoc_string(&tags, "component", "PHP-PDO");
122
123 // params
124 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
125 if (arg_count) {
126 zval *p = ZEND_CALL_ARG(execute_data, 1);
127 //db.statement
128 switch (Z_TYPE_P(p)) {
129 case IS_STRING:
130 add_assoc_string(&tags, "db.statement", Z_STRVAL_P(p));//丰富PDO 查询语句
131 break;
132
133 }
134 }
135
136 char db_type[64] = {0};
137 pdo_dbh_t *dbh = Z_PDO_DBH_P(&(execute_data->This));
138 if (dbh != NULL) {
139 if (dbh->driver != NULL && dbh->driver->driver_name != NULL) {
140 memcpy(db_type, (char *) dbh->driver->driver_name, dbh->driver->driver_name_len);
141 add_assoc_string(&tags, "db.type", db_type);
142 }
143
144 if (dbh->data_source != NULL && db_type[0] != '\0') {
145 add_assoc_string(&tags, "db.data_source", (char *) dbh->data_source);
146 char *host = pcre_match("(host=([^;\\s]+))", sizeof("(host=([^;\\s]+))")-1, (char *) dbh->data_source);
147 char *port = pcre_match("(port=([^;\\s]+))", sizeof("(port=([^;\\s]+))")-1, (char *) dbh->data_source);
148 if (host != NULL && port != NULL) {
149 peer = (char *) emalloc(strlen(host) + 10);
150 bzero(peer, strlen(host) + 10);
151 sprintf(peer, "%s:%s", host, port);
152 }
153 if (host != NULL) efree(host);
154 if (port != NULL) efree(port);
155 }
156 }
157 } else if (strcmp(class_name, "PDOStatement") == 0) {
158 char db_type[64] = {0};
159 pdo_stmt_t *stmt = (pdo_stmt_t *) Z_PDO_STMT_P(&(execute_data->This));
160 if (stmt != NULL) {
161 add_assoc_string(&tags, "db.statement", stmt->query_string);
162 add_assoc_string(&tags, "component", "PHP-PDO");
163
164 if (stmt->dbh != NULL && stmt->dbh->driver->driver_name != NULL) {
165 memcpy(db_type, (char *) stmt->dbh->driver->driver_name, stmt->dbh->driver->driver_name_len);
166 add_assoc_string(&tags, "db.type", db_type);
167 }
168
169 if (db_type[0] != '\0' && stmt->dbh != NULL && stmt->dbh->data_source != NULL) {
170 add_assoc_string(&tags, "db.data_source", (char *) stmt->dbh->data_source);
171 char *host = pcre_match("(host=([^;\\s]+))", sizeof("(host=([^;\\s]+))")-1, (char *) stmt->dbh->data_source);
172 char *port = pcre_match("(port=([^;\\s]+))", sizeof("(port=([^;\\s]+))")-1, (char *) stmt->dbh->data_source);
173 if (host != NULL && port != NULL) {
174 peer = (char *) emalloc(strlen(host) + 10);
175 bzero(peer, strlen(host) + 10);
176 sprintf(peer, "%s:%s", host, port);
177 }
178 if (host != NULL) efree(host);
179 if (port != NULL) efree(port);
180 }
181 }
182 } else if (strcmp(class_name, "mysqli") == 0) {
183 if (strcmp(function_name, "query") == 0) {
184#ifdef MYSQLI_USE_MYSQLND
185 mysqli_object *mysqli = NULL;
186 if(is_procedural_mysqli) {
187 mysqli = (mysqli_object *) Z_MYSQLI_P(ZEND_CALL_ARG(execute_data, 1));
188 } else {
189 mysqli = (mysqli_object *) Z_MYSQLI_P(&(execute_data->This));
190 }
191
192 MYSQLI_RESOURCE *my_res = (MYSQLI_RESOURCE *) mysqli->ptr;
193 if (my_res && my_res->ptr) {
194 MY_MYSQL *mysql = (MY_MYSQL *) my_res->ptr;
195 if (mysql->mysql) {
196#if PHP_VERSION_ID >= 70100
197 char *host = mysql->mysql->data->hostname.s;
198#else
199 char *host = mysql->mysql->data->host;
200#endif
201 char port[6];
202 char nullHost[10] = "nullhost\0";
203
204 sprintf(port, "%d", mysql->mysql->data->port);
205 add_assoc_string(&tags, "db.port", port);
206 if(host != NULL){
207 add_assoc_string(&tags, "db.host", host);
208 peer = (char *)emalloc(strlen(host) + 10);
209 bzero(peer, strlen(host) + 10);
210 sprintf(peer, "%s:%d", host, mysql->mysql->data->port);
211 }else{
212 add_assoc_string(&tags, "db.host", nullHost);
213 peer = (char *)emalloc(strlen(nullHost) + 10);
214 bzero(peer, strlen(nullHost) + 10);
215 sprintf(peer, "%s:%d", nullHost, mysql->mysql->data->port);
216 }
217 }
218 }
219#endif
220 add_assoc_string(&tags, "db.type", "mysql");
221 add_assoc_string(&tags, "component", "PHP-mysqli");
222 // params
223 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
224 if (arg_count) {
225 zval *p = is_procedural_mysqli ? ZEND_CALL_ARG(execute_data, 2) : ZEND_CALL_ARG(execute_data, 1);
226 //db.statement
227 switch (Z_TYPE_P(p)) {
228 case IS_STRING:
229 add_assoc_string(&tags, "db.statement", Z_STRVAL_P(p));
230 break;
231
232 }
233 }
234 }
235 } else if (strcmp(class_name, "Yar_Client") == 0) {
236 if (strcmp(function_name, "__call") == 0) {
237 zval rv, _uri;
238 ZVAL_STRING(&_uri, "_uri");
239 zval *yar_uri = Z_OBJ_HT(EX(This))->read_property(&EX(This), &_uri, BP_VAR_R, 0, &rv);
240 add_assoc_string(&tags, "component", "PHP-Yar");
241 add_assoc_string(&tags, "yar.uri", Z_STRVAL_P(yar_uri));
242 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
243 if (arg_count) {
244 zval *p = ZEND_CALL_ARG(execute_data, 1);
245 if (Z_TYPE_P(p) == IS_STRING) {
246 add_assoc_string(&tags, "yar.method", Z_STRVAL_P(p));
247 }
248 }
249 }
250 } else if (strcmp(class_name, "Redis") == 0 || strcmp(class_name, "RedisCluster") == 0) { //丰富phpredis 操作span数据
251 add_assoc_string(&tags, "db.type", "redis");
252 add_assoc_string(&tags, "component", "phpredis");
253 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
254
255 if (strcmp(class_name, "Redis") == 0) {
256 // find peer
257 zval *this = &(execute_data->This);
258 zval host;
259 zval port;
260 zend_call_method(this, Z_OBJCE_P(this), NULL, ZEND_STRL("gethost"), &host, 0, NULL, NULL);
261 zend_call_method(this, Z_OBJCE_P(this), NULL, ZEND_STRL("getport"), &port, 0, NULL, NULL);
262
263
264 if (!Z_ISUNDEF(host) && !Z_ISUNDEF(port) && Z_TYPE(host) == IS_STRING && Z_TYPE(port) == IS_LONG) {
265 const char *h = ZSTR_VAL(Z_STR(host));
266 peer = (char *) emalloc(strlen(h) + 10);
267 bzero(peer, strlen(h) + 10);
268 sprintf(peer, "%s:%" PRId3264, h, Z_LVAL(port));
269 }
270
271 if (!Z_ISUNDEF(host)) {
272 zval_ptr_dtor(&host);
273 }
274
275 if (!Z_ISUNDEF(port)) {
276 zval_ptr_dtor(&port);
277 }
278 }
279
280 smart_str command = {0};
281 char *fname = zend_str_tolower_dup((char *) function_name, strlen((char *) function_name));
282 smart_str_appends(&command, fname);
283 smart_str_appends(&command, " ");
284 efree(fname);
285
286 int is_string_command = 1;
287 int i;
288 for (i = 1; i < arg_count + 1; ++i) {
289 zval str_p;
290 zval *p = ZEND_CALL_ARG(execute_data, i);
291 if (Z_TYPE_P(p) == IS_ARRAY) {
292 is_string_command = 0;
293 break;
294 }
295
296 ZVAL_COPY(&str_p, p);
297 if (Z_TYPE_P(&str_p) != IS_STRING) {
298 convert_to_string(&str_p);
299 }
300 if (i == 1) {
301 add_assoc_string(&tags, "redis.key", Z_STRVAL_P(&str_p));//丰富phpredis key数据
302 }
303 char *tmp = zend_str_tolower_dup(Z_STRVAL_P(&str_p), Z_STRLEN_P(&str_p));
304 smart_str_appends(&command, tmp);
305 smart_str_appends(&command, " ");
306 efree(tmp);
307 }
308 // store command to tags
309 if (command.s) {
310 smart_str_0(&command);
311 if (is_string_command) {
312 zend_string *trim_s = php_trim(command.s, NULL, 0, 3);
313 add_assoc_string(&tags, "redis.command", ZSTR_VAL(trim_s));//丰富phpredis 指令数据
314 zend_string_free(trim_s);
315 }
316 smart_str_free(&command);
317 }
318 } else if (strcmp(class_name, "Memcached") == 0) {
319
320 add_assoc_string(&tags, "db.type", "memcached");
321 add_assoc_string(&tags, "component", "PHP-Memcached");
322 uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
323
324 smart_str command = {0};
325 smart_str_appends(&command, zend_str_tolower_dup((char *) function_name, strlen((char *) function_name)));
326 smart_str_appends(&command, " ");
327
328 int i;
329 for (i = 1; i < arg_count + 1; ++i) {
330 char *str = NULL;
331 zval str_p;
332 zval *p = ZEND_CALL_ARG(execute_data, i);
333 if (Z_TYPE_P(p) == IS_ARRAY) {
334 str = sky_json_encode(p);
335 }
336
337 ZVAL_COPY(&str_p, p);
338 if (Z_TYPE_P(&str_p) != IS_ARRAY && Z_TYPE_P(&str_p) != IS_STRING) {
339 convert_to_string(&str_p);
340 }
341
342 if (str == NULL) {
343 str = Z_STRVAL_P(&str_p);
344 }
345
346 if (i == 1) {
347 add_assoc_string(&tags, "memcached.key", str);
348 }
349 smart_str_appends(&command, zend_str_tolower_dup(str, strlen(str)));
350 smart_str_appends(&command, " ");
351 }
352 // store command to tags
353 if (command.s) {
354 smart_str_0(&command);
355 add_assoc_string(&tags, "memcached.command", ZSTR_VAL(php_trim(command.s, NULL, 0, 3)));
356 smart_str_free(&command);
357 }
358 }
359
360
361 zval temp;
362 zval *spans = NULL;
363 zval *span_id = NULL;
364 zval *last_span = NULL;
365 char *l_millisecond;
366 long millisecond;
367 array_init(&temp);
368 spans = get_spans();
369 last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1);
370 span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
371
372 add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
373 add_assoc_long(&temp, "parentSpanId", 0);
374 l_millisecond = get_millisecond();
375 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
376 efree(l_millisecond);
377 add_assoc_long(&temp, "startTime", millisecond);
378 add_assoc_long(&temp, "spanType", 1);
379 add_assoc_long(&temp, "spanLayer", 1);
380// add_assoc_string(&temp, "component", component);
381 add_assoc_long(&temp, "componentId", componentId);
382 add_assoc_string(&temp, "operationName", operationName);
383 add_assoc_string(&temp, "peer", peer == NULL ? "" : peer);
384 efree(component);
385 efree(operationName);
386 if (peer != NULL) {
387 efree(peer);
388 }
389
390 //执行PHP内核原函数指针
391 if (ori_execute_internal) {
392 ori_execute_internal(execute_data, return_value);
393 } else {
394 execute_internal(execute_data, return_value);
395 }
396
397 l_millisecond = get_millisecond();
398 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
399 efree(l_millisecond);
400
401
402 add_assoc_zval(&temp, "tags", &tags);
403 add_assoc_long(&temp, "endTime", millisecond);
404 add_assoc_long(&temp, "isError", 0);
405
406 zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
407 } else {
408 if (ori_execute_internal) {
409 ori_execute_internal(execute_data, return_value);
410 } else {
411 execute_internal(execute_data, return_value);
412 }
413 }
414}
3.3 PHP-CURL 内核Hook拦截
1//curl执行拦截
2void sky_curl_exec_handler(INTERNAL_FUNCTION_PARAMETERS)
3{
4 if(application_instance == 0) {
5 orig_curl_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU);
6 return;
7 }
8
9 zval *zid;
10 if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE) {
11 return;
12 }
13
14 int is_send = 1;
15
16 zval function_name,curlInfo;
17 zval params[1];
18 ZVAL_COPY(¶ms[0], zid);
19
20 //执行curl_getinfo函数:
21 ZVAL_STRING(&function_name, "curl_getinfo");
22 call_user_function(CG(function_table), NULL, &function_name, &curlInfo, 1, params); //从CG全局函数表、调用curl_getinfo函数
23 zval_dtor(&function_name);
24 zval_dtor(¶ms[0]);
25
26 //获取curl请求的url信息
27 zval *z_url = zend_hash_str_find(Z_ARRVAL(curlInfo), ZEND_STRL("url"));
28 char *url_str = Z_STRVAL_P(z_url);
29
30 if(strlen(url_str) <= 0) {
31 zval_dtor(&curlInfo);
32 is_send = 0;
33 }
34
35 //构建标准的HTTP URI结构
36 php_url *url_info = NULL;
37 if(is_send == 1) {
38 url_info = php_url_parse(url_str); //解析
39 if(url_info->scheme == NULL || url_info->host == NULL) {
40 zval_dtor(&curlInfo);
41 php_url_free(url_info); //释放内存
42 is_send = 0;
43 }
44 }
45
46 char *sw = NULL;
47 zval *spans = NULL;
48 zval *last_span = NULL;
49 zval *span_id = NULL;
50 char *peer = NULL;
51 char *operation_name = NULL;
52 char *full_url = NULL;
53
54
55 //确定要发送的请求、当前请求合理,才进行下面的信息丰富。
56 if (is_send == 1) {
57
58// for php7.3.0+
59#if PHP_VERSION_ID >= 70300
60 char *php_url_scheme = ZSTR_VAL(url_info->scheme);
61 char *php_url_host = ZSTR_VAL(url_info->host);
62 char *php_url_path = ZSTR_VAL(url_info->path);
63 char *php_url_query = ZSTR_VAL(url_info->query);
64#else
65 char *php_url_scheme = url_info->scheme;
66 char *php_url_host = url_info->host;
67 char *php_url_path = url_info->path;
68 char *php_url_query = url_info->query;
69#endif
70
71 //处理url_info内部的被调用方端口值
72 int peer_port = 0;
73 if (url_info->port) {
74 peer_port = url_info->port;
75 } else {
76 if (strcasecmp("http", php_url_scheme) == 0) { //默认端口设置
77 peer_port = 80;
78 } else {
79 peer_port = 443;
80 }
81 }
82
83 //处理url_info内部的query参数
84 if (url_info->query){
85 if (url_info->path == NULL) {
86 spprintf(&operation_name, 0, "%s", "/");
87 spprintf(&full_url, 0, "%s?%s", "/", php_url_query);
88 } else {
89 spprintf(&operation_name, 0, "%s", php_url_path);
90 spprintf(&full_url, 0, "%s?%s", php_url_path, php_url_query);
91 }
92 }
93 else
94 {
95 if (url_info->path == NULL) {
96 spprintf(&operation_name, 0, "%s", "/");
97 spprintf(&full_url, 0, "%s", "/");
98 } else {
99 spprintf(&operation_name, 0, "%s", php_url_path);
100 spprintf(&full_url, 0, "%s", php_url_path);
101 }
102 }
103
104 spans = get_spans(); //获取spans数组
105 last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1); //获取最后一个span
106 span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
107
108 //生成新的Trace-Span
109 if (SKYWALKING_G(version) == 5)
110 { // skywalking 5.x
111 spprintf(&peer, 0, "%s://%s:%d", php_url_scheme, php_url_host, peer_port);
112 sw = generate_sw3(Z_LVAL_P(span_id) + 1, peer, operation_name);
113 }
114 else if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7)
115 { // skywalking 6.x
116 spprintf(&peer, 0, "%s:%d", php_url_host, peer_port);
117 sw = generate_sw6(Z_LVAL_P(span_id) + 1, peer);
118 }
119 else if (SKYWALKING_G(version) == 8)
120 {
121 spprintf(&peer, 0, "%s:%d", php_url_host, peer_port); //
122 sw = generate_sw8(Z_LVAL_P(span_id) + 1, peer); //生成version 8的span
123 }
124 }
125
126 /*
127 sw6_l = snprintf(NULL, 0, "sw8: 1-%s-%s-%" PRId3264 "-%s-%s-%s-%s",
128 Z_STRVAL(traceIdEncode),
129 Z_STRVAL(currentTraceIdEncode),
130 span_id,
131 Z_STRVAL(serviceEncode),
132 Z_STRVAL(serviceInstanceEncode),
133 Z_STRVAL(parentEndpointEncode),
134 Z_STRVAL(targetAddressEncode));
135
136 */
137 //如果span创建成功 : sw结构样例:
138 //sw8: 1-MS41NDY0LjE1OTg1MDU3ODEwMDAx-MS41NDY0LjE1OTg1MDU3ODEwMDAx-1-bG9jYWxfcGhw-YjFmN2QzNTctZTNkYi00ODkzLTk1NjktY2ZmOTczNWNlNDRh-L3NjX2N1cmxfcG9zdC5waHA=-MTI3LjAuMC4xOjkwOTA=
139 if (sw != NULL) {
140 zval *option = NULL;
141 int is_init = 0;
142 option = zend_hash_index_find(Z_ARRVAL_P(&SKYWALKING_G(curl_header)), Z_RES_HANDLE_P(zid));
143
144 if(option == NULL) {
145 option = emalloc(sizeof(zval));
146 bzero(option, sizeof(zval));
147 array_init(option);
148 is_init = 1;
149 }
150
151 add_next_index_string(option, sw); //sw字符串为sw8: 打头,所以天然就是HTTP header
152 add_index_bool(&SKYWALKING_G(curl_header_send), (zend_ulong)Z_RES_HANDLE_P(zid), IS_TRUE);
153
154 zval func;
155 zval argv[3];
156 zval ret;
157 ZVAL_STRING(&func, "curl_setopt");
158
159 ZVAL_COPY(&argv[0], zid);
160 ZVAL_LONG(&argv[1], CURLOPT_HTTPHEADER);
161 ZVAL_COPY(&argv[2], option);
162 call_user_function(CG(function_table), NULL, &func, &ret, 3, argv); //给curl header调用注入全链路Trace信息
163 zval_dtor(&ret);
164 zval_dtor(&func);
165 if(is_init == 1) {
166 zval_ptr_dtor(option);
167 efree(option);
168 }
169 zval_dtor(&argv[0]);
170 zval_dtor(&argv[1]);
171 zval_dtor(&argv[2]);
172 efree(sw);
173 }
174
175 zval temp;
176 char *l_millisecond;
177 long millisecond;
178 if(is_send == 1) {
179
180 array_init(&temp);
181
182 add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
183 add_assoc_long(&temp, "parentSpanId", 0);
184 l_millisecond = get_millisecond();
185 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
186 efree(l_millisecond);
187 add_assoc_long(&temp, "startTime", millisecond);
188 add_assoc_long(&temp, "spanType", 1);
189 add_assoc_long(&temp, "spanLayer", 3);
190 add_assoc_long(&temp, "componentId", COMPONENT_HTTPCLIENT);
191 }
192
193
194 orig_curl_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU); //执行原生的curl函数
195
196 if (is_send == 1) {
197 //结束时间获取
198 l_millisecond = get_millisecond();
199 millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
200 efree(l_millisecond);
201
202 zval function_name_1, curlInfo_1;
203 zval params_1[1];
204 ZVAL_COPY(¶ms_1[0], zid);
205 ZVAL_STRING(&function_name_1, "curl_getinfo");
206 call_user_function(CG(function_table), NULL, &function_name_1, &curlInfo_1, 1, params_1);
207 zval_dtor(¶ms_1[0]);
208 zval_dtor(&function_name_1);
209
210 add_assoc_long(&temp, "endTime", millisecond);
211
212 add_assoc_string(&temp, "operationName", operation_name);
213 add_assoc_string(&temp, "peer", peer);
214
215 zval tags;
216 array_init(&tags);
217 add_assoc_string(&tags, "url", full_url);
218
219 add_assoc_string(&tags, "component", "PHP-CURL");
220 //HTTP 响应code
221 zval *z_http_code = zend_hash_str_find(Z_ARRVAL(curlInfo_1), ZEND_STRL("http_code"));
222
223 if (Z_LVAL_P(z_http_code) != 200){
224 add_assoc_long(&temp, "isError", 1);
225 }
226 else
227 {
228 add_assoc_long(&temp, "isError", 0);
229 }
230
231 //针对http_code进行类型转化,便于增加tag
232 if (Z_TYPE_P(z_http_code) == IS_LONG)
233 {
234 convert_to_string(z_http_code); //针对z_http_code进行类型转换
235 add_assoc_string(&tags, "http.status_code", Z_STRVAL_P(z_http_code)); //追加 reponse http code tag
236 }
237
238 add_assoc_zval(&temp, "tags", &tags);
239 efree(peer);
240 efree(operation_name);
241 efree(full_url);
242
243 php_url_free(url_info);
244
245 zval _refs;
246 array_init(&_refs);
247 add_assoc_zval(&temp, "refs", &_refs);
248 zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
249 zval_dtor(&curlInfo_1);
250 zval_dtor(&curlInfo);
251 }
252}
四. 关键附属函数分析
4.1 全局变量初始化函数
1//初始化内核扩展全局变量
2static void php_skywalking_init_globals(zend_skywalking_globals *skywalking_globals)
3{
4 skywalking_globals->app_code = NULL;
5 skywalking_globals->enable = 0;
6 skywalking_globals->version = 6;
7 skywalking_globals->sock_path = "/var/run/sky-agent.sock";
8 skywalking_globals->app_code_env_key = "APM_APP_CODE";
9}
4.2 json序列化函数
1static char *sky_json_encode(zval *parameter){
2
3 smart_str buf = {0};
4 zend_long options = 64;
5#if PHP_VERSION_ID >= 70100
6 if (php_json_encode(&buf, parameter, (int)options) != SUCCESS) {
7 smart_str_free(&buf);
8 return NULL;
9 }
10#else
11 php_json_encode(&buf, parameter, (int)options);
12#endif
13 smart_str_0(&buf);
14 if(buf.s != NULL) {
15 char *bufs = emalloc(strlen(ZSTR_VAL(buf.s)) + 1);
16 strcpy(bufs, ZSTR_VAL(buf.s));
17 smart_str_free(&buf);
18 return bufs;
19 }
20 return NULL;
21}
4.3 发送APM数据函数
1static void write_log(char *text) {
2 if (application_instance != 0) {
3 // to stream
4 if(text == NULL || strlen(text) <= 0) {
5 return;
6 }
7
8 struct sockaddr_un un;
9 un.sun_family = AF_UNIX;
10 strcpy(un.sun_path, SKYWALKING_G(sock_path));
11 int fd;
12 char *message = (char*) emalloc(strlen(text) + 10);
13 bzero(message, strlen(text) + 10);
14
15 fd = socket(AF_UNIX, SOCK_STREAM, 0);
16 if (fd >= 0) {
17 struct timeval tv;
18 tv.tv_sec = 0;
19 tv.tv_usec = 100000;
20 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
21 int conn = connect(fd, (struct sockaddr *) &un, sizeof(un));
22
23 if (conn >= 0) {
24 sprintf(message, "1%s\n", text);
25 write(fd, message, strlen(message));
26 } else {
27 php_error_docref(NULL, E_WARNING, "[skywalking] failed to connect the sock.");
28 }
29 close(fd);
30 } else {
31 php_error_docref(NULL, E_WARNING, "[skywalking] failed to open the sock.");
32 }
33 efree(message);
34 efree(text);
35 }
36
37}
4.4 生成上下文函数
1static void generate_context() {
2 int sys_pid = getpid();
3 long second = get_second();
4 second = second * 10000 + sky_increment_id; //创建traceid的因子
5 char *makeTraceId;
6 makeTraceId = (char *) emalloc(sizeof(char) * 180); //分配traceId所需要的内存
7
8 bzero(makeTraceId, sizeof(char) * 180);
9
10 sprintf(makeTraceId, "%d.%d.%ld", application_instance, sys_pid, second);
11
12 add_assoc_string(&SKYWALKING_G(context), "currentTraceId", makeTraceId); //针对上下文context变量添加currentTraceId元素
13 add_assoc_long(&SKYWALKING_G(context), "isChild", 0);
14
15 // parent
16 zval *carrier = NULL;
17 zval *sw;
18
19 zend_bool jit_initialization = PG(auto_globals_jit); //PHP全局变量
20
21 if (jit_initialization) {
22 zend_string *server_str = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
23 zend_is_auto_global(server_str);
24 zend_string_release(server_str);
25 }
26 carrier = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER")); //获取查找$_SERVER超级全局变量
27
28 if(SKYWALKING_G(version) == 5) {
29 sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW3", sizeof("HTTP_SW3") - 1);
30
31 if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) {
32 add_assoc_string(&SKYWALKING_G(context), "sw3", Z_STRVAL_P(sw));
33
34 zval temp;
35 array_init(&temp);
36
37 php_explode(zend_string_init(ZEND_STRL("|"), 0), Z_STR_P(sw), &temp, 10);
38
39 if(zend_array_count(Z_ARRVAL_P(&temp)) >= 8) {
40 zval *sw3_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
41 zval *sw3_1 = zend_hash_index_find(Z_ARRVAL(temp), 1);
42 zval *sw3_2 = zend_hash_index_find(Z_ARRVAL(temp), 2);
43 zval *sw3_3 = zend_hash_index_find(Z_ARRVAL(temp), 3);
44 zval *sw3_4 = zend_hash_index_find(Z_ARRVAL(temp), 4);
45 zval *sw3_5 = zend_hash_index_find(Z_ARRVAL(temp), 5);
46 zval *sw3_6 = zend_hash_index_find(Z_ARRVAL(temp), 6);
47 zval *sw3_7 = zend_hash_index_find(Z_ARRVAL(temp), 7);
48
49 zval child;
50 array_init(&child);
51 ZVAL_LONG(&child, 1);
52 zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child);
53
54 add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL_P(sw3_0));
55 add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw3_1), sizeof(Z_STRVAL_P(sw3_1)) - 1));
56 add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", zend_atol(Z_STRVAL_P(sw3_2), sizeof(Z_STRVAL_P(sw3_2)) - 1));
57 add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", zend_atol(Z_STRVAL_P(sw3_3), sizeof(Z_STRVAL_P(sw3_3)) - 1));
58 add_assoc_str(&SKYWALKING_G(context), "networkAddress", trim_sharp(sw3_4));
59 add_assoc_str(&SKYWALKING_G(context), "entryOperationName", trim_sharp(sw3_5));
60 add_assoc_str(&SKYWALKING_G(context), "parentOperationName", trim_sharp(sw3_6));
61 add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", Z_STRVAL_P(sw3_7));
62 }
63 } else {
64 add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", application_instance);
65 add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", application_instance);
66 char *uri = get_page_request_uri();
67 add_assoc_string(&SKYWALKING_G(context), "entryOperationName", (uri == NULL) ? "" : uri);
68 add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", makeTraceId);
69 if(uri != NULL) {
70 efree(uri);
71 }
72 }
73 } else if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7) {
74 sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW6", sizeof("HTTP_SW6") - 1);
75 if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) {
76 add_assoc_string(&SKYWALKING_G(context), "sw6", Z_STRVAL_P(sw));
77
78 zval temp;
79 array_init(&temp);
80
81 php_explode(zend_string_init(ZEND_STRL("-"), 0), Z_STR_P(sw), &temp, 10);
82
83 if(zend_array_count(Z_ARRVAL_P(&temp)) >= 7) {
84 zval *sw6_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
85 zval *sw6_1 = zend_hash_index_find(Z_ARRVAL(temp), 1); // Trace Id
86 zval *sw6_2 = zend_hash_index_find(Z_ARRVAL(temp), 2); // Parent trace segment Id
87 zval *sw6_3 = zend_hash_index_find(Z_ARRVAL(temp), 3); // Parent span Id
88 zval *sw6_4 = zend_hash_index_find(Z_ARRVAL(temp), 4); // Parent service instance Id
89 zval *sw6_5 = zend_hash_index_find(Z_ARRVAL(temp), 5); // Entrance service instance Id
90 zval *sw6_6 = zend_hash_index_find(Z_ARRVAL(temp), 6); // Target address of this request
91
92 zval *sw6_7 = NULL;
93 zval *sw6_8 = NULL;
94 if (zend_array_count(Z_ARRVAL_P(&temp)) >= 9) {
95 sw6_7 = zend_hash_index_find(Z_ARRVAL(temp), 7);
96 sw6_8 = zend_hash_index_find(Z_ARRVAL(temp), 8);
97 }
98
99
100 zval child;
101 array_init(&child);
102 ZVAL_LONG(&child, 1)
103 zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child);
104
105 zval sw6_1decode;
106 zval sw6_2decode;
107 zval sw6_6decode;
108 zval_b64_decode(&sw6_1decode, Z_STRVAL_P(sw6_1));
109 zval_b64_decode(&sw6_2decode, Z_STRVAL_P(sw6_2));
110 zval_b64_decode(&sw6_6decode, Z_STRVAL_P(sw6_6));
111
112 add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL(sw6_2decode));
113 add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw6_3), sizeof(Z_STRVAL_P(sw6_3)) - 1));
114 add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", zend_atol(Z_STRVAL_P(sw6_4), sizeof(Z_STRVAL_P(sw6_4)) - 1));
115 add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", zend_atol(Z_STRVAL_P(sw6_5), sizeof(Z_STRVAL_P(sw6_5)) - 1));
116 add_assoc_string(&SKYWALKING_G(context), "networkAddress", Z_STRVAL(sw6_6decode));
117 if (sw6_7 != NULL && sw6_8 != NULL) {
118
119 zval sw6_7decode;
120 zval sw6_8decode;
121 zval_b64_decode(&sw6_7decode, Z_STRVAL_P(sw6_7));
122 zval_b64_decode(&sw6_8decode, Z_STRVAL_P(sw6_8));
123
124 add_assoc_string(&SKYWALKING_G(context), "entryOperationName", Z_STRVAL(sw6_7decode));
125 add_assoc_string(&SKYWALKING_G(context), "parentOperationName", Z_STRVAL(sw6_8decode));
126
127 zval_dtor(&sw6_7decode);
128 zval_dtor(&sw6_8decode);
129 }
130 add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", Z_STRVAL(sw6_1decode));
131
132 zval_dtor(&sw6_1decode);
133 zval_dtor(&sw6_2decode);
134 zval_dtor(&sw6_6decode);
135 }
136 } else {
137 add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", application_instance);
138 add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", application_instance);
139 char *uri = get_page_request_uri();
140 char *path = NULL;
141 if (uri != NULL) {
142 path = (char *)emalloc(strlen(uri) + 5);
143 bzero(path, strlen(uri) + 5);
144
145 int i;
146 for(i = 0; i < strlen(uri); i++) {
147 if (uri[i] == '?') {
148 break;
149 }
150 path[i] = uri[i];
151 }
152 path[i] = '\0';
153 }
154
155 add_assoc_string(&SKYWALKING_G(context), "entryOperationName", (path == NULL) ? "" : path);
156 if (path != NULL) {
157 efree(path);
158 }
159
160 add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", makeTraceId);
161 if(uri != NULL) {
162 efree(uri);
163 }
164 }
165 } else if (SKYWALKING_G(version) == 8) {
166 sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW8", sizeof("HTTP_SW8") - 1); //$SERVER['HTTP_SW8'];
167 if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) { //从header头部拿到了trace信息
168 add_assoc_string(&SKYWALKING_G(context), "sw8", Z_STRVAL_P(sw));
169
170 zval temp;
171 array_init(&temp); //初始化数组元素
172
173 php_explode(zend_string_init(ZEND_STRL("-"), 0), Z_STR_P(sw), &temp, 10);
174
175 if(zend_array_count(Z_ARRVAL_P(&temp)) >= 7) {
176 zval *sw8_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
177 zval *sw8_1 = zend_hash_index_find(Z_ARRVAL(temp), 1); // Trace Id base64
178 zval *sw8_2 = zend_hash_index_find(Z_ARRVAL(temp), 2); // Parent trace segment Id
179 zval *sw8_3 = zend_hash_index_find(Z_ARRVAL(temp), 3); // Parent span Id
180 zval *sw8_4 = zend_hash_index_find(Z_ARRVAL(temp), 4); // Parent service
181 zval *sw8_5 = zend_hash_index_find(Z_ARRVAL(temp), 5); // Parent service instance
182 zval *sw8_6 = zend_hash_index_find(Z_ARRVAL(temp), 6); // Parent endpoint
183 zval *sw8_7 = zend_hash_index_find(Z_ARRVAL(temp), 7); // Target address used at client side of this request
184
185 zval child;
186 array_init(&child);
187 ZVAL_LONG(&child, 1)
188 zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child); //因为当前HTTP SERVER有TraceId,所以链路存在父对象、因此切换当前上下文状态为child状态。
189
190 zval sw8_1decode;
191 zval sw8_2decode;
192 zval sw8_4decode;
193 zval sw8_5decode;
194 zval sw8_6decode;
195 zval sw8_7decode;
196 zval_b64_decode(&sw8_1decode, Z_STRVAL_P(sw8_1));
197 zval_b64_decode(&sw8_2decode, Z_STRVAL_P(sw8_2));
198 zval_b64_decode(&sw8_4decode, Z_STRVAL_P(sw8_4));
199 zval_b64_decode(&sw8_5decode, Z_STRVAL_P(sw8_5));
200 zval_b64_decode(&sw8_6decode, Z_STRVAL_P(sw8_6));
201 zval_b64_decode(&sw8_7decode, Z_STRVAL_P(sw8_7));
202
203 //针对当前请求的上下文丰富相关链路数据。
204 add_assoc_string(&SKYWALKING_G(context), "traceId", Z_STRVAL(sw8_1decode));
205 add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL(sw8_2decode));
206 add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw8_3), sizeof(Z_STRVAL_P(sw8_3)) - 1));
207 add_assoc_string(&SKYWALKING_G(context), "parentService", Z_STRVAL(sw8_4decode));
208 add_assoc_string(&SKYWALKING_G(context), "parentServiceInstance", Z_STRVAL(sw8_5decode));
209 add_assoc_string(&SKYWALKING_G(context), "parentEndpoint", Z_STRVAL(sw8_6decode));
210 add_assoc_string(&SKYWALKING_G(context), "targetAddress", Z_STRVAL(sw8_7decode));
211
212 //释放转码相关内存
213 zval_dtor(&sw8_1decode);
214 zval_dtor(&sw8_2decode);
215 zval_dtor(&sw8_4decode);
216 zval_dtor(&sw8_5decode);
217 zval_dtor(&sw8_6decode);
218 zval_dtor(&sw8_7decode);
219 }
220 } else {
221 //$SERVER没有全链路ID,证明当前为ROOT根
222 //此处可以注入采样率
223 add_assoc_string(&SKYWALKING_G(context), "parentService", service);
224 add_assoc_string(&SKYWALKING_G(context), "parentServiceInstance", service_instance);
225 char *uri = get_page_request_uri(); //获取当前请求的URI PATH
226 char *path = NULL;
227 if (uri != NULL) {
228 path = (char *)emalloc(strlen(uri) + 5);
229 bzero(path, strlen(uri) + 5);
230
231 int i;
232 for(i = 0; i < strlen(uri); i++) {
233 if (uri[i] == '?') {
234 break;
235 }
236 path[i] = uri[i];
237 }
238 path[i] = '\0';
239 }
240
241 add_assoc_string(&SKYWALKING_G(context), "parentEndpoint", (path == NULL) ? "" : path); //把自己当前的PATH当做父级EndPoint
242 if (path != NULL) {
243 efree(path);
244 }
245
246 add_assoc_string(&SKYWALKING_G(context), "traceId", makeTraceId); //丰富traceId信息
247 if(uri != NULL) {
248 efree(uri);
249 }
250 }
251 }
252
253 efree(makeTraceId);
254}
4.5 解析请求URI信息函数
1//获取当前请求的URI地址
2static char *get_page_request_uri() {
3 zval *carrier = NULL;
4 zval *request_uri;
5
6 smart_str uri = {0}; //创建smart_str对象
7
8 if (strcasecmp("cli", sapi_module.name) == 0) {
9 smart_str_appendl(&uri, "cli", strlen("cli"));
10 } else {
11 zend_bool jit_initialization = PG(auto_globals_jit);
12
13 if (jit_initialization) {
14 zend_string *server_str = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
15 zend_is_auto_global(server_str);
16 zend_string_release(server_str);
17 }
18 carrier = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"));
19
20 request_uri = zend_hash_str_find(Z_ARRVAL_P(carrier), "REQUEST_URI", sizeof("REQUEST_URI") - 1);
21 smart_str_appendl(&uri, Z_STRVAL_P(request_uri), strlen(Z_STRVAL_P(request_uri)));
22 }
23
24 smart_str_0(&uri); //smart_str追加\0结尾
25 if (uri.s != NULL) { //转化为char*类型
26 char *uris = emalloc(strlen(ZSTR_VAL(uri.s)) + 1);
27 strcpy(uris, ZSTR_VAL(uri.s));
28 smart_str_free(&uri);
29 return uris;
30 }
31 return NULL;
32}
4.6 初始化请求函数
1//请求初始化操作:用来丰富trace、spans等信息: 审计:2020.08.27 00:01
2static void request_init() {
3
4 array_init(&SKYWALKING_G(curl_header));
5 array_init(&SKYWALKING_G(curl_header_send));
6 array_init(&SKYWALKING_G(context)); //初始化上下文
7 array_init(&SKYWALKING_G(UpstreamSegment)); //初始化to agent的数据格式
8
9 generate_context(); //生成上下文
10
11 add_assoc_long(&SKYWALKING_G(UpstreamSegment), "application_instance", application_instance); //from agent
12 add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "uuid", application_uuid, strlen(application_uuid)); //from agent
13 add_assoc_long(&SKYWALKING_G(UpstreamSegment), "pid", getppid());
14 add_assoc_long(&SKYWALKING_G(UpstreamSegment), "application_id", application_id);
15 add_assoc_long(&SKYWALKING_G(UpstreamSegment), "version", SKYWALKING_G(version));
16 SKY_ADD_ASSOC_ZVAL(&SKYWALKING_G(UpstreamSegment), "segment"); //创建子数组对象,用于构建 $UpstreamSegment['segment'] = array();
17 SKY_ADD_ASSOC_ZVAL(&SKYWALKING_G(UpstreamSegment), "globalTraceIds");
18
19 add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "service", service, strlen(service)); //like app_code: from agent
20 add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "serviceInstance", service_instance, strlen(service_instance)); //uuid : from agent
21 zval *traceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "currentTraceId", sizeof("currentTraceId") - 1);
22
23 zval traceSegmentObject;
24 zval spans;
25 array_init(&spans);
26 array_init(&traceSegmentObject);
27 add_assoc_string(&traceSegmentObject, "traceSegmentId", Z_STRVAL_P(traceId));
28 add_assoc_long(&traceSegmentObject, "isSizeLimited", 0);
29
30 zval temp;
31 char *peer = NULL;
32 char *uri = get_page_request_uri();
33 char *path = (char*)emalloc(strlen(uri) + 5);
34 bzero(path, strlen(uri) + 5);
35
36
37 int i;
38 for(i = 0; i < strlen(uri); i++) {
39 if (uri[i] == '?') {
40 break;
41 }
42 path[i] = uri[i];
43 }
44 path[i] = '\0';
45
46 array_init(&temp);
47 peer = get_page_request_peer(); // 从PHP $_SERVER变量中读取peer数据
48 zval tags;
49 array_init(&tags); //创建tags对象
50 add_assoc_string(&tags, "url", (uri == NULL) ? "" : uri);
51 add_assoc_string(&tags, "server_type", "PHP"); //注入PHP Server类型
52 add_assoc_string(&tags, "php_version",PHP_VERSION); //注入PHP版本号
53
54 add_assoc_zval(&temp, "tags", &tags);
55
56 add_assoc_long(&temp, "spanId", 0);
57 add_assoc_long(&temp, "parentSpanId", -1);
58 char *l_millisecond = get_millisecond();
59 long millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
60 efree(l_millisecond);
61 add_assoc_long(&temp, "startTime", millisecond);
62 add_assoc_string(&temp, "operationName", path);
63 add_assoc_string(&temp, "peer", (peer == NULL) ? "" : peer);
64 add_assoc_long(&temp, "spanType", 0);
65 add_assoc_long(&temp, "spanLayer", 3);
66 if (SKYWALKING_G(version) == 8) {
67 add_assoc_long(&temp, "componentId", 8001);
68 } else {
69 add_assoc_long(&temp, "componentId", COMPONENT_UNDERTOW);
70 }
71
72
73 // sw8 or sw6 for parent endpoint name
74 add_assoc_string(&SKYWALKING_G(context), "currentEndpoint", path);
75
76 efree(path);
77 if (peer != NULL) {
78 efree(peer);
79 }
80 if (uri != NULL) {
81 efree(uri);
82 }
83
84 zval *isChild = zend_hash_str_find(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1);
85 // refs
86 zval refs;
87 array_init(&refs);
88
89 zval globalTraceIds;
90 array_init(&globalTraceIds);
91 zval tmpGlobalTraceIds;
92
93 add_assoc_string(&SKYWALKING_G(UpstreamSegment), "traceId", Z_STRVAL_P(traceId));
94
95 if(Z_LVAL_P(isChild) == 1) {
96 zval ref;
97 array_init(&ref);
98 zval *parentTraceSegmentId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentTraceSegmentId", sizeof("parentTraceSegmentId") - 1);
99 zval *parentSpanId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentSpanId", sizeof("parentSpanId") - 1);
100 zval *parentApplicationInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentApplicationInstance", sizeof("parentApplicationInstance") - 1);
101 zval *entryApplicationInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "entryApplicationInstance", sizeof("entryApplicationInstance") - 1);
102 zval *entryOperationName = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "entryOperationName", sizeof("entryOperationName") - 1);
103 zval *networkAddress = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "networkAddress", sizeof("networkAddress") - 1);
104 zval *parentOperationName = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentOperationName", sizeof("parentOperationName") - 1);
105 zval *distributedTraceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "distributedTraceId", sizeof("distributedTraceId") - 1);
106 add_assoc_long(&ref, "type", 0);
107 add_assoc_string(&ref, "parentTraceSegmentId", Z_STRVAL_P(parentTraceSegmentId));
108 add_assoc_long(&ref, "parentSpanId", Z_LVAL_P(parentSpanId));
109
110 if (SKYWALKING_G(version) == 8) {
111 zval *traceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "traceId", sizeof("traceId") - 1);
112 zval *parentService = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentService", sizeof("parentService") - 1);
113 zval *parentServiceInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentServiceInstance", sizeof("parentServiceInstance") - 1);
114 zval *parentEndpoint = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentEndpoint", sizeof("parentEndpoint") - 1);
115 zval *targetAddress = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "targetAddress", sizeof("targetAddress") - 1);
116
117 add_assoc_string(&ref, "traceId", Z_STRVAL_P(traceId));
118 add_assoc_string(&ref, "parentService", Z_STRVAL_P(parentService));
119 add_assoc_string(&ref, "parentServiceInstance", Z_STRVAL_P(parentServiceInstance));
120 add_assoc_string(&ref, "parentEndpoint", Z_STRVAL_P(parentEndpoint));
121 add_assoc_string(&ref, "targetAddress", Z_STRVAL_P(targetAddress));
122 zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "traceId", sizeof("traceId") - 1, traceId);
123 } else {
124 add_assoc_long(&ref, "parentApplicationInstanceId", Z_LVAL_P(parentApplicationInstance));
125 add_assoc_long(&ref, "entryApplicationInstanceId", Z_LVAL_P(entryApplicationInstance));
126 add_assoc_string(&ref, "networkAddress", Z_STRVAL_P(networkAddress));
127 add_assoc_string(&ref, "entryServiceName", Z_STRVAL_P(entryOperationName));
128 add_assoc_string(&ref, "parentServiceName", Z_STRVAL_P(parentOperationName));
129 ZVAL_STRING(&tmpGlobalTraceIds, Z_STRVAL_P(distributedTraceId));
130 }
131
132 zend_hash_next_index_insert(Z_ARRVAL(refs), &ref);
133 } else {
134 ZVAL_STRING(&tmpGlobalTraceIds, Z_STRVAL_P(traceId));
135 }
136
137 zend_hash_str_add(Z_ARRVAL(temp), "refs", sizeof("refs") - 1, &refs);
138 zend_hash_next_index_insert(Z_ARRVAL(spans), &temp);
139
140 add_assoc_zval(&traceSegmentObject, "spans", &spans);
141
142 if (SKYWALKING_G(version) != 8)
143 { //谨慎调整
144 zend_hash_next_index_insert(Z_ARRVAL(globalTraceIds), &tmpGlobalTraceIds); //问题症结 :skywalking 8.0版本之后移除这个参数支持
145 }
146 zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "segment", sizeof("segment") - 1, &traceSegmentObject);
147 zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "globalTraceIds", sizeof("globalTraceIds") - 1, &globalTraceIds);
148}
4.7 APM数据flush函数
1static void sky_flush_all() {
2 char *l_millisecond = get_millisecond();
3 long millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
4 efree(l_millisecond);
5
6 zval *span = get_first_span();
7
8 add_assoc_long(span, "endTime", millisecond); //标志请求结束时间
9 if ((SG(sapi_headers).http_response_code >= 500)) { //标志是否出错
10 add_assoc_long(span, "isError", 1);
11 } else {
12 add_assoc_long(span, "isError", 0);
13 }
14 write_log(sky_json_encode(&SKYWALKING_G(UpstreamSegment))); //发送APM数据 - UNIX domain socket途径
15}
4.8 APM注册函数
1//sky-agent注册 :2020.08.26 22:34审计结束
2static int sky_register() {
3 if (application_instance == 0) {
4 struct sockaddr_un un;
5 un.sun_family = AF_UNIX; //UNIX通信类型
6 strcpy(un.sun_path, SKYWALKING_G(sock_path)); //UNIX通信地址
7 int fd;
8 char message[1024]; //内存空间优化
9 char return_message[1024]; //内存空间优化
10
11 fd = socket(AF_UNIX, SOCK_STREAM, 0); //创建client fd
12 if (fd >= 0) {
13 struct timeval tv;
14 tv.tv_sec = 0;
15 tv.tv_usec = 50000; //单位微秒:50ms
16 setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);
17 int conn = connect(fd, (struct sockaddr *) &un, sizeof(un));
18
19 if (conn >= 0) {
20 //进行ServiceName构建:如果环境变量不存在KEY对应的值,则进行环境变量获取
21 char *env_app_code = getenv(SKYWALKING_G(app_code_env_key));
22 if (env_app_code != NULL)
23 {
24 SKYWALKING_G(app_code) = env_app_code;
25 }
26
27 bzero(message, sizeof(message));
28
29 sprintf(message, "0{\"app_code\":\"%s\",\"pid\":%d,\"version\":%d}\n", SKYWALKING_G(app_code),
30 getppid(), SKYWALKING_G(version));
31 write(fd, message, strlen(message));
32
33 bzero(return_message, sizeof(return_message));
34 read(fd, return_message, sizeof(return_message));
35
36 char *ids[10] = {0};
37 int i = 0;
38 char *p = strtok(return_message, ","); //分割握手协议
39 while (p != NULL) {
40 ids[i++] = p;
41 p = strtok(NULL, ",");
42 }
43
44 if (ids[0] != NULL && ids[1] != NULL && ids[2] != NULL) {
45 if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7) {
46 application_id = atoi(ids[0]);
47 application_instance = atoi(ids[1]);
48 strncpy(application_uuid, ids[2], sizeof application_uuid - 1);
49 } else if (SKYWALKING_G(version) == 8) {
50 application_id = 1;
51 application_instance = 1;
52 strncpy(service, ids[0], sizeof service - 1); //拷贝service app_code ,此处和sidecar进行交互,可以定制化 : app_code
53 strncpy(service_instance, ids[1], sizeof service_instance - 1);
54 strncpy(application_uuid, ids[2], sizeof application_uuid - 1);
55 }
56 }
57
58 } else {
59 php_error_docref(NULL, E_WARNING, "[skywalking] failed to connect the socket.");
60 }
61
62 close(fd);
63 } else {
64 php_error_docref(NULL, E_WARNING, "[skywalking] failed to open the socket.");
65 }
66 }
67 return 0;
68}
五.附录
5.1 附录一 : SkyWalking-PHP内核组件的Component列表
1#ifndef SKYWALKING_COMPONENTS_H
2#define SKYWALKING_COMPONENTS_H
3
4#define COMPONENT_TOMCAT 1
5#define COMPONENT_HTTPCLIENT 2
6#define COMPONENT_DUBBO 3
7#define COMPONENT_MOTAN 8
8#define COMPONENT_RESIN 10
9#define COMPONENT_FEIGN 11
10#define COMPONENT_OKHTTP 12
11#define COMPONENT_SPRING_REST_TEMPLATE 13
12#define COMPONENT_SPRING_MVC_ANNOTATION 14
13#define COMPONENT_STRUTS2 15
14#define COMPONENT_NUTZ_MVC_ANNOTATION 16
15#define COMPONENT_NUTZ_HTTP 17
16#define COMPONENT_JETTY_CLIENT 18
17#define COMPONENT_JETTY_SERVER 19
18#define COMPONENT_SHARDING_JDBC 21
19#define COMPONENT_GRPC 23
20#define COMPONENT_ELASTIC_JOB 24
21#define COMPONENT_HTTP_ASYNC_CLIENT 26
22#define COMPONENT_SERVICECOMB 28
23#define COMPONENT_HYSTRIX 29
24#define COMPONENT_JEDIS 30
25#define COMPONENT_H2_JDBC_DRIVER 32
26#define COMPONENT_MYSQL_JDBC_DRIVER 33
27#define COMPONENT_OJDBC 34
28#define COMPONENT_SPYMEMCACHED 35
29#define COMPONENT_XMEMCACHED 36
30#define COMPONENT_POSTGRESQL_DRIVER 37
31#define COMPONENT_ROCKET_MQ_PRODUCER 38
32#define COMPONENT_ROCKET_MQ_CONSUMER 39
33#define COMPONENT_KAFKA_PRODUCER 40
34#define COMPONENT_KAFKA_CONSUMER 41
35#define COMPONENT_MONGO_DRIVER 42
36#define COMPONENT_SOFARPC 43
37#define COMPONENT_ACTIVEMQ_PRODUCER 45
38#define COMPONENT_ACTIVEMQ_CONSUMER 46
39#define COMPONENT_TRANSPORT_CLIENT 48
40#define COMPONENT_UNDERTOW 49
41#define COMPONENT_RPC 50
42
43#endif //SKYWALKING_COMPONENTS_H
5.2 附录二:APM拦截所需的关键函数定义
1static char *sky_json_encode(zval *parameter); //json序列化
2static long get_second(); //获取秒级时间戳
3static char *get_millisecond(); //获取毫秒级时间戳
4static char *generate_sw3(zend_long span_id, char *peer_host, char *operation_name); //生成sw3协议数据
5static char *generate_sw6(zend_long span_id, char *peer_host); //生成sw6协议数据
6static char *generate_sw8(zend_long span_id, char *peer_host); //生成sw8协议数据
7static void generate_context(); //生成上下文
8static char *get_page_request_uri(); //获取request的uri
9static char *get_page_request_peer(); //获取request的主机信息
10static void write_log( char *text); //发送APM日志
11static void request_init();//初始化一次请求
12static void zval_b64_encode(zval *out, char *in); //base64序列化
13static void zval_b64_decode(zval *out, char *in); //base64反序列化
14static char *sky_get_class_name(zval *obj); //获取zval数据结构的class名称
15static zval *sky_read_property(zval *obj, const char *property, int parent); //读取apm监控数据的某个属性
16static char *sky_redis_fnamewall(const char *function_name); //针对redis操作增加函数边界符号 |%s|
17static int sky_redis_opt_for_string_key(char *fnamewall); //查询redis指令是否在APM监控范围内
18
19static void sky_flush_all(); //发送skywalking APM数据包
20static zval *get_first_span(); //获取收个全链路span数据
21static zval *get_spans(); //获取全部的spans数据
22static char* _get_current_machine_ip(); //获取当前机器的IP
23
24static char *sky_memcached_fnamewall(const char *function_name); //针对memcache操作增加函数边界服务 |%s|
25static int sky_memcached_opt_for_string_key(char *fnamewall); //查询memcache指令是否在APM监控范围内
5.3 附录三:APM关键函数hook定义
1//curl函数Hook函数
2void sky_curl_exec_handler(INTERNAL_FUNCTION_PARAMETERS);
3void sky_curl_setopt_handler(INTERNAL_FUNCTION_PARAMETERS);
4void sky_curl_setopt_array_handler(INTERNAL_FUNCTION_PARAMETERS);
5void sky_curl_close_handler(INTERNAL_FUNCTION_PARAMETERS);
6
7static void (*orig_curl_exec)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
8static void (*orig_curl_setopt)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
9static void (*orig_curl_setopt_array)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
10static void (*orig_curl_close)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
5.4 附录三:全局变量定义
1/*
2 Declare any global variables you may need between the BEGIN
3 and END macros here:
4*/
5ZEND_BEGIN_MODULE_GLOBALS(skywalking)
6 char *sock_path; //和sidecar通信的unix地址
7 char *app_code; //服务名称,app_name eg:skywalking.app_code = MyProjectName
8 char *app_code_env_key; //app_name 环境变量地址:环境变量->默认KEY:APM_APP_CODE
9 zend_bool enable; //是否开启内核扩展
10 zval UpstreamSegment; //全局上报数据段
11 zval context; //APM上下文
12 zval curl_header; //curl header数据
13 zval curl_header_send; //记录当前R周期 是否已经send过curl_header
14 int version; //APM 版本号
15ZEND_END_MODULE_GLOBALS(skywalking)
16
17extern ZEND_DECLARE_MODULE_GLOBALS(skywalking);