一、进程执行追踪命令strace的详细参数及使用
strace常用来跟踪进程执行时的系统调用和所接收的信号。Linux看进程不能直接访问硬件设备,当需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
1. strace命令的各参数备注如下:
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column 设置返回值的输出位置.默认 为40.
-e expr 指定一个表达式,用来控制如何跟踪
-o filename 将strace的输出写入文件filename
-p pid 跟踪指定的进程pid. 一般用于跟踪后台程序
-s strsize 指定输出的字符串的最大长度.默认为32.
-u username 以username 的UID和GID执行被跟踪的命令
2. strace命令执行示例:
[on@B4471 ~]$ ps -ef | grep php
root 10984 1 0 19:13 ? 00:00:00 php-fpm: master process (/opt/soft/php7/etc/php-fpm.conf)
www 10985 10984 0 19:13 ? 00:00:00 php-fpm: pool www
www 10986 10984 0 19:13 ? 00:00:00 php-fpm: pool www
#追踪线程 10985,如下会列出执行过程中的系统调用函数
[on@B4471 ~]$ strace -p 10985
fstat(10, {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lseek(10, 0, SEEK_CUR) = 0
write(10, "{\"url\":\"http:\\/\\/newapi.shouji.b"..., 3790) = 3790
close(10) = 0
chdir("/home/onlinedev") = -1 EACCES (Permission denied)
times({tms_utime=3, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 3013530754
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
fcntl(3, F_SETLK, {type=F_UNLCK, whence=SEEK_SET, start=0, len=0}) = 0
write(4, "\1\6\0\1\0\6\2\0000\"}}]}\0\0\1\3\0\1\0\10\0\0\0\0\0\0\0.10", 32) = 32
shutdown(4, SHUT_WR) = 0
recvfrom(4, "\1\5\0\1\0\0\0\0", 8, 0, NULL, NULL) = 8
recvfrom(4, "", 8, 0, NULL, NULL) = 0
close(4) = 0
munmap(0x7f7838000000, 2097152) = 0
setitimer(ITIMER_PROF, {it_interval={0, 0}, it_value={0, 0}}, NULL) = 0
accept(0,
上面显示的就是php线程执行时执行的系统函数。
3. 常见系统调用函数列表如下:
fork 创建一个新进程
clone 按指定条件创建子进程
execve 运行可执行文件
exit 中止进程
nanosleep 使进程睡眠指定的时间
pause 挂起进程,等待信号
wait 等待子进程终止
waitpid 等待指定子进程终止
socket 建立socket
bind 绑定socket到端口
connect 连接远程主机
accept 响应socket连接请求
send 通过socket发送信息
recv 通过socket接收信息
mmap 映射虚拟内存页
munmap 去除内存页映射
mremap 重新映射虚拟内存地址
msync 将映射内存中的数据写回磁盘
listen 监听socket端口
select 对多路同步I/O进行轮询
shutdown 关闭socket上的连接
readv 从文件读入数据到缓冲数组中
writev 将缓冲数组里的数据写入文件
pread 对文件随机读
pwrite 对文件随机写
lseek 移动文件指针
dup 复制已打开的文件描述字
flock 文件加/解锁
poll I/O多路转换
truncate 截断文件
另外strace的-e trace选项详细
-e trace=file 跟踪和文件访问相关的调用(参数中有文件名)
-e trace=process 和进程管理相关的调用,比如fork/exec/exit_group
-e trace=network 和网络通信相关的调用,比如socket/sendto/connect
-e trace=signal 信号发送和处理相关,比如kill/sigaction
-e trace=desc 和文件描述符相关,比如write/read/select/epoll等
-e trace=ipc 进程见同学相关,比如shmget等
4. strace其它应用示例
1.跟踪nginx, 看其启动时都访问了哪些文件 strace -tt -T -f -e trace=file -o /data/log/strace.log -s 1024 2>&1 ./nginx
2. 定位程序异常退出 strace -ttf -T -p 10893 -o tmp -e trace=process 2>&1
3.程序启动加载文件 strace -e open,acces ./sh 2>&1 | grep fileName
4. 查选程序耗时 strace -c -p 11084
5.链接服务器失败 strace -e poll,select,connect,recvfrom,sendto nc www.baidu.com 80
二、曾经用于开发测试调试的一些 Lua 代码包括Lua显示table、table转string、动态加载模块等
1. 显示数据的函数,包括显示table, 显示table时递归调用的函数
-----------------------------------
--Note :工具函数类
--Author:linge
--Time :2016-06-15
-----------------------------------
--1,显示数据的函数,包括显示table,
function show(t)
if(type(t) ~= 'table') then
print(t)
else
print(_show_table(t, 1))
end
end
--2,显示table时递归调用的函数
function _show_table(t, level)
local show = ''
local temp = ''
local space = string.rep(' ', (level-1)*4)
local sapce4 = string.rep(' ', 4)
if(type(t) ~= 'table') then
return 'error: params t is not a table.'
else
--取得表名
show = show .. tostring(t) .. "{\n" .. space
for k,v in pairs(t) do
if type(v) == 'table' then
temp = _show_table(v, level+1)
elseif(type(v) == 'string') then
temp = '\"' .. tostring(v) .. '\"'
else
temp = tostring(v)
end
show = show .. sapce4 .. tostring(k) .. " = " .. temp .. ",\n" .. space
end
show = show .. "}"
end
return show
end
local config = require "4requiredata"
local config ={10,20,30}
function unpacks(t, i)
i = i or 1
if t[i] then
return t[i], unpacks(t, i+1)
end
end
print(unpacks(config))
2. 把table转换成string字符串的方法
function table_tostring(tb)
if type(tb) ~= "table" then
error("Sorry, it's not table, it is " .. type(tb) .. ".")
end
local ret = " = {\n"
local table_list = {}
table_list[tb] = "root table"
ret = ret .. _list_table(tb, table_list, 1)
ret = ret .. "}"
return ret
end
function _list_table(tb, table_list, level)
local ret = ""
local indent = string.rep(" ", level*4)
for k, v in pairs(tb) do
local quo = type(k) == "string" and "\"" or ""
ret = ret .. indent .. "[" .. quo .. tostring(k) .. quo .. "] = "
if type(v) == "table" then
local t_name = table_list[v]
if t_name then
ret = ret .. tostring(v) .. " -- > [\"" .. t_name .. "\"]\n"
else
table_list[v] = tostring(k)
ret = ret .. "{\n"
ret = ret .. _list_table(v, table_list, level+1)
ret = ret .. indent .. "}\n"
end
elseif type(v) == "string" then
ret = ret .. "\"" .. tostring(v) .. "\"\n"
else
ret = ret .. tostring(v) .. "\n"
end
end
local mt = getmetatable(tb)
print(mt)
if mt then
ret = ret .. "\n"
local t_name = table_list[mt]
ret = ret .. indent .. "<metatable> = "
if t_name then
ret = ret .. tostring(mt) .. " -- > [\"" .. t_name .. "\"]\n"
else
ret = ret .. "{\n"
ret = ret .. _list_table(mt, table_list, level+1)
ret = ret .. indent .. "}\n"
end
end
return ret
end
local con = require "4requiredata"
print(tostring(con) .. table_tostring(con))
3. 工具函数类-动态加载模块函数 加载一些核心table、辅助table等
-----------------------------------
--Note :工具函数类-开发
--Author:linge
--Time :2016-06-15
-----------------------------------
package.path = 'E:/Lnmp/www/Default/danmu/danmu_lyl/?.lua;'
--1.2,加载模块函数
function load_module(module_name)
local m = nil
local _, err = pcall(function(mod)
m = require(mod)
end, module_name)
return m, err
end
--1.3,加载一些核心table、辅助table等
_,err = load_module("library/json")
if err then
print(err)
end
local data = {
message1 = {
text = 'hello',
showtime = '12000',
},
message2 = {
text = 'test',
showtime = '15000',
message3 = {
name = 'kermit',
age = 1234,
message4 = {
level4 = 'ddd'
}
},
bool = true
}
}
print(json.encode(data))
4. lua直接向redis插入数据
-----------------------------------
--Note :弹幕添加测试数据:直接向redis插入数据
--Author:linge
--Time :2016-06-16
-----------------------------------
package.path = '/opt/data/danmu/branches/danmu_lyl/?.lua;'
local help = require "library/help"
--设4个影片,ID如下
local video_ids = { 2300, 2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309 }
math.randomseed(tostring(os.time()):reverse():sub(1, 6))
--弹幕要有顺序,同时要在一个集合中, 取值时能按键取值
--在redis里,hash,list,sortset可考虑。目前来看有序集合适合。
--zset
--键前五位为秒值:预留86400五位做秒值
--键后二位为帧值:目前不超过30帧,2位可保证99帧的视频
--键最后二位为随机数:11-99之间,
--如此要取视频第1秒到第3秒的弹幕,只要取 10000 < 键值 < 30000的值出来。
--要取1小时0分0秒到1小时0分3秒,只要取 36010000 < 键值 <36030000的值出来。
--
function utfstrlen(str)
local len = #str;
local left = len;
local cnt = 0;
local arr={0,0xc0,0xe0,0xf0,0xf8,0xfc};
while left ~= 0 do
local tmp=string.byte(str,-left);
local i=#arr;
while arr[i] do
if tmp>=arr[i] then left=left-i;break;end
i=i-1;
end
cnt=cnt+1;
end
return cnt;
end
--随机取一段中文
text = '如你所见主节点对命令的复制工作发生在返回命令回复之后因为如果每次处理命令请求都需要等待复制操作完成的话那么主节点处理命令请求的速度将极大地降低我们必须在性能和一致性之间做出权衡'
length = utfstrlen(text)
--随机取几个汉字出来当做评论
function get_chinese_char(num)
local chars = ''
for i = 1,num do
local index = math.random(1, length)
local start = (index-1) * 3 + 1
chars = chars .. text:sub(start, start + 2)
end
return chars
end
--连接redis
local redis = require 'library.redis'
local config = require 'config.config'
local msgpack = require 'cmsgpack'
local queue = redis:new()
queue:set_timeout(config.storage.queue_redis.connect_timeout)
local ok, err = queue:connect(config.storage.queue_redis.host, config.storage.queue_redis.port)
if not ok then
return ngx.say(err)
end
ngx.say('<h2>向redis插入1000条测试数据,分布wid:13,aid:160000,vid:2300~2309随机 弹幕帧:0-3600内</h2>')
ngx.say('<h3>-------------first 2 records:</h3>')
--随机生成多条弹幕
for i =1,1000 do
local index = math.random(1,10)
local time = math.random(0,3600) --预留5位给秒值,范围缩小点,不写86400
local frame = math.random(11,30)
local text_num = math.random(5,15)
local params = {
uid = '{2D58D7B0-A49C-907F-39AE-9C70CBEB8F3B}',
movieid = 111111, --暂时无movieid
aid = 160000,
vid = video_ids[index],
wid = 13,
time = time,
text = get_chinese_char(text_num),
font = 2,
color = 16711813,
mod = 1,
nt = os.time()
}
--添加测试数据进入redis
local list_key = 'dm_' .. params.wid ..'_'.. params.aid ..'_'.. params.vid
local list_score = tonumber(params.time .. math.random(10,99))
queue:zadd(list_key, list_score, msgpack.pack(params))
--加入的前2条数据格式
if i <=2 then
ngx.say(help.html(params) .. "<br>")
end
end
--读取统计数据:
local dm_keys = queue:keys('dm_*')
ngx.say('<h3>-------------插入后当前Redis中弹幕存储量:</h3>')
local all_dm = 0
for i,dmkey in pairs(dm_keys) do
local tempnum = queue:zcard(dmkey)
ngx.say('sortedset key:' .. dmkey .. " ,弹幕量:" .. tempnum .."<br>" )
all_dm = all_dm + tempnum
end
ngx.say('<br><b>弹幕总量:' .. all_dm .. "</b>")
queue:set_keepalive(config.storage.queue_redis.keepalive_timeout, config.storage.queue_redis.pool_size)
5. lua向redis 添加 弹幕测试数据
新的弹幕服务器端区别于4的内容,设4个影片,ID如下。随机取一段中文,随机取几个汉字出来当做评论,随机生成多条弹幕,预留5位给秒值,范围缩小点,不写86400,向redis注入几十万条弹幕。读取统计数据。
-----------------------------------
--Note :弹幕添加测试数据:
--Author:linge
--Time :2016-06-18
-----------------------------------
package.path = '/opt/baofeng-data/danmu/branches/danmu_lyl/?.lua;'
local help = require "library/help"
--设4个影片,ID如下
local video_ids = { 2300, 2301, 2302, 2303, 2304, 2305, 2306, 2307, 2308, 2309 }
math.randomseed(tostring(os.time()):reverse():sub(1, 6))
function utfstrlen(str)
local len = #str;
local left = len;
local cnt = 0;
local arr={0,0xc0,0xe0,0xf0,0xf8,0xfc};
while left ~= 0 do
local tmp=string.byte(str,-left);
local i=#arr;
while arr[i] do
if tmp>=arr[i] then left=left-i;break;end
i=i-1;
end
cnt=cnt+1;
end
return cnt;
end
--随机取一段中文
text = '如你所见主节点对命令的复制工作发生在返回命令回复之后因为如果每次处理命令请求都需要等待复制操作完成的话那么主节点处理命令请求的速度将极大地降低我们必须在性能和一致性之间做出权衡'
length = utfstrlen(text)
--随机取几个汉字出来当做评论
function get_chinese_char(num)
local chars = ''
for i = 1,num do
local index = math.random(1, length)
local start = (index-1) * 3 + 1
chars = chars .. text:sub(start, start + 2)
end
return chars
end
--连接redis
local redis = require 'library.redis'
local config = require 'config.config'
local msgpack = require 'cmsgpack'
local queue = redis:new()
queue:set_timeout(config.storage.queue_redis.connect_timeout)
local ok, err = queue:connect(config.storage.queue_redis.host, config.storage.queue_redis.port)
if not ok then
return ngx.say(err)
end
ngx.say('<h2>向redis插入1000条测试数据,分布wid:13,aid:160000,vid:2300~2309随机 弹幕帧:0-3600内</h2>')
ngx.say('<h3>-------前10条请求:</h3><h5 style="font-weight:normal;">')
local success,fail = 0, 0
--随机生成多条弹幕
for i =1,10 do
--随机vid
local index = math.random(1,10)
local vid = video_ids[index]
--随机帧值
local time = math.random(0,3600) --预留5位给秒值,范围缩小点,不写86400
--随机内容
local text_num = math.random(5,15)
local text = i .. "--" .. get_chinese_char(text_num) --内容前加数值好判断谁在前谁在后
--URL拼接
local url = 'http://danmul.linge.com/up?uid={2D58D7B0-A49C-907F-39AE-9C70CBEB8F3B}&movieid=11111&aid=160000&font=2&color=16711813&mod=1&wid=13'..'&vid='.. vid ..'&time='.. time ..'&text='.. text
--加入的前2条数据格式
if i <= 10 then
ngx.say(help.html(url) .. "<br>" .. string.rep('-', 200) .. "<br>")
end
--请求URL
os.execute('curl "'.. url ..'"')
--[[if exec_rs == '0' then
success = success +1
else
fail = fail +1
end]]
end
ngx.say("curl url success:" .. success ..", fail:" .. fail)
ngx.say('</h5>')
--读取统计数据:
local dm_keys = queue:keys('dm_*')
ngx.say('<h3>-------插入后当前Redis中弹幕存储量:</h3>')
local all_dm = 0
if next(dm_keys) ~= nil then
for i,list_key in pairs(dm_keys) do
local dm_data = queue:zrangebyscore(list_key, 0, 100000000)
local frame_num = #dm_data
local tempnum = 0
for i,data in pairs(dm_data) do
data = msgpack.unpack(data)
tempnum = tempnum + #data
end
ngx.say('sortedset key:' .. list_key .. ",帧量:" ..frame_num .." ,弹幕量:" .. tempnum .."<br>" )
all_dm = all_dm + tempnum
end
end
ngx.say('<br><b>弹幕总量:' .. all_dm .. "</b>")
queue:set_keepalive(config.storage.queue_redis.keepalive_timeout, config.storage.queue_redis.pool_size)