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定义
static void (*ori_execute_ex)(zend_execute_data *execute_data); //PHP内核原始PHP层执行流程函数指针
static void (*ori_execute_internal)(zend_execute_data *execute_data, zval *return_value);//PHP原始内核执行函数指针
ZEND_API void sky_execute_ex(zend_execute_data *execute_data);//skywalking针对PHP层执行函数的替换指针
ZEND_API void sky_execute_internal(zend_execute_data *execute_data, zval *return_value);//skywalking针对原始内核执行函数的替换指针
2.2.2 php.ini配置解析周期
PHP_INI_BEGIN()
#if SKY_DEBUG
STD_PHP_INI_BOOLEAN("skywalking.enable", "1", PHP_INI_ALL, OnUpdateBool, enable, zend_skywalking_globals, skywalking_globals) //读取skywalking.enable配置项
#else
STD_PHP_INI_BOOLEAN("skywalking.enable", "0", PHP_INI_ALL, OnUpdateBool, enable, zend_skywalking_globals, skywalking_globals) //读取skywalking.enable配置项
#endif
STD_PHP_INI_ENTRY("skywalking.version", "8", PHP_INI_ALL, OnUpdateLong, version, zend_skywalking_globals, skywalking_globals) //读取skywalking 版本配置项
STD_PHP_INI_ENTRY("skywalking.app_code", UNKNOW_SERVICE_NAME, PHP_INI_ALL, OnUpdateString, app_code, zend_skywalking_globals, skywalking_globals) //读取微服务-服务名配置项
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) //读取微服务-服务名环境变量 配置项
STD_PHP_INI_ENTRY("skywalking.sock_path", "/tmp/sky-agent.sock", PHP_INI_ALL, OnUpdateString, sock_path, zend_skywalking_globals, skywalking_globals) //读取微服务-agent通信unix路径配置项
PHP_INI_END()
2.2.3 PHP内核模块初始化周期 - MI周期
MI周期是PHP进程的模块加载周期,这个周期内部会逐个加载内核扩展,并调用内核扩展MI周期的函数Hook,APM内核扩展对于这块的处理逻辑如下:
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION (skywalking) {
ZEND_INIT_MODULE_GLOBALS(skywalking, php_skywalking_init_globals, NULL); //初始化模块变量
//data_register_hashtable();
REGISTER_INI_ENTRIES();
/* If you have INI entries, uncomment these lines
*/
if (SKYWALKING_G(enable)) { //如果内核扩展功能开启了,则进行关键内核函数指针替换。
//屏蔽php的cli运行模式的APM监控
if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
return SUCCESS;
}
// 用户自定义函数执行器(php脚本定义的类、函数)
ori_execute_ex = zend_execute_ex;
zend_execute_ex = sky_execute_ex;
// 内部函数执行器(c语言定义的类、函数)
ori_execute_internal = zend_execute_internal;
zend_execute_internal = sky_execute_internal;
// 托管curl内核函数:从zend函数表寻找内核函数、并使用内部的函数钩子进行拦截:自定函数钩子->exec、setopt、setopt_array、close
zend_function *old_function;
if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_exec", sizeof("curl_exec") - 1)) != NULL) { //替换curl_exec函数
orig_curl_exec = old_function->internal_function.handler;
old_function->internal_function.handler = sky_curl_exec_handler;
}
if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_setopt", sizeof("curl_setopt")-1)) != NULL) { //替换curl_setopt函数
orig_curl_setopt = old_function->internal_function.handler;
old_function->internal_function.handler = sky_curl_setopt_handler;
}
if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_setopt_array", sizeof("curl_setopt_array")-1)) != NULL) { //替换curl_setopt_array函数
orig_curl_setopt_array = old_function->internal_function.handler;
old_function->internal_function.handler = sky_curl_setopt_array_handler;
}
if ((old_function = zend_hash_str_find_ptr(CG(function_table), "curl_close", sizeof("curl_close")-1)) != NULL) { //替换curl_close函数
orig_curl_close = old_function->internal_function.handler;
old_function->internal_function.handler = sky_curl_close_handler;
}
}
return SUCCESS;
}
/* }}} */
2.2.4 PHP内核请求初始化阶段 - RI周期
这个周期是PHP进程针对每次PHP请求入口进行的周期设计,RI周期会针对每次PHP请求均会执行,也是PHP-FPM模式的短生命周期的根源,针对这块源码的解读如下:
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION7
*/
PHP_RINIT_FUNCTION(skywalking)
{
#if defined(COMPILE_DL_SKYWALKING) && defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
if (SKYWALKING_G(enable)) {
if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
return SUCCESS;
}
sky_register(); //向APM-Agent进行服务注册逻辑,这个逻辑仅仅当application_instance == 0条件下执行,
if (application_instance == 0) {
return SUCCESS;
}
sky_increment_id++; //因为PHP-FPM进程是线程安全的,所以此处可以单调的递加,变量主要用于构建Trace-Id
if (sky_increment_id >= 9999) {
sky_increment_id = 0;
}
request_init(); //初始化当前请求的APM数据,里面进行初始上下文、创建Trace、Span信息,及必要的父子Trace关系
}
return SUCCESS;
}
/* }}} */
2.2.5 PHP内核请求结束阶段 - RS周期
这个周期是PHP进程针对每次PHP请求结束进行的周期设计,RS周期会针对每次PHP请求的结束进行拦截,这个阶段一般进行内核资源回收操作:
/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(skywalking)
{
if(SKYWALKING_G(enable)){
if (strcasecmp("cli", sapi_module.name) == 0 && cli_debug == 0) {
return SUCCESS;
}
if (application_instance == 0) {
return SUCCESS;
}
sky_flush_all();//发送APM数据给Agent程序
zval_dtor(&SKYWALKING_G(context)); //析构context hashtable、释放内存
zval_dtor(&SKYWALKING_G(curl_header));//析构curl_header hashtable、释放内存
zval_dtor(&SKYWALKING_G(curl_header_send));//析构curl_header_send hashtable、释放内存
zval_dtor(&SKYWALKING_G(UpstreamSegment));//析构UpstreamSegment hashtable、释放内存
}
return SUCCESS;
}
/* }}} */
三. 关键运行函数Hook分析
除了PHP生命期的关键环节,APM主要就是针对关键IO进行监控,因为这些IO是外部调用依赖的关键,接下来会分析不同的函数Hook的具体实现:
3.1 PHP用户态函数拦截
APM内核组件会对用户态函数进行执行拦截,通过拦截执行过程并构建APM需要的数据。
/* PHP 内核函数执行相关的核心内核数据结构
struct _zend_execute_data {
const zend_op *opline; executed opline
zend_execute_data *call; current call
zval *return_value;
zend_function *func; executed function
zval This; this + call_info + num_args
zend_execute_data *prev_execute_data;
zend_array *symbol_table;
#if ZEND_EX_USE_RUN_TIME_CACHE
void **run_time_cache; cache op_array->run_time_cache
#endif
#if ZEND_EX_USE_LITERALS
zval *literals; cache op_array->literals
#endif
}
*/
// sky-apm 内核扩展注入的函数执行钩子 : 用户态函数拦截
ZEND_API void sky_execute_ex(zend_execute_data *execute_data) {
if (application_instance == 0) { //如果当前请求周期不需要APM采集,则直接走原内核函数钩子
ori_execute_ex(execute_data);
return;
}
zend_function *zf = execute_data->func; //拦截执行的函数
const char *class_name = (zf->common.scope != NULL && zf->common.scope->name != NULL) ? ZSTR_VAL(
zf->common.scope->name) : NULL; //获取class名称
const char *function_name = zf->common.function_name == NULL ? NULL : ZSTR_VAL(zf->common.function_name); //获取function name
char *operationName = NULL; //定义操作名称
char *peer = NULL;
int componentId = 0; //组件Id
if (class_name != NULL) {
if (strcmp(class_name, "Predis\\Client") == 0 && strcmp(function_name, "executeCommand") == 0) {
// 检测predis组件的executeCommand函数
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (arg_count) {
zval *p = ZEND_CALL_ARG(execute_data, 1);
zval *id = (zval *) emalloc(sizeof(zval));
zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getid"), id, 0, NULL, NULL);
if (Z_TYPE_P(id) == IS_STRING) { //构建predis函数类->函数方法的 操作名。
operationName = (char *) emalloc(strlen(class_name) + strlen(Z_STRVAL_P(id)) + 3);
componentId = COMPONENT_JEDIS;
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, Z_STRVAL_P(id));
}
efree(id);
}
} else if (strcmp(class_name, "Grpc\\BaseStub") == 0) { //拦截Grpc组件的关键方法。
if (strcmp(function_name, "_simpleRequest") == 0
|| strcmp(function_name, "_clientStreamRequest") == 0
|| strcmp(function_name, "_serverStreamRequest") == 0
|| strcmp(function_name, "_bidiRequest") == 0
) {
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
if (SKYWALKING_G(version) == 5) {
componentId = COMPONENT_GRPC;
} else {
componentId = COMPONENT_RPC;
}
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
}
}
if (operationName != NULL) { //如果操作名称不为空,证明成功拦截到函数
zval tags;
array_init(&tags);
//构建APM的tags属性,增加数据库类型、客户端组件类型
if (strcmp(class_name, "Predis\\Client") == 0 && strcmp(function_name, "executeCommand") == 0) {
add_assoc_string(&tags, "db.type", "redis");
add_assoc_string(&tags, "component", "predis");
zval *p = ZEND_CALL_ARG(execute_data, 1);
zval *id = (zval *) emalloc(sizeof(zval));
zval *arguments = (zval *) emalloc(sizeof(zval));
zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getid"), id, 0, NULL, NULL);
zend_call_method(p, Z_OBJCE_P(p), NULL, ZEND_STRL("getarguments"), arguments, 0, NULL, NULL);
// 丰富APM的predis的连接peer信息
zval *connection = sky_read_property(&(execute_data->This),"connection", 0);
if (connection != NULL && Z_TYPE_P(connection) == IS_OBJECT && strcmp(sky_get_class_name(connection), "Predis\\Connection\\StreamConnection") == 0) {
zval *parameters = sky_read_property(connection, "parameters", 0);
if (parameters != NULL && Z_TYPE_P(parameters) == IS_OBJECT && strcmp(sky_get_class_name(parameters), "Predis\\Connection\\Parameters") == 0) {
zval *parameters_arr = sky_read_property(parameters, "parameters", 0);
if (Z_TYPE_P(parameters_arr) == IS_ARRAY) {
zval *predis_host = zend_hash_str_find(Z_ARRVAL_P(parameters_arr), "host", sizeof("host") - 1);
zval *predis_port = zend_hash_str_find(Z_ARRVAL_P(parameters_arr), "port", sizeof("port") - 1);
zval *port;
ZVAL_COPY(&port, predis_port);
if (Z_TYPE_P(port) != IS_LONG) {
convert_to_long(port);
}
if (Z_TYPE_P(predis_host) == IS_STRING && Z_TYPE_P(port) == IS_LONG) {
const char *host = ZSTR_VAL(Z_STR_P(predis_host));
peer = (char *) emalloc(strlen(host) + 10);
bzero(peer, strlen(host) + 10);
sprintf(peer, "%s:%" PRId3264, host, Z_LVAL_P(port));
}
}
}
}
// peer end
if (Z_TYPE_P(arguments) == IS_ARRAY) {
zend_ulong num_key;
zval *entry, str_entry;
smart_str command = {0};
smart_str_appends(&command, Z_STRVAL_P(id));
smart_str_appends(&command, " ");
ZEND_HASH_FOREACH_NUM_KEY_VAL(Z_ARRVAL_P(arguments), num_key, entry)
{
switch (Z_TYPE_P(entry)) {
case IS_STRING:
smart_str_appends(&command, Z_STRVAL_P(entry));
smart_str_appends(&command, " ");
break;
case IS_ARRAY:
break;
default:
ZVAL_COPY(&str_entry, entry);
convert_to_string(&str_entry);
smart_str_appends(&command, Z_STRVAL_P(&str_entry));
smart_str_appends(&command, " ");
break;
}
}
ZEND_HASH_FOREACH_END();
// store command to tags
if (command.s) {
smart_str_0(&command);
add_assoc_string(&tags, "redis.command", ZSTR_VAL(command.s));
smart_str_free(&command);
}
}
zval_ptr_dtor(id);
zval_ptr_dtor(arguments);
efree(id);
efree(arguments);
} else if (strcmp(class_name, "Grpc\\BaseStub") == 0) {
add_assoc_string(&tags, "rpc.type", "grpc");
add_assoc_string(&tags, "component", "php-grpc");
zval *p = ZEND_CALL_ARG(execute_data, 1);
if (Z_TYPE_P(p) == IS_STRING) {
add_assoc_string(&tags, "rpc.method", Z_STRVAL_P(p));
}
zval *hostname = sky_read_property(&(execute_data->This), "hostname", 1);
zval *hostname_override = sky_read_property(&(execute_data->This), "hostname_override", 1);
const char *host = NULL;
if (hostname_override != NULL && Z_TYPE_P(hostname_override) == IS_STRING) {
host = ZSTR_VAL(Z_STR_P(hostname_override));
} else if (hostname != NULL && Z_TYPE_P(hostname) == IS_STRING) {
host = ZSTR_VAL(Z_STR_P(hostname));
}
if (host != NULL) {
peer = (char *) emalloc(strlen(host) + 10);
bzero(peer, strlen(host) + 10);
sprintf(peer, "%s", host);
}
}
zval temp;
zval *spans = NULL;
zval *span_id = NULL;
zval *last_span = NULL;
char *l_millisecond;
long millisecond;
array_init(&temp);
spans = get_spans();
last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1);
span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
add_assoc_long(&temp, "parentSpanId", 0);
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_long(&temp, "startTime", millisecond);
add_assoc_long(&temp, "spanType", 1);
add_assoc_long(&temp, "spanLayer", 1);
add_assoc_long(&temp, "componentId", componentId);
add_assoc_string(&temp, "operationName", operationName);
add_assoc_string(&temp, "peer", peer == NULL ? "" : peer);
efree(operationName);
if (peer != NULL) {
efree(peer);
}
ori_execute_ex(execute_data); //前面已经注入了APM原始数据、正式执行原函数钩子
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_zval(&temp, "tags", &tags); //执行完后,注入请求监控的APM-tags
add_assoc_long(&temp, "endTime", millisecond);
add_assoc_long(&temp, "isError", 0);
zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
} else {
ori_execute_ex(execute_data);
}
}
3.2 PHP内核态函数拦截
APM内核组件会对PHP内核态函数进行执行拦截,通过拦截执行过程并构建APM需要的数据,相关源码解释如下:
//PHP非用户态函数拦截:例如PHP内核、PHP其他内核扩展
ZEND_API void sky_execute_internal(zend_execute_data *execute_data, zval *return_value) {
if (application_instance == 0) {
if (ori_execute_internal) {
ori_execute_internal(execute_data, return_value);
} else {
execute_internal(execute_data, return_value);
}
return;
}
zend_function *zf = execute_data->func;
const char *class_name = (zf->common.scope != NULL && zf->common.scope->name != NULL) ? ZSTR_VAL(
zf->common.scope->name) : NULL; //获取函数所属的class名称
const char *function_name = zf->common.function_name == NULL ? NULL : ZSTR_VAL(zf->common.function_name);//获取函数名称
int is_procedural_mysqli = 0; // "Procedural style" or "Object oriented style" ?
char *operationName = NULL;
char *peer = NULL;
char *component = NULL;
int componentId = COMPONENT_MYSQL_JDBC_DRIVER;
if (class_name != NULL) {
if (strcmp(class_name, "PDO") == 0) { //拦截PDO类
if (strcmp(function_name, "exec") == 0
|| strcmp(function_name, "query") == 0
|| strcmp(function_name, "prepare") == 0
|| strcmp(function_name, "commit") == 0) {
component = (char *) emalloc(strlen("PDO") + 1);
strcpy(component, "PDO");
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3); //构建PDO操作名称
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
} else if (strcmp(class_name, "PDOStatement") == 0) { //拦截PDOStatement类操作
if (strcmp(function_name, "execute") == 0) {
component = (char *) emalloc(strlen("PDOStatement") + 1);
strcpy(component, "PDOStatement");
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3); //构建PDOStatement操作名称
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
} else if (strcmp(class_name, "mysqli") == 0) {//拦截mysqli类操作
if (strcmp(function_name, "query") == 0) {
component = (char *) emalloc(strlen("mysqli") + 1);
strcpy(component, "mysqli");
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
} else if (strcmp(class_name, "Yar_Client") == 0) { //拦截Yar_Client类操作
if (strcmp(function_name, "__call") == 0) {
if (SKYWALKING_G(version) == 5) {
componentId = COMPONENT_GRPC;
} else {
componentId = COMPONENT_RPC;
}
component = (char *) emalloc(strlen("Yar_Client") + 1);
strcpy(component, "Yar_Client");
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (arg_count) {
zval *p = ZEND_CALL_ARG(execute_data, 1);
if (Z_TYPE_P(p) == IS_STRING) {
operationName = (char *) emalloc(strlen(class_name) + strlen(Z_STRVAL_P(p)) + 3);
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, Z_STRVAL_P(p));
}
}
}
} else if (strcmp(class_name, "Redis") == 0 || strcmp(class_name, "RedisCluster") == 0) { //拦截phpredis内核组件
char *fnamewall = sky_redis_fnamewall(function_name);
if (sky_redis_opt_for_string_key(fnamewall) == 1) {
componentId = COMPONENT_JEDIS;
component = (char *) emalloc(strlen("Redis") + 1);
strcpy(component, "Redis");
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
efree(fnamewall);
} else if (strcmp(class_name, "Memcached") == 0) {//拦截Memcached内核组件
char *fnamewall = sky_memcached_fnamewall(function_name);
if (sky_memcached_opt_for_string_key(fnamewall) == 1) {
componentId = COMPONENT_XMEMCACHED;
component = (char *) emalloc(strlen("memcached") + 1);
strcpy(component, "memcached");
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
}
efree(fnamewall);
}
} else if (function_name != NULL) {
if (strcmp(function_name, "mysqli_query") == 0) {//拦截mysqli_query函数
class_name = "mysqli";
function_name = "query";
component = (char *) emalloc(strlen(class_name) + 1);
strcpy(component, class_name);
operationName = (char *) emalloc(strlen(class_name) + strlen(function_name) + 3);
strcpy(operationName, class_name);
strcat(operationName, "->");
strcat(operationName, function_name);
is_procedural_mysqli = 1;
}
}
//如果在控制范围内的函数行为
if (operationName != NULL) {
zval tags;
array_init(&tags);
if (strcmp(class_name, "PDO") == 0) {//丰富PDO span tags
add_assoc_string(&tags, "component", "PHP-PDO");
// params
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (arg_count) {
zval *p = ZEND_CALL_ARG(execute_data, 1);
//db.statement
switch (Z_TYPE_P(p)) {
case IS_STRING:
add_assoc_string(&tags, "db.statement", Z_STRVAL_P(p));//丰富PDO 查询语句
break;
}
}
char db_type[64] = {0};
pdo_dbh_t *dbh = Z_PDO_DBH_P(&(execute_data->This));
if (dbh != NULL) {
if (dbh->driver != NULL && dbh->driver->driver_name != NULL) {
memcpy(db_type, (char *) dbh->driver->driver_name, dbh->driver->driver_name_len);
add_assoc_string(&tags, "db.type", db_type);
}
if (dbh->data_source != NULL && db_type[0] != '\0') {
add_assoc_string(&tags, "db.data_source", (char *) dbh->data_source);
char *host = pcre_match("(host=([^;\\s]+))", sizeof("(host=([^;\\s]+))")-1, (char *) dbh->data_source);
char *port = pcre_match("(port=([^;\\s]+))", sizeof("(port=([^;\\s]+))")-1, (char *) dbh->data_source);
if (host != NULL && port != NULL) {
peer = (char *) emalloc(strlen(host) + 10);
bzero(peer, strlen(host) + 10);
sprintf(peer, "%s:%s", host, port);
}
if (host != NULL) efree(host);
if (port != NULL) efree(port);
}
}
} else if (strcmp(class_name, "PDOStatement") == 0) {
char db_type[64] = {0};
pdo_stmt_t *stmt = (pdo_stmt_t *) Z_PDO_STMT_P(&(execute_data->This));
if (stmt != NULL) {
add_assoc_string(&tags, "db.statement", stmt->query_string);
add_assoc_string(&tags, "component", "PHP-PDO");
if (stmt->dbh != NULL && stmt->dbh->driver->driver_name != NULL) {
memcpy(db_type, (char *) stmt->dbh->driver->driver_name, stmt->dbh->driver->driver_name_len);
add_assoc_string(&tags, "db.type", db_type);
}
if (db_type[0] != '\0' && stmt->dbh != NULL && stmt->dbh->data_source != NULL) {
add_assoc_string(&tags, "db.data_source", (char *) stmt->dbh->data_source);
char *host = pcre_match("(host=([^;\\s]+))", sizeof("(host=([^;\\s]+))")-1, (char *) stmt->dbh->data_source);
char *port = pcre_match("(port=([^;\\s]+))", sizeof("(port=([^;\\s]+))")-1, (char *) stmt->dbh->data_source);
if (host != NULL && port != NULL) {
peer = (char *) emalloc(strlen(host) + 10);
bzero(peer, strlen(host) + 10);
sprintf(peer, "%s:%s", host, port);
}
if (host != NULL) efree(host);
if (port != NULL) efree(port);
}
}
} else if (strcmp(class_name, "mysqli") == 0) {
if (strcmp(function_name, "query") == 0) {
#ifdef MYSQLI_USE_MYSQLND
mysqli_object *mysqli = NULL;
if(is_procedural_mysqli) {
mysqli = (mysqli_object *) Z_MYSQLI_P(ZEND_CALL_ARG(execute_data, 1));
} else {
mysqli = (mysqli_object *) Z_MYSQLI_P(&(execute_data->This));
}
MYSQLI_RESOURCE *my_res = (MYSQLI_RESOURCE *) mysqli->ptr;
if (my_res && my_res->ptr) {
MY_MYSQL *mysql = (MY_MYSQL *) my_res->ptr;
if (mysql->mysql) {
#if PHP_VERSION_ID >= 70100
char *host = mysql->mysql->data->hostname.s;
#else
char *host = mysql->mysql->data->host;
#endif
char port[6];
char nullHost[10] = "nullhost\0";
sprintf(port, "%d", mysql->mysql->data->port);
add_assoc_string(&tags, "db.port", port);
if(host != NULL){
add_assoc_string(&tags, "db.host", host);
peer = (char *)emalloc(strlen(host) + 10);
bzero(peer, strlen(host) + 10);
sprintf(peer, "%s:%d", host, mysql->mysql->data->port);
}else{
add_assoc_string(&tags, "db.host", nullHost);
peer = (char *)emalloc(strlen(nullHost) + 10);
bzero(peer, strlen(nullHost) + 10);
sprintf(peer, "%s:%d", nullHost, mysql->mysql->data->port);
}
}
}
#endif
add_assoc_string(&tags, "db.type", "mysql");
add_assoc_string(&tags, "component", "PHP-mysqli");
// params
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (arg_count) {
zval *p = is_procedural_mysqli ? ZEND_CALL_ARG(execute_data, 2) : ZEND_CALL_ARG(execute_data, 1);
//db.statement
switch (Z_TYPE_P(p)) {
case IS_STRING:
add_assoc_string(&tags, "db.statement", Z_STRVAL_P(p));
break;
}
}
}
} else if (strcmp(class_name, "Yar_Client") == 0) {
if (strcmp(function_name, "__call") == 0) {
zval rv, _uri;
ZVAL_STRING(&_uri, "_uri");
zval *yar_uri = Z_OBJ_HT(EX(This))->read_property(&EX(This), &_uri, BP_VAR_R, 0, &rv);
add_assoc_string(&tags, "component", "PHP-Yar");
add_assoc_string(&tags, "yar.uri", Z_STRVAL_P(yar_uri));
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (arg_count) {
zval *p = ZEND_CALL_ARG(execute_data, 1);
if (Z_TYPE_P(p) == IS_STRING) {
add_assoc_string(&tags, "yar.method", Z_STRVAL_P(p));
}
}
}
} else if (strcmp(class_name, "Redis") == 0 || strcmp(class_name, "RedisCluster") == 0) { //丰富phpredis 操作span数据
add_assoc_string(&tags, "db.type", "redis");
add_assoc_string(&tags, "component", "phpredis");
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
if (strcmp(class_name, "Redis") == 0) {
// find peer
zval *this = &(execute_data->This);
zval host;
zval port;
zend_call_method(this, Z_OBJCE_P(this), NULL, ZEND_STRL("gethost"), &host, 0, NULL, NULL);
zend_call_method(this, Z_OBJCE_P(this), NULL, ZEND_STRL("getport"), &port, 0, NULL, NULL);
if (!Z_ISUNDEF(host) && !Z_ISUNDEF(port) && Z_TYPE(host) == IS_STRING && Z_TYPE(port) == IS_LONG) {
const char *h = ZSTR_VAL(Z_STR(host));
peer = (char *) emalloc(strlen(h) + 10);
bzero(peer, strlen(h) + 10);
sprintf(peer, "%s:%" PRId3264, h, Z_LVAL(port));
}
if (!Z_ISUNDEF(host)) {
zval_ptr_dtor(&host);
}
if (!Z_ISUNDEF(port)) {
zval_ptr_dtor(&port);
}
}
smart_str command = {0};
char *fname = zend_str_tolower_dup((char *) function_name, strlen((char *) function_name));
smart_str_appends(&command, fname);
smart_str_appends(&command, " ");
efree(fname);
int is_string_command = 1;
int i;
for (i = 1; i < arg_count + 1; ++i) {
zval str_p;
zval *p = ZEND_CALL_ARG(execute_data, i);
if (Z_TYPE_P(p) == IS_ARRAY) {
is_string_command = 0;
break;
}
ZVAL_COPY(&str_p, p);
if (Z_TYPE_P(&str_p) != IS_STRING) {
convert_to_string(&str_p);
}
if (i == 1) {
add_assoc_string(&tags, "redis.key", Z_STRVAL_P(&str_p));//丰富phpredis key数据
}
char *tmp = zend_str_tolower_dup(Z_STRVAL_P(&str_p), Z_STRLEN_P(&str_p));
smart_str_appends(&command, tmp);
smart_str_appends(&command, " ");
efree(tmp);
}
// store command to tags
if (command.s) {
smart_str_0(&command);
if (is_string_command) {
zend_string *trim_s = php_trim(command.s, NULL, 0, 3);
add_assoc_string(&tags, "redis.command", ZSTR_VAL(trim_s));//丰富phpredis 指令数据
zend_string_free(trim_s);
}
smart_str_free(&command);
}
} else if (strcmp(class_name, "Memcached") == 0) {
add_assoc_string(&tags, "db.type", "memcached");
add_assoc_string(&tags, "component", "PHP-Memcached");
uint32_t arg_count = ZEND_CALL_NUM_ARGS(execute_data);
smart_str command = {0};
smart_str_appends(&command, zend_str_tolower_dup((char *) function_name, strlen((char *) function_name)));
smart_str_appends(&command, " ");
int i;
for (i = 1; i < arg_count + 1; ++i) {
char *str = NULL;
zval str_p;
zval *p = ZEND_CALL_ARG(execute_data, i);
if (Z_TYPE_P(p) == IS_ARRAY) {
str = sky_json_encode(p);
}
ZVAL_COPY(&str_p, p);
if (Z_TYPE_P(&str_p) != IS_ARRAY && Z_TYPE_P(&str_p) != IS_STRING) {
convert_to_string(&str_p);
}
if (str == NULL) {
str = Z_STRVAL_P(&str_p);
}
if (i == 1) {
add_assoc_string(&tags, "memcached.key", str);
}
smart_str_appends(&command, zend_str_tolower_dup(str, strlen(str)));
smart_str_appends(&command, " ");
}
// store command to tags
if (command.s) {
smart_str_0(&command);
add_assoc_string(&tags, "memcached.command", ZSTR_VAL(php_trim(command.s, NULL, 0, 3)));
smart_str_free(&command);
}
}
zval temp;
zval *spans = NULL;
zval *span_id = NULL;
zval *last_span = NULL;
char *l_millisecond;
long millisecond;
array_init(&temp);
spans = get_spans();
last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1);
span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
add_assoc_long(&temp, "parentSpanId", 0);
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_long(&temp, "startTime", millisecond);
add_assoc_long(&temp, "spanType", 1);
add_assoc_long(&temp, "spanLayer", 1);
// add_assoc_string(&temp, "component", component);
add_assoc_long(&temp, "componentId", componentId);
add_assoc_string(&temp, "operationName", operationName);
add_assoc_string(&temp, "peer", peer == NULL ? "" : peer);
efree(component);
efree(operationName);
if (peer != NULL) {
efree(peer);
}
//执行PHP内核原函数指针
if (ori_execute_internal) {
ori_execute_internal(execute_data, return_value);
} else {
execute_internal(execute_data, return_value);
}
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_zval(&temp, "tags", &tags);
add_assoc_long(&temp, "endTime", millisecond);
add_assoc_long(&temp, "isError", 0);
zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
} else {
if (ori_execute_internal) {
ori_execute_internal(execute_data, return_value);
} else {
execute_internal(execute_data, return_value);
}
}
}
3.3 PHP-CURL 内核Hook拦截
//curl执行拦截
void sky_curl_exec_handler(INTERNAL_FUNCTION_PARAMETERS)
{
if(application_instance == 0) {
orig_curl_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU);
return;
}
zval *zid;
if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE) {
return;
}
int is_send = 1;
zval function_name,curlInfo;
zval params[1];
ZVAL_COPY(¶ms[0], zid);
//执行curl_getinfo函数:
ZVAL_STRING(&function_name, "curl_getinfo");
call_user_function(CG(function_table), NULL, &function_name, &curlInfo, 1, params); //从CG全局函数表、调用curl_getinfo函数
zval_dtor(&function_name);
zval_dtor(¶ms[0]);
//获取curl请求的url信息
zval *z_url = zend_hash_str_find(Z_ARRVAL(curlInfo), ZEND_STRL("url"));
char *url_str = Z_STRVAL_P(z_url);
if(strlen(url_str) <= 0) {
zval_dtor(&curlInfo);
is_send = 0;
}
//构建标准的HTTP URI结构
php_url *url_info = NULL;
if(is_send == 1) {
url_info = php_url_parse(url_str); //解析
if(url_info->scheme == NULL || url_info->host == NULL) {
zval_dtor(&curlInfo);
php_url_free(url_info); //释放内存
is_send = 0;
}
}
char *sw = NULL;
zval *spans = NULL;
zval *last_span = NULL;
zval *span_id = NULL;
char *peer = NULL;
char *operation_name = NULL;
char *full_url = NULL;
//确定要发送的请求、当前请求合理,才进行下面的信息丰富。
if (is_send == 1) {
// for php7.3.0+
#if PHP_VERSION_ID >= 70300
char *php_url_scheme = ZSTR_VAL(url_info->scheme);
char *php_url_host = ZSTR_VAL(url_info->host);
char *php_url_path = ZSTR_VAL(url_info->path);
char *php_url_query = ZSTR_VAL(url_info->query);
#else
char *php_url_scheme = url_info->scheme;
char *php_url_host = url_info->host;
char *php_url_path = url_info->path;
char *php_url_query = url_info->query;
#endif
//处理url_info内部的被调用方端口值
int peer_port = 0;
if (url_info->port) {
peer_port = url_info->port;
} else {
if (strcasecmp("http", php_url_scheme) == 0) { //默认端口设置
peer_port = 80;
} else {
peer_port = 443;
}
}
//处理url_info内部的query参数
if (url_info->query){
if (url_info->path == NULL) {
spprintf(&operation_name, 0, "%s", "/");
spprintf(&full_url, 0, "%s?%s", "/", php_url_query);
} else {
spprintf(&operation_name, 0, "%s", php_url_path);
spprintf(&full_url, 0, "%s?%s", php_url_path, php_url_query);
}
}
else
{
if (url_info->path == NULL) {
spprintf(&operation_name, 0, "%s", "/");
spprintf(&full_url, 0, "%s", "/");
} else {
spprintf(&operation_name, 0, "%s", php_url_path);
spprintf(&full_url, 0, "%s", php_url_path);
}
}
spans = get_spans(); //获取spans数组
last_span = zend_hash_index_find(Z_ARRVAL_P(spans), zend_hash_num_elements(Z_ARRVAL_P(spans)) - 1); //获取最后一个span
span_id = zend_hash_str_find(Z_ARRVAL_P(last_span), "spanId", sizeof("spanId") - 1);
//生成新的Trace-Span
if (SKYWALKING_G(version) == 5)
{ // skywalking 5.x
spprintf(&peer, 0, "%s://%s:%d", php_url_scheme, php_url_host, peer_port);
sw = generate_sw3(Z_LVAL_P(span_id) + 1, peer, operation_name);
}
else if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7)
{ // skywalking 6.x
spprintf(&peer, 0, "%s:%d", php_url_host, peer_port);
sw = generate_sw6(Z_LVAL_P(span_id) + 1, peer);
}
else if (SKYWALKING_G(version) == 8)
{
spprintf(&peer, 0, "%s:%d", php_url_host, peer_port); //
sw = generate_sw8(Z_LVAL_P(span_id) + 1, peer); //生成version 8的span
}
}
/*
sw6_l = snprintf(NULL, 0, "sw8: 1-%s-%s-%" PRId3264 "-%s-%s-%s-%s",
Z_STRVAL(traceIdEncode),
Z_STRVAL(currentTraceIdEncode),
span_id,
Z_STRVAL(serviceEncode),
Z_STRVAL(serviceInstanceEncode),
Z_STRVAL(parentEndpointEncode),
Z_STRVAL(targetAddressEncode));
*/
//如果span创建成功 : sw结构样例:
//sw8: 1-MS41NDY0LjE1OTg1MDU3ODEwMDAx-MS41NDY0LjE1OTg1MDU3ODEwMDAx-1-bG9jYWxfcGhw-YjFmN2QzNTctZTNkYi00ODkzLTk1NjktY2ZmOTczNWNlNDRh-L3NjX2N1cmxfcG9zdC5waHA=-MTI3LjAuMC4xOjkwOTA=
if (sw != NULL) {
zval *option = NULL;
int is_init = 0;
option = zend_hash_index_find(Z_ARRVAL_P(&SKYWALKING_G(curl_header)), Z_RES_HANDLE_P(zid));
if(option == NULL) {
option = emalloc(sizeof(zval));
bzero(option, sizeof(zval));
array_init(option);
is_init = 1;
}
add_next_index_string(option, sw); //sw字符串为sw8: 打头,所以天然就是HTTP header
add_index_bool(&SKYWALKING_G(curl_header_send), (zend_ulong)Z_RES_HANDLE_P(zid), IS_TRUE);
zval func;
zval argv[3];
zval ret;
ZVAL_STRING(&func, "curl_setopt");
ZVAL_COPY(&argv[0], zid);
ZVAL_LONG(&argv[1], CURLOPT_HTTPHEADER);
ZVAL_COPY(&argv[2], option);
call_user_function(CG(function_table), NULL, &func, &ret, 3, argv); //给curl header调用注入全链路Trace信息
zval_dtor(&ret);
zval_dtor(&func);
if(is_init == 1) {
zval_ptr_dtor(option);
efree(option);
}
zval_dtor(&argv[0]);
zval_dtor(&argv[1]);
zval_dtor(&argv[2]);
efree(sw);
}
zval temp;
char *l_millisecond;
long millisecond;
if(is_send == 1) {
array_init(&temp);
add_assoc_long(&temp, "spanId", Z_LVAL_P(span_id) + 1);
add_assoc_long(&temp, "parentSpanId", 0);
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_long(&temp, "startTime", millisecond);
add_assoc_long(&temp, "spanType", 1);
add_assoc_long(&temp, "spanLayer", 3);
add_assoc_long(&temp, "componentId", COMPONENT_HTTPCLIENT);
}
orig_curl_exec(INTERNAL_FUNCTION_PARAM_PASSTHRU); //执行原生的curl函数
if (is_send == 1) {
//结束时间获取
l_millisecond = get_millisecond();
millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
zval function_name_1, curlInfo_1;
zval params_1[1];
ZVAL_COPY(¶ms_1[0], zid);
ZVAL_STRING(&function_name_1, "curl_getinfo");
call_user_function(CG(function_table), NULL, &function_name_1, &curlInfo_1, 1, params_1);
zval_dtor(¶ms_1[0]);
zval_dtor(&function_name_1);
add_assoc_long(&temp, "endTime", millisecond);
add_assoc_string(&temp, "operationName", operation_name);
add_assoc_string(&temp, "peer", peer);
zval tags;
array_init(&tags);
add_assoc_string(&tags, "url", full_url);
add_assoc_string(&tags, "component", "PHP-CURL");
//HTTP 响应code
zval *z_http_code = zend_hash_str_find(Z_ARRVAL(curlInfo_1), ZEND_STRL("http_code"));
if (Z_LVAL_P(z_http_code) != 200){
add_assoc_long(&temp, "isError", 1);
}
else
{
add_assoc_long(&temp, "isError", 0);
}
//针对http_code进行类型转化,便于增加tag
if (Z_TYPE_P(z_http_code) == IS_LONG)
{
convert_to_string(z_http_code); //针对z_http_code进行类型转换
add_assoc_string(&tags, "http.status_code", Z_STRVAL_P(z_http_code)); //追加 reponse http code tag
}
add_assoc_zval(&temp, "tags", &tags);
efree(peer);
efree(operation_name);
efree(full_url);
php_url_free(url_info);
zval _refs;
array_init(&_refs);
add_assoc_zval(&temp, "refs", &_refs);
zend_hash_next_index_insert(Z_ARRVAL_P(spans), &temp);
zval_dtor(&curlInfo_1);
zval_dtor(&curlInfo);
}
}
四. 关键附属函数分析
4.1 全局变量初始化函数
//初始化内核扩展全局变量
static void php_skywalking_init_globals(zend_skywalking_globals *skywalking_globals)
{
skywalking_globals->app_code = NULL;
skywalking_globals->enable = 0;
skywalking_globals->version = 6;
skywalking_globals->sock_path = "/var/run/sky-agent.sock";
skywalking_globals->app_code_env_key = "APM_APP_CODE";
}
4.2 json序列化函数
static char *sky_json_encode(zval *parameter){
smart_str buf = {0};
zend_long options = 64;
#if PHP_VERSION_ID >= 70100
if (php_json_encode(&buf, parameter, (int)options) != SUCCESS) {
smart_str_free(&buf);
return NULL;
}
#else
php_json_encode(&buf, parameter, (int)options);
#endif
smart_str_0(&buf);
if(buf.s != NULL) {
char *bufs = emalloc(strlen(ZSTR_VAL(buf.s)) + 1);
strcpy(bufs, ZSTR_VAL(buf.s));
smart_str_free(&buf);
return bufs;
}
return NULL;
}
4.3 发送APM数据函数
static void write_log(char *text) {
if (application_instance != 0) {
// to stream
if(text == NULL || strlen(text) <= 0) {
return;
}
struct sockaddr_un un;
un.sun_family = AF_UNIX;
strcpy(un.sun_path, SKYWALKING_G(sock_path));
int fd;
char *message = (char*) emalloc(strlen(text) + 10);
bzero(message, strlen(text) + 10);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd >= 0) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof tv);
int conn = connect(fd, (struct sockaddr *) &un, sizeof(un));
if (conn >= 0) {
sprintf(message, "1%s\n", text);
write(fd, message, strlen(message));
} else {
php_error_docref(NULL, E_WARNING, "[skywalking] failed to connect the sock.");
}
close(fd);
} else {
php_error_docref(NULL, E_WARNING, "[skywalking] failed to open the sock.");
}
efree(message);
efree(text);
}
}
4.4 生成上下文函数
static void generate_context() {
int sys_pid = getpid();
long second = get_second();
second = second * 10000 + sky_increment_id; //创建traceid的因子
char *makeTraceId;
makeTraceId = (char *) emalloc(sizeof(char) * 180); //分配traceId所需要的内存
bzero(makeTraceId, sizeof(char) * 180);
sprintf(makeTraceId, "%d.%d.%ld", application_instance, sys_pid, second);
add_assoc_string(&SKYWALKING_G(context), "currentTraceId", makeTraceId); //针对上下文context变量添加currentTraceId元素
add_assoc_long(&SKYWALKING_G(context), "isChild", 0);
// parent
zval *carrier = NULL;
zval *sw;
zend_bool jit_initialization = PG(auto_globals_jit); //PHP全局变量
if (jit_initialization) {
zend_string *server_str = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
zend_is_auto_global(server_str);
zend_string_release(server_str);
}
carrier = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER")); //获取查找$_SERVER超级全局变量
if(SKYWALKING_G(version) == 5) {
sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW3", sizeof("HTTP_SW3") - 1);
if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) {
add_assoc_string(&SKYWALKING_G(context), "sw3", Z_STRVAL_P(sw));
zval temp;
array_init(&temp);
php_explode(zend_string_init(ZEND_STRL("|"), 0), Z_STR_P(sw), &temp, 10);
if(zend_array_count(Z_ARRVAL_P(&temp)) >= 8) {
zval *sw3_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
zval *sw3_1 = zend_hash_index_find(Z_ARRVAL(temp), 1);
zval *sw3_2 = zend_hash_index_find(Z_ARRVAL(temp), 2);
zval *sw3_3 = zend_hash_index_find(Z_ARRVAL(temp), 3);
zval *sw3_4 = zend_hash_index_find(Z_ARRVAL(temp), 4);
zval *sw3_5 = zend_hash_index_find(Z_ARRVAL(temp), 5);
zval *sw3_6 = zend_hash_index_find(Z_ARRVAL(temp), 6);
zval *sw3_7 = zend_hash_index_find(Z_ARRVAL(temp), 7);
zval child;
array_init(&child);
ZVAL_LONG(&child, 1);
zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child);
add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL_P(sw3_0));
add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw3_1), sizeof(Z_STRVAL_P(sw3_1)) - 1));
add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", zend_atol(Z_STRVAL_P(sw3_2), sizeof(Z_STRVAL_P(sw3_2)) - 1));
add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", zend_atol(Z_STRVAL_P(sw3_3), sizeof(Z_STRVAL_P(sw3_3)) - 1));
add_assoc_str(&SKYWALKING_G(context), "networkAddress", trim_sharp(sw3_4));
add_assoc_str(&SKYWALKING_G(context), "entryOperationName", trim_sharp(sw3_5));
add_assoc_str(&SKYWALKING_G(context), "parentOperationName", trim_sharp(sw3_6));
add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", Z_STRVAL_P(sw3_7));
}
} else {
add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", application_instance);
add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", application_instance);
char *uri = get_page_request_uri();
add_assoc_string(&SKYWALKING_G(context), "entryOperationName", (uri == NULL) ? "" : uri);
add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", makeTraceId);
if(uri != NULL) {
efree(uri);
}
}
} else if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7) {
sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW6", sizeof("HTTP_SW6") - 1);
if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) {
add_assoc_string(&SKYWALKING_G(context), "sw6", Z_STRVAL_P(sw));
zval temp;
array_init(&temp);
php_explode(zend_string_init(ZEND_STRL("-"), 0), Z_STR_P(sw), &temp, 10);
if(zend_array_count(Z_ARRVAL_P(&temp)) >= 7) {
zval *sw6_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
zval *sw6_1 = zend_hash_index_find(Z_ARRVAL(temp), 1); // Trace Id
zval *sw6_2 = zend_hash_index_find(Z_ARRVAL(temp), 2); // Parent trace segment Id
zval *sw6_3 = zend_hash_index_find(Z_ARRVAL(temp), 3); // Parent span Id
zval *sw6_4 = zend_hash_index_find(Z_ARRVAL(temp), 4); // Parent service instance Id
zval *sw6_5 = zend_hash_index_find(Z_ARRVAL(temp), 5); // Entrance service instance Id
zval *sw6_6 = zend_hash_index_find(Z_ARRVAL(temp), 6); // Target address of this request
zval *sw6_7 = NULL;
zval *sw6_8 = NULL;
if (zend_array_count(Z_ARRVAL_P(&temp)) >= 9) {
sw6_7 = zend_hash_index_find(Z_ARRVAL(temp), 7);
sw6_8 = zend_hash_index_find(Z_ARRVAL(temp), 8);
}
zval child;
array_init(&child);
ZVAL_LONG(&child, 1)
zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child);
zval sw6_1decode;
zval sw6_2decode;
zval sw6_6decode;
zval_b64_decode(&sw6_1decode, Z_STRVAL_P(sw6_1));
zval_b64_decode(&sw6_2decode, Z_STRVAL_P(sw6_2));
zval_b64_decode(&sw6_6decode, Z_STRVAL_P(sw6_6));
add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL(sw6_2decode));
add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw6_3), sizeof(Z_STRVAL_P(sw6_3)) - 1));
add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", zend_atol(Z_STRVAL_P(sw6_4), sizeof(Z_STRVAL_P(sw6_4)) - 1));
add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", zend_atol(Z_STRVAL_P(sw6_5), sizeof(Z_STRVAL_P(sw6_5)) - 1));
add_assoc_string(&SKYWALKING_G(context), "networkAddress", Z_STRVAL(sw6_6decode));
if (sw6_7 != NULL && sw6_8 != NULL) {
zval sw6_7decode;
zval sw6_8decode;
zval_b64_decode(&sw6_7decode, Z_STRVAL_P(sw6_7));
zval_b64_decode(&sw6_8decode, Z_STRVAL_P(sw6_8));
add_assoc_string(&SKYWALKING_G(context), "entryOperationName", Z_STRVAL(sw6_7decode));
add_assoc_string(&SKYWALKING_G(context), "parentOperationName", Z_STRVAL(sw6_8decode));
zval_dtor(&sw6_7decode);
zval_dtor(&sw6_8decode);
}
add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", Z_STRVAL(sw6_1decode));
zval_dtor(&sw6_1decode);
zval_dtor(&sw6_2decode);
zval_dtor(&sw6_6decode);
}
} else {
add_assoc_long(&SKYWALKING_G(context), "parentApplicationInstance", application_instance);
add_assoc_long(&SKYWALKING_G(context), "entryApplicationInstance", application_instance);
char *uri = get_page_request_uri();
char *path = NULL;
if (uri != NULL) {
path = (char *)emalloc(strlen(uri) + 5);
bzero(path, strlen(uri) + 5);
int i;
for(i = 0; i < strlen(uri); i++) {
if (uri[i] == '?') {
break;
}
path[i] = uri[i];
}
path[i] = '\0';
}
add_assoc_string(&SKYWALKING_G(context), "entryOperationName", (path == NULL) ? "" : path);
if (path != NULL) {
efree(path);
}
add_assoc_string(&SKYWALKING_G(context), "distributedTraceId", makeTraceId);
if(uri != NULL) {
efree(uri);
}
}
} else if (SKYWALKING_G(version) == 8) {
sw = zend_hash_str_find(Z_ARRVAL_P(carrier), "HTTP_SW8", sizeof("HTTP_SW8") - 1); //$SERVER['HTTP_SW8'];
if (sw != NULL && Z_TYPE_P(sw) == IS_STRING && Z_STRLEN_P(sw) > 10) { //从header头部拿到了trace信息
add_assoc_string(&SKYWALKING_G(context), "sw8", Z_STRVAL_P(sw));
zval temp;
array_init(&temp); //初始化数组元素
php_explode(zend_string_init(ZEND_STRL("-"), 0), Z_STR_P(sw), &temp, 10);
if(zend_array_count(Z_ARRVAL_P(&temp)) >= 7) {
zval *sw8_0 = zend_hash_index_find(Z_ARRVAL(temp), 0);
zval *sw8_1 = zend_hash_index_find(Z_ARRVAL(temp), 1); // Trace Id base64
zval *sw8_2 = zend_hash_index_find(Z_ARRVAL(temp), 2); // Parent trace segment Id
zval *sw8_3 = zend_hash_index_find(Z_ARRVAL(temp), 3); // Parent span Id
zval *sw8_4 = zend_hash_index_find(Z_ARRVAL(temp), 4); // Parent service
zval *sw8_5 = zend_hash_index_find(Z_ARRVAL(temp), 5); // Parent service instance
zval *sw8_6 = zend_hash_index_find(Z_ARRVAL(temp), 6); // Parent endpoint
zval *sw8_7 = zend_hash_index_find(Z_ARRVAL(temp), 7); // Target address used at client side of this request
zval child;
array_init(&child);
ZVAL_LONG(&child, 1)
zend_hash_str_update(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1, &child); //因为当前HTTP SERVER有TraceId,所以链路存在父对象、因此切换当前上下文状态为child状态。
zval sw8_1decode;
zval sw8_2decode;
zval sw8_4decode;
zval sw8_5decode;
zval sw8_6decode;
zval sw8_7decode;
zval_b64_decode(&sw8_1decode, Z_STRVAL_P(sw8_1));
zval_b64_decode(&sw8_2decode, Z_STRVAL_P(sw8_2));
zval_b64_decode(&sw8_4decode, Z_STRVAL_P(sw8_4));
zval_b64_decode(&sw8_5decode, Z_STRVAL_P(sw8_5));
zval_b64_decode(&sw8_6decode, Z_STRVAL_P(sw8_6));
zval_b64_decode(&sw8_7decode, Z_STRVAL_P(sw8_7));
//针对当前请求的上下文丰富相关链路数据。
add_assoc_string(&SKYWALKING_G(context), "traceId", Z_STRVAL(sw8_1decode));
add_assoc_string(&SKYWALKING_G(context), "parentTraceSegmentId", Z_STRVAL(sw8_2decode));
add_assoc_long(&SKYWALKING_G(context), "parentSpanId", zend_atol(Z_STRVAL_P(sw8_3), sizeof(Z_STRVAL_P(sw8_3)) - 1));
add_assoc_string(&SKYWALKING_G(context), "parentService", Z_STRVAL(sw8_4decode));
add_assoc_string(&SKYWALKING_G(context), "parentServiceInstance", Z_STRVAL(sw8_5decode));
add_assoc_string(&SKYWALKING_G(context), "parentEndpoint", Z_STRVAL(sw8_6decode));
add_assoc_string(&SKYWALKING_G(context), "targetAddress", Z_STRVAL(sw8_7decode));
//释放转码相关内存
zval_dtor(&sw8_1decode);
zval_dtor(&sw8_2decode);
zval_dtor(&sw8_4decode);
zval_dtor(&sw8_5decode);
zval_dtor(&sw8_6decode);
zval_dtor(&sw8_7decode);
}
} else {
//$SERVER没有全链路ID,证明当前为ROOT根
//此处可以注入采样率
add_assoc_string(&SKYWALKING_G(context), "parentService", service);
add_assoc_string(&SKYWALKING_G(context), "parentServiceInstance", service_instance);
char *uri = get_page_request_uri(); //获取当前请求的URI PATH
char *path = NULL;
if (uri != NULL) {
path = (char *)emalloc(strlen(uri) + 5);
bzero(path, strlen(uri) + 5);
int i;
for(i = 0; i < strlen(uri); i++) {
if (uri[i] == '?') {
break;
}
path[i] = uri[i];
}
path[i] = '\0';
}
add_assoc_string(&SKYWALKING_G(context), "parentEndpoint", (path == NULL) ? "" : path); //把自己当前的PATH当做父级EndPoint
if (path != NULL) {
efree(path);
}
add_assoc_string(&SKYWALKING_G(context), "traceId", makeTraceId); //丰富traceId信息
if(uri != NULL) {
efree(uri);
}
}
}
efree(makeTraceId);
}
4.5 解析请求URI信息函数
//获取当前请求的URI地址
static char *get_page_request_uri() {
zval *carrier = NULL;
zval *request_uri;
smart_str uri = {0}; //创建smart_str对象
if (strcasecmp("cli", sapi_module.name) == 0) {
smart_str_appendl(&uri, "cli", strlen("cli"));
} else {
zend_bool jit_initialization = PG(auto_globals_jit);
if (jit_initialization) {
zend_string *server_str = zend_string_init("_SERVER", sizeof("_SERVER") - 1, 0);
zend_is_auto_global(server_str);
zend_string_release(server_str);
}
carrier = zend_hash_str_find(&EG(symbol_table), ZEND_STRL("_SERVER"));
request_uri = zend_hash_str_find(Z_ARRVAL_P(carrier), "REQUEST_URI", sizeof("REQUEST_URI") - 1);
smart_str_appendl(&uri, Z_STRVAL_P(request_uri), strlen(Z_STRVAL_P(request_uri)));
}
smart_str_0(&uri); //smart_str追加\0结尾
if (uri.s != NULL) { //转化为char*类型
char *uris = emalloc(strlen(ZSTR_VAL(uri.s)) + 1);
strcpy(uris, ZSTR_VAL(uri.s));
smart_str_free(&uri);
return uris;
}
return NULL;
}
4.6 初始化请求函数
//请求初始化操作:用来丰富trace、spans等信息: 审计:2020.08.27 00:01
static void request_init() {
array_init(&SKYWALKING_G(curl_header));
array_init(&SKYWALKING_G(curl_header_send));
array_init(&SKYWALKING_G(context)); //初始化上下文
array_init(&SKYWALKING_G(UpstreamSegment)); //初始化to agent的数据格式
generate_context(); //生成上下文
add_assoc_long(&SKYWALKING_G(UpstreamSegment), "application_instance", application_instance); //from agent
add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "uuid", application_uuid, strlen(application_uuid)); //from agent
add_assoc_long(&SKYWALKING_G(UpstreamSegment), "pid", getppid());
add_assoc_long(&SKYWALKING_G(UpstreamSegment), "application_id", application_id);
add_assoc_long(&SKYWALKING_G(UpstreamSegment), "version", SKYWALKING_G(version));
SKY_ADD_ASSOC_ZVAL(&SKYWALKING_G(UpstreamSegment), "segment"); //创建子数组对象,用于构建 $UpstreamSegment['segment'] = array();
SKY_ADD_ASSOC_ZVAL(&SKYWALKING_G(UpstreamSegment), "globalTraceIds");
add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "service", service, strlen(service)); //like app_code: from agent
add_assoc_stringl(&SKYWALKING_G(UpstreamSegment), "serviceInstance", service_instance, strlen(service_instance)); //uuid : from agent
zval *traceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "currentTraceId", sizeof("currentTraceId") - 1);
zval traceSegmentObject;
zval spans;
array_init(&spans);
array_init(&traceSegmentObject);
add_assoc_string(&traceSegmentObject, "traceSegmentId", Z_STRVAL_P(traceId));
add_assoc_long(&traceSegmentObject, "isSizeLimited", 0);
zval temp;
char *peer = NULL;
char *uri = get_page_request_uri();
char *path = (char*)emalloc(strlen(uri) + 5);
bzero(path, strlen(uri) + 5);
int i;
for(i = 0; i < strlen(uri); i++) {
if (uri[i] == '?') {
break;
}
path[i] = uri[i];
}
path[i] = '\0';
array_init(&temp);
peer = get_page_request_peer(); // 从PHP $_SERVER变量中读取peer数据
zval tags;
array_init(&tags); //创建tags对象
add_assoc_string(&tags, "url", (uri == NULL) ? "" : uri);
add_assoc_string(&tags, "server_type", "PHP"); //注入PHP Server类型
add_assoc_string(&tags, "php_version",PHP_VERSION); //注入PHP版本号
add_assoc_zval(&temp, "tags", &tags);
add_assoc_long(&temp, "spanId", 0);
add_assoc_long(&temp, "parentSpanId", -1);
char *l_millisecond = get_millisecond();
long millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
add_assoc_long(&temp, "startTime", millisecond);
add_assoc_string(&temp, "operationName", path);
add_assoc_string(&temp, "peer", (peer == NULL) ? "" : peer);
add_assoc_long(&temp, "spanType", 0);
add_assoc_long(&temp, "spanLayer", 3);
if (SKYWALKING_G(version) == 8) {
add_assoc_long(&temp, "componentId", 8001);
} else {
add_assoc_long(&temp, "componentId", COMPONENT_UNDERTOW);
}
// sw8 or sw6 for parent endpoint name
add_assoc_string(&SKYWALKING_G(context), "currentEndpoint", path);
efree(path);
if (peer != NULL) {
efree(peer);
}
if (uri != NULL) {
efree(uri);
}
zval *isChild = zend_hash_str_find(Z_ARRVAL_P(&SKYWALKING_G(context)), "isChild", sizeof("isChild") - 1);
// refs
zval refs;
array_init(&refs);
zval globalTraceIds;
array_init(&globalTraceIds);
zval tmpGlobalTraceIds;
add_assoc_string(&SKYWALKING_G(UpstreamSegment), "traceId", Z_STRVAL_P(traceId));
if(Z_LVAL_P(isChild) == 1) {
zval ref;
array_init(&ref);
zval *parentTraceSegmentId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentTraceSegmentId", sizeof("parentTraceSegmentId") - 1);
zval *parentSpanId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentSpanId", sizeof("parentSpanId") - 1);
zval *parentApplicationInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentApplicationInstance", sizeof("parentApplicationInstance") - 1);
zval *entryApplicationInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "entryApplicationInstance", sizeof("entryApplicationInstance") - 1);
zval *entryOperationName = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "entryOperationName", sizeof("entryOperationName") - 1);
zval *networkAddress = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "networkAddress", sizeof("networkAddress") - 1);
zval *parentOperationName = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentOperationName", sizeof("parentOperationName") - 1);
zval *distributedTraceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "distributedTraceId", sizeof("distributedTraceId") - 1);
add_assoc_long(&ref, "type", 0);
add_assoc_string(&ref, "parentTraceSegmentId", Z_STRVAL_P(parentTraceSegmentId));
add_assoc_long(&ref, "parentSpanId", Z_LVAL_P(parentSpanId));
if (SKYWALKING_G(version) == 8) {
zval *traceId = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "traceId", sizeof("traceId") - 1);
zval *parentService = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentService", sizeof("parentService") - 1);
zval *parentServiceInstance = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentServiceInstance", sizeof("parentServiceInstance") - 1);
zval *parentEndpoint = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "parentEndpoint", sizeof("parentEndpoint") - 1);
zval *targetAddress = zend_hash_str_find(Z_ARRVAL(SKYWALKING_G(context)), "targetAddress", sizeof("targetAddress") - 1);
add_assoc_string(&ref, "traceId", Z_STRVAL_P(traceId));
add_assoc_string(&ref, "parentService", Z_STRVAL_P(parentService));
add_assoc_string(&ref, "parentServiceInstance", Z_STRVAL_P(parentServiceInstance));
add_assoc_string(&ref, "parentEndpoint", Z_STRVAL_P(parentEndpoint));
add_assoc_string(&ref, "targetAddress", Z_STRVAL_P(targetAddress));
zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "traceId", sizeof("traceId") - 1, traceId);
} else {
add_assoc_long(&ref, "parentApplicationInstanceId", Z_LVAL_P(parentApplicationInstance));
add_assoc_long(&ref, "entryApplicationInstanceId", Z_LVAL_P(entryApplicationInstance));
add_assoc_string(&ref, "networkAddress", Z_STRVAL_P(networkAddress));
add_assoc_string(&ref, "entryServiceName", Z_STRVAL_P(entryOperationName));
add_assoc_string(&ref, "parentServiceName", Z_STRVAL_P(parentOperationName));
ZVAL_STRING(&tmpGlobalTraceIds, Z_STRVAL_P(distributedTraceId));
}
zend_hash_next_index_insert(Z_ARRVAL(refs), &ref);
} else {
ZVAL_STRING(&tmpGlobalTraceIds, Z_STRVAL_P(traceId));
}
zend_hash_str_add(Z_ARRVAL(temp), "refs", sizeof("refs") - 1, &refs);
zend_hash_next_index_insert(Z_ARRVAL(spans), &temp);
add_assoc_zval(&traceSegmentObject, "spans", &spans);
if (SKYWALKING_G(version) != 8)
{ //谨慎调整
zend_hash_next_index_insert(Z_ARRVAL(globalTraceIds), &tmpGlobalTraceIds); //问题症结 :skywalking 8.0版本之后移除这个参数支持
}
zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "segment", sizeof("segment") - 1, &traceSegmentObject);
zend_hash_str_update(Z_ARRVAL(SKYWALKING_G(UpstreamSegment)), "globalTraceIds", sizeof("globalTraceIds") - 1, &globalTraceIds);
}
4.7 APM数据flush函数
static void sky_flush_all() {
char *l_millisecond = get_millisecond();
long millisecond = zend_atol(l_millisecond, strlen(l_millisecond));
efree(l_millisecond);
zval *span = get_first_span();
add_assoc_long(span, "endTime", millisecond); //标志请求结束时间
if ((SG(sapi_headers).http_response_code >= 500)) { //标志是否出错
add_assoc_long(span, "isError", 1);
} else {
add_assoc_long(span, "isError", 0);
}
write_log(sky_json_encode(&SKYWALKING_G(UpstreamSegment))); //发送APM数据 - UNIX domain socket途径
}
4.8 APM注册函数
//sky-agent注册 :2020.08.26 22:34审计结束
static int sky_register() {
if (application_instance == 0) {
struct sockaddr_un un;
un.sun_family = AF_UNIX; //UNIX通信类型
strcpy(un.sun_path, SKYWALKING_G(sock_path)); //UNIX通信地址
int fd;
char message[1024]; //内存空间优化
char return_message[1024]; //内存空间优化
fd = socket(AF_UNIX, SOCK_STREAM, 0); //创建client fd
if (fd >= 0) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 50000; //单位微秒:50ms
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv);
int conn = connect(fd, (struct sockaddr *) &un, sizeof(un));
if (conn >= 0) {
//进行ServiceName构建:如果环境变量不存在KEY对应的值,则进行环境变量获取
char *env_app_code = getenv(SKYWALKING_G(app_code_env_key));
if (env_app_code != NULL)
{
SKYWALKING_G(app_code) = env_app_code;
}
bzero(message, sizeof(message));
sprintf(message, "0{\"app_code\":\"%s\",\"pid\":%d,\"version\":%d}\n", SKYWALKING_G(app_code),
getppid(), SKYWALKING_G(version));
write(fd, message, strlen(message));
bzero(return_message, sizeof(return_message));
read(fd, return_message, sizeof(return_message));
char *ids[10] = {0};
int i = 0;
char *p = strtok(return_message, ","); //分割握手协议
while (p != NULL) {
ids[i++] = p;
p = strtok(NULL, ",");
}
if (ids[0] != NULL && ids[1] != NULL && ids[2] != NULL) {
if (SKYWALKING_G(version) == 6 || SKYWALKING_G(version) == 7) {
application_id = atoi(ids[0]);
application_instance = atoi(ids[1]);
strncpy(application_uuid, ids[2], sizeof application_uuid - 1);
} else if (SKYWALKING_G(version) == 8) {
application_id = 1;
application_instance = 1;
strncpy(service, ids[0], sizeof service - 1); //拷贝service app_code ,此处和sidecar进行交互,可以定制化 : app_code
strncpy(service_instance, ids[1], sizeof service_instance - 1);
strncpy(application_uuid, ids[2], sizeof application_uuid - 1);
}
}
} else {
php_error_docref(NULL, E_WARNING, "[skywalking] failed to connect the socket.");
}
close(fd);
} else {
php_error_docref(NULL, E_WARNING, "[skywalking] failed to open the socket.");
}
}
return 0;
}
五.附录
5.1 附录一 : SkyWalking-PHP内核组件的Component列表
#ifndef SKYWALKING_COMPONENTS_H
#define SKYWALKING_COMPONENTS_H
#define COMPONENT_TOMCAT 1
#define COMPONENT_HTTPCLIENT 2
#define COMPONENT_DUBBO 3
#define COMPONENT_MOTAN 8
#define COMPONENT_RESIN 10
#define COMPONENT_FEIGN 11
#define COMPONENT_OKHTTP 12
#define COMPONENT_SPRING_REST_TEMPLATE 13
#define COMPONENT_SPRING_MVC_ANNOTATION 14
#define COMPONENT_STRUTS2 15
#define COMPONENT_NUTZ_MVC_ANNOTATION 16
#define COMPONENT_NUTZ_HTTP 17
#define COMPONENT_JETTY_CLIENT 18
#define COMPONENT_JETTY_SERVER 19
#define COMPONENT_SHARDING_JDBC 21
#define COMPONENT_GRPC 23
#define COMPONENT_ELASTIC_JOB 24
#define COMPONENT_HTTP_ASYNC_CLIENT 26
#define COMPONENT_SERVICECOMB 28
#define COMPONENT_HYSTRIX 29
#define COMPONENT_JEDIS 30
#define COMPONENT_H2_JDBC_DRIVER 32
#define COMPONENT_MYSQL_JDBC_DRIVER 33
#define COMPONENT_OJDBC 34
#define COMPONENT_SPYMEMCACHED 35
#define COMPONENT_XMEMCACHED 36
#define COMPONENT_POSTGRESQL_DRIVER 37
#define COMPONENT_ROCKET_MQ_PRODUCER 38
#define COMPONENT_ROCKET_MQ_CONSUMER 39
#define COMPONENT_KAFKA_PRODUCER 40
#define COMPONENT_KAFKA_CONSUMER 41
#define COMPONENT_MONGO_DRIVER 42
#define COMPONENT_SOFARPC 43
#define COMPONENT_ACTIVEMQ_PRODUCER 45
#define COMPONENT_ACTIVEMQ_CONSUMER 46
#define COMPONENT_TRANSPORT_CLIENT 48
#define COMPONENT_UNDERTOW 49
#define COMPONENT_RPC 50
#endif //SKYWALKING_COMPONENTS_H
5.2 附录二:APM拦截所需的关键函数定义
static char *sky_json_encode(zval *parameter); //json序列化
static long get_second(); //获取秒级时间戳
static char *get_millisecond(); //获取毫秒级时间戳
static char *generate_sw3(zend_long span_id, char *peer_host, char *operation_name); //生成sw3协议数据
static char *generate_sw6(zend_long span_id, char *peer_host); //生成sw6协议数据
static char *generate_sw8(zend_long span_id, char *peer_host); //生成sw8协议数据
static void generate_context(); //生成上下文
static char *get_page_request_uri(); //获取request的uri
static char *get_page_request_peer(); //获取request的主机信息
static void write_log( char *text); //发送APM日志
static void request_init();//初始化一次请求
static void zval_b64_encode(zval *out, char *in); //base64序列化
static void zval_b64_decode(zval *out, char *in); //base64反序列化
static char *sky_get_class_name(zval *obj); //获取zval数据结构的class名称
static zval *sky_read_property(zval *obj, const char *property, int parent); //读取apm监控数据的某个属性
static char *sky_redis_fnamewall(const char *function_name); //针对redis操作增加函数边界符号 |%s|
static int sky_redis_opt_for_string_key(char *fnamewall); //查询redis指令是否在APM监控范围内
static void sky_flush_all(); //发送skywalking APM数据包
static zval *get_first_span(); //获取收个全链路span数据
static zval *get_spans(); //获取全部的spans数据
static char* _get_current_machine_ip(); //获取当前机器的IP
static char *sky_memcached_fnamewall(const char *function_name); //针对memcache操作增加函数边界服务 |%s|
static int sky_memcached_opt_for_string_key(char *fnamewall); //查询memcache指令是否在APM监控范围内
5.3 附录三:APM关键函数hook定义
//curl函数Hook函数
void sky_curl_exec_handler(INTERNAL_FUNCTION_PARAMETERS);
void sky_curl_setopt_handler(INTERNAL_FUNCTION_PARAMETERS);
void sky_curl_setopt_array_handler(INTERNAL_FUNCTION_PARAMETERS);
void sky_curl_close_handler(INTERNAL_FUNCTION_PARAMETERS);
static void (*orig_curl_exec)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
static void (*orig_curl_setopt)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
static void (*orig_curl_setopt_array)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
static void (*orig_curl_close)(INTERNAL_FUNCTION_PARAMETERS) = NULL;
5.4 附录三:全局变量定义
/*
Declare any global variables you may need between the BEGIN
and END macros here:
*/
ZEND_BEGIN_MODULE_GLOBALS(skywalking)
char *sock_path; //和sidecar通信的unix地址
char *app_code; //服务名称,app_name eg:skywalking.app_code = MyProjectName
char *app_code_env_key; //app_name 环境变量地址:环境变量->默认KEY:APM_APP_CODE
zend_bool enable; //是否开启内核扩展
zval UpstreamSegment; //全局上报数据段
zval context; //APM上下文
zval curl_header; //curl header数据
zval curl_header_send; //记录当前R周期 是否已经send过curl_header
int version; //APM 版本号
ZEND_END_MODULE_GLOBALS(skywalking)
extern ZEND_DECLARE_MODULE_GLOBALS(skywalking);
评论已关闭