2.这个脚本不具备通用性,如果你使用了lxc或者cgroup,获得的结果可能有误或者找不到
3.这个脚本只是提供了找到socket实例的方法,如果你想获取这个套接字的其他信息需要自己编写脚本,
通
过获得的socket实例来获取。插入的位置在脚本中已标注,可以直接插入代码或函数。如果你对
SystemTap比较熟悉的话可以自己灵活地修改。
思路很简单,
根据pid查找对应的task_struct结构实例,然后找到打开文件描述符表,查找fd对应的file结构实例。如果fd是套接字,socket结构实例保存在file结构的private_data成员中。在从private_data成员中获取数据前,会检查文件的操作是否指向socket_file_ops,但是该成员没有导出,所以脚本中没有这个校验,由使用者自己来保证指定的fd是套接字。
脚本如下所示:
%{
# include <linux /fdtable.h >
# include <linux /file.h >
# include <net /inet_connection_sock.h >
%}
%{
int err = 0, fput_needed = 0;
%}
function stap_fget_light(fd_param : long, pid_param : long) %{
int fd = STAP_ARG_fd_param;
struct file *file;
struct files_struct *files;
struct task_struct *p;
p = pid_task(find_pid_ns(STAP_ARG_pid_param,
current - >nsproxy - >pid_ns), PIDTYPE_PID);
if ( !p) {
_stp_printf( "Find task %d failed\n", STAP_ARG_pid_param);
STAP_RETVALUE = ( long)NULL;
return;
}
files = p - >files;
fput_needed = 0;
if (likely((atomic_read( &files - >count) == 1))) {
file = fcheck_files(files, fd);
} else {
rcu_read_lock();
file = fcheck_files(files, fd);
if (file) {
if (atomic_long_inc_not_zero( &file - >f_count))
fput_needed = 1;
else
/* Didn't get the reference, someone's freed */
file = NULL;
}
rcu_read_unlock();
}
if ( !file) {
_stp_printf( "Task %d did not have fd %d\n" ,STAP_ARG_pid_param, STAP_ARG_fd_param);
}
STAP_RETVALUE = ( long)file;
return;
%}
function stap_sockfd_lookup_light(file_param : long) %{
struct file *file = (typeof(file))STAP_ARG_file_param;
STAP_RETVALUE = ( long)file - >private_data;
return;
%}
global file_ptr
function get_socket_by_pid_and_fd()
{
sock_ptr = 0;
file_ptr = stap_fget_light($ 1, $ 2);
if (file_ptr) {
sock_ptr = stap_sockfd_lookup_light(file_ptr);
}
return sock_ptr;
}
function stap_fput_light(file_param : long) %{
struct file *file = (typeof(file))STAP_ARG_file_param;
if (file) {
fput_light(file, fput_needed);
}
%}
function verify_socket(socket_param : long) %{
struct socket *sock = (typeof(sock))STAP_ARG_socket_param;
struct sock *sk = sock - >sk;
struct inet_sock *isk = inet_sk(sk);
_stp_printf( "\n\nverify_socket: local IP: " NIPQUAD_FMT ", port: %u\n", NIPQUAD(isk - >saddr), isk - >num);
%}
probe begin
{
printf( "Start to get socket. fd: %d, process: %d\n", $ 1, $ 2);
sock_address = get_socket_by_pid_and_fd();
if (sock_address) {
/* You can remove printf and verify_socket, just for test */
printf( "sock_address = %p\n", sock_address);
verify_socket(sock_address);
/* ******** Insert your code or function here ******** */
stap_fput_light(file_ptr);
}
exit();
}
# include <linux /fdtable.h >
# include <linux /file.h >
# include <net /inet_connection_sock.h >
%}
%{
int err = 0, fput_needed = 0;
%}
function stap_fget_light(fd_param : long, pid_param : long) %{
int fd = STAP_ARG_fd_param;
struct file *file;
struct files_struct *files;
struct task_struct *p;
p = pid_task(find_pid_ns(STAP_ARG_pid_param,
current - >nsproxy - >pid_ns), PIDTYPE_PID);
if ( !p) {
_stp_printf( "Find task %d failed\n", STAP_ARG_pid_param);
STAP_RETVALUE = ( long)NULL;
return;
}
files = p - >files;
fput_needed = 0;
if (likely((atomic_read( &files - >count) == 1))) {
file = fcheck_files(files, fd);
} else {
rcu_read_lock();
file = fcheck_files(files, fd);
if (file) {
if (atomic_long_inc_not_zero( &file - >f_count))
fput_needed = 1;
else
/* Didn't get the reference, someone's freed */
file = NULL;
}
rcu_read_unlock();
}
if ( !file) {
_stp_printf( "Task %d did not have fd %d\n" ,STAP_ARG_pid_param, STAP_ARG_fd_param);
}
STAP_RETVALUE = ( long)file;
return;
%}
function stap_sockfd_lookup_light(file_param : long) %{
struct file *file = (typeof(file))STAP_ARG_file_param;
STAP_RETVALUE = ( long)file - >private_data;
return;
%}
global file_ptr
function get_socket_by_pid_and_fd()
{
sock_ptr = 0;
file_ptr = stap_fget_light($ 1, $ 2);
if (file_ptr) {
sock_ptr = stap_sockfd_lookup_light(file_ptr);
}
return sock_ptr;
}
function stap_fput_light(file_param : long) %{
struct file *file = (typeof(file))STAP_ARG_file_param;
if (file) {
fput_light(file, fput_needed);
}
%}
function verify_socket(socket_param : long) %{
struct socket *sock = (typeof(sock))STAP_ARG_socket_param;
struct sock *sk = sock - >sk;
struct inet_sock *isk = inet_sk(sk);
_stp_printf( "\n\nverify_socket: local IP: " NIPQUAD_FMT ", port: %u\n", NIPQUAD(isk - >saddr), isk - >num);
%}
probe begin
{
printf( "Start to get socket. fd: %d, process: %d\n", $ 1, $ 2);
sock_address = get_socket_by_pid_and_fd();
if (sock_address) {
/* You can remove printf and verify_socket, just for test */
printf( "sock_address = %p\n", sock_address);
verify_socket(sock_address);
/* ******** Insert your code or function here ******** */
stap_fput_light(file_ptr);
}
exit();
}
在上面的脚本中,
verify_socket()函数是为了验证获得的结果,实际使用时可以去掉。将上面的脚本保存为get_socket_by_pid_and_fd.stp文件,使用方法如下所示(fd是文件描述符,pid是fd所属的进程的PID):
stap
-g get_socket_by_pid_and_fd.stp fd pid
下面我们通过一个简单的实例,来了解一下具体的使用。这里以nginx为例,查找其监听套接字的地址,监听端口是80。首先获取nginx的pid和监听套接字的fd,过程如下所示:
[root@CentOS_102
~]
# lsof -iTCP -sTCP:LISTEN -P | grep ":80"
nginx 6770 root 6u IPv4 34649 0t0 TCP 192. 168. 56. 102 : 80 (LISTEN)
nginx 6771 nobody 6u IPv4 34649 0t0 TCP 192. 168. 56. 102 : 80 (LISTEN)
[root@CentOS_102 ~] # ps aux | grep -v grep | grep nginx
root 6770 0. 0 0. 2 23932 616 ? Ss 13 : 15 0 :00 nginx : master process /usr /local /nginx - 1. 5. 8 /sbin /nginx
nobody 6771 0. 0 0. 3 24336 1180 ? S 13 : 15 0 :00 nginx : worker process
[root@CentOS_102 ~] #
nginx 6770 root 6u IPv4 34649 0t0 TCP 192. 168. 56. 102 : 80 (LISTEN)
nginx 6771 nobody 6u IPv4 34649 0t0 TCP 192. 168. 56. 102 : 80 (LISTEN)
[root@CentOS_102 ~] # ps aux | grep -v grep | grep nginx
root 6770 0. 0 0. 2 23932 616 ? Ss 13 : 15 0 :00 nginx : master process /usr /local /nginx - 1. 5. 8 /sbin /nginx
nobody 6771 0. 0 0. 3 24336 1180 ? S 13 : 15 0 :00 nginx : worker process
[root@CentOS_102 ~] #
我们这里使用nginx的master进程中的fd,通过上面的两个命令,我们可以看到fd为6,pid是6770。下面执行我们的脚本,输出结果如下所示:
[root@CentOS_102
~]
# stap -g get_socket_by_pid_and_fd.stp 6 6770
Start to get socket. fd : 6, process : 6770
sock_address = 0xffff88001783fa00
verify_socket : local IP : 192. 168. 56. 102, port : 80
Start to get socket. fd : 6, process : 6770
sock_address = 0xffff88001783fa00
verify_socket : local IP : 192. 168. 56. 102, port : 80
测试机器的IP地址就是192.168.56.102,根据输出结果验证了这个脚本是可以工作的。