见字如面,我是东北码农。

本文是造轮子-strace的第一篇,我们先介绍strace的功能、使用。下一篇我们来用代码实现一下strace的功能,造个轮子。今天我们先观察、使用轮子。

1、什么是strace

strace是一个Linux的工具,用于记录process的system call的参数、返回值等信息。

strace是我们调试、排障的好帮手,尤其是没有源代码或不方便重新编译的时候。

1.1、小试牛刀

strace使用时非常方便,最简单的方式就是在指令前面加上strace。接下来以telnet为例来看看telnet时调用了哪些system call。

先直接运行一下telnet:

root@xxx:~$ telnet 220.181.38.148 80
Trying 220.181.38.148...
Connected to 220.181.38.148.
Escape character is '^]'.
Connection closed by foreign host.

我们再用strace执行一下telnet:

root@xxx:~$ strace telnet 220.181.38.148 80

...下面省略了一部分...
Trying 220.181.38.148...
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
setsockopt(3, SOL_IP, IP_TOS, [16], 4) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("220.181.38.148")}, 16) = 0
Connected to 220.181.38.148.
Escape character is '^]'.
setsockopt(3, SOL_SOCKET, SO_OOBINLINE, [1], 4) = 0
recvfrom(3, "", 8191, 0, NULL, NULL) = 0
Connection closed by foreign host.
+++ exited with 1 +++

神奇吧,把telnet调用的system call的参数和返回值打印了出来。上面我只截取了一部分关于网络的system call。

1.2、什么是system call(系统调用)

strace可以跟踪system call,那么什么是system call呢?

为了安全,Linux 中分为用户态和内核态两种运行状态。对于普通进程,平时都是运行在用户态下,仅拥有基本的运行能力。当进行一些敏感操作,比如说要打开文件(open)然后进行写入(write)、分配内存(malloc)时,就会切换到内核态。内核态进行相应的检查,如果通过了,则按照进程的要求执行相应的操作,分配相应的资源。这种机制被称为system call(系统调用),用户态进程发起调用,切换到内核态,内核态完成,返回用户态继续执行,是用户态唯一主动切换到内核态的合法手段(exception 和 interrupt 是被动切换)。

关于系统调用的详细定义可以通过 man syscalls 查看,它列出了目前 Linux Kernel 提供的系统调用 ABI 。我们熟悉的调用比如 open, read ,close 之类的都属于系统调用,但它们都经过了 C 库 (glibc)的封装。实际上,只要符合 ABI 规范,我们可以自己用汇编代码来进行调用。

2、怎么使用strace

学习strace的各种用法,各种参数,推荐两个途径

下面介绍一下我认为比较有用的一些用法。

2.1、两种trace模式

strace也有两种trace模式:

  • strace启动模式:就像上面telnet的例子,正常启动指令前面加strace即可。
  • attach模式:strace -p pid。

strace和gdb很像,二者实现的时候都是依赖ptrace。

2.2、过滤

如果不加过滤,strace会打印所有的system call。即使简单的ls指令,也会调用多达167个system call。所以我们需要根据需求对输出进行一些过滤。

root@xxx:~/code/case/case20_ptrace$ strace ls 2>&1 |wc -l
167
2.2.1、按system call类型过滤

可以按照system call类型过滤,我比较常用的有文件相关、内存相关、网络相关。

-e trace=%file     Trace all system calls which take a file name as an argument.
%memory Trace all memory mapping related system calls.
%network Trace all the network related system calls.
%process Trace all system calls which involve process management.

例如只看文件相关的system call

***@xxx:~$ strace -e trace=%file cat ./out
execve("/usr/bin/cat", ["cat", "./out"], 0x7ffd2b5f1f08 /* 19 vars */) = 0
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "./out", O_RDONLY) = 3
2.2.2、按system call名称过滤

指定system call名称,例如只跟踪connect函数

***@xxx:~$ strace -e trace=connect telnet baidu.com 80
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/nscd/socket"}, 110) = -1 ENOENT (No such file or directory)
connect(3, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("172.30.224.1")}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("220.181.38.148")}, 16) = 0
connect(3, {sa_family=AF_UNSPEC, sa_data="\0\0\0\0\0\0\0\0\0\0\0\0\0\0"}, 16) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("220.181.38.251")}, 16) = 0
Trying 220.181.38.148...
connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("220.181.38.148")}, 16) = 0
Connected to baidu.com.
Escape character is '^]'.
^Cstrace: Process 371 detached

2.3、system call故障注入

我们可以借助strace,观察process在system call失败时的表现。例如我们可以观察一下,cat打开文件失败时的表现。

在测试时很有用,起到了类似hook的效果。可以测试程序在内存不足、文件访问权限不足、网络不通等异常情况的表现。

root@xxx:~$ strace -e trace=openat -e fault=openat cat ./out
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/haswell/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/tls/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/haswell/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/x86_64/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOSYS (Function not implemented) (INJECTED)
cat: error while loading shared libraries: libc.so.6: cannot open shared object file: Error 38
+++ exited with 127 +++

strace输出:“ (Function not implemented) (INJECTED)”。cat在打开文件失败时输出:“cat: error while loading shared libraries: libc.so.6: cannot open shared object file: Error 38”

2.4、system call信息统计

strace除了可以打印每次system call信息外,还可以使用统计模式。
在性能测试时比较有用。

root@xxx:~$ strace -c -e trace=network telnet baidu.com 80
Trying 220.181.38.148...
Connected to baidu.com.
Escape character is '^]'.
telnet> q
Connection closed.
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
22.93 0.001428 204 7 2 connect
13.12 0.000817 272 3 sendto
13.05 0.000813 135 6 socket
11.24 0.000700 233 3 setsockopt
10.44 0.000650 216 3 recvmsg
9.55 0.000595 198 3 getsockname
7.10 0.000442 221 2 recvfrom
5.15 0.000321 321 1 sendmmsg
3.72 0.000232 232 1 bind
3.71 0.000231 231 1 shutdown
------ ----------- ----------- --------- --------- ----------------
100.00 0.006229 30 2 total
  • time:时间占比
  • seconds:总执行时间
  • usecs/call:平均每次执行时间(微秒)
  • calls:执行次数
  • errors:执行出错次数

3、什么时候使用strace?

3.1、诊断、调试

strace的man手册里是这么介绍自己的

strace  is  a useful diagnostic, instructional, and debugging tool.
System administrators, diagnosticians andtrouble-shooters will find it invaluable
for solving problems with programs for which the source is not readily available since they do not need to be recompiled in order to trace them.

strace是诊断、调试工具。尤其是代码不可用的时候,因为无需重新编译就可以跟踪system call。
例如一个程序调用system call失败退出,但是程序没输出关键日志。通过strace就可以获取调用的参数、返回值、err no。迅速定位问题。

3.2、学习用途

一个程序的实现,无外乎数据+算法+system call。一些“神奇”的程序往往要借助system call实现。例如tcpdump如何实现抓包,gdb如何实现调试。我们都可以使用strace来找灵感。

可以通过strace来了解程序调用了哪些system call,来大致摸清程序实现的骨架。甚至一会我们会利用strace来学习strace是如何实现的。

最后,全网同名,求关注、点赞、转发,谢谢~

造轮子-strace(一)的更多相关文章

  1. 造轮子-strace(二)实现

    这一篇文章会介绍strace如何工作,再稍微深入介绍一下什么是system call.再介绍一下ptrace.wait(strace依赖的system call).最后再一起来造个轮子,动手用代码实现 ...

  2. 避免重复造轮子的UI自动化测试框架开发

    一懒起来就好久没更新文章了,其实懒也还是因为忙,今年上半年的加班赶上了去年一年的加班,加班不息啊,好了吐槽完就写写一直打算继续的自动化开发 目前各种UI测试框架层出不穷,但是万变不离其宗,驱动PC浏览 ...

  3. 【疯狂造轮子-iOS】JSON转Model系列之二

    [疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...

  4. 【疯狂造轮子-iOS】JSON转Model系列之一

    [疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...

  5. h5engine造轮子

    基于学习的造轮子,这是一个最简单,最基础的一个canvas渲染引擎,通过这个引擎架构,可以很快的学习canvas渲染模式! 地址:https://github.com/RichLiu1023/h5en ...

  6. 我为什么还要造轮子?欠踹?Monk.UI表单美化插件诞生记!

    背景 目前市场上有很多表单美化的UI,做的都挺不错,但是他们都有一个共同点,那就是90%以上都是前端工程师开发的,导致我们引入这些UI的时候,很难和程序绑定.所以作为程序员的我,下了一个决定!我要自己 ...

  7. 「iOS造轮子」之UIButton 用Block响应事件

    俗语说 一个不懒的程序员不是好程序员 造轮子,也只是为了以后更好的coding. coding,简易明了的代码更是所有程序员都希望看到的 无论是看自己的代码,还是接手别人的代码 都希望一看都知道这代码 ...

  8. 重复造轮子感悟 – XLinq性能提升心得

    曾经的两座大山 1.EF 刚接触linq那段时间,感觉这家伙好神奇,语法好优美,好厉害.后来经历了EF一些不如意的地方,就想去弥补,既然想弥补,就必须去了解原理.最开始甚至很长一段时间都搞不懂IQue ...

  9. GitHub Android 最火开源项目Top20 GitHub 上的开源项目不胜枚举,越来越多的开源项目正在迁移到GitHub平台上。基于不要重复造轮子的原则,了解当下比较流行的Android与iOS开源项目很是必要。利用这些项目,有时能够让你达到事半功倍的效果。

    1. ActionBarSherlock(推荐) ActionBarSherlock应该算得上是GitHub上最火的Android开源项目了,它是一个独立的库,通过一个API和主题,开发者就可以很方便 ...

随机推荐

  1. 【Android】修改快捷键,前一步默认是Ctrl + Z,修改后一步

    我已经忘了,我什么时候已经习惯前一步是Ctrl + Z,后一步是Ctrl + Y Android Studio默认前一步快捷键是相同的,但是后一步就不是了 Ctrl + Y变成删除一行代码,就是下图D ...

  2. 【Go】【Basic】MacOS上搭建GO开发环境

    1. GO下载 1.1. 下载地址:https://www.golangtc.com/download (需要科学上网) 1.1.1. PKG安装: 下载这个包:go1.9.2.darwin-amd6 ...

  3. entfrm-boot开发平台功能介绍【entfrm开源模块化无代码开发平台】

    简介 entfrm开发平台,是一个以模块化为核心的无代码开发平台,是一个集PC和APP快速开发.系统管理.运维监控.开发工具.OAuth2授权.可视化数据源管理与数据构建.API动态生成与统计.工作流 ...

  4. 【编程思想】【设计模式】【行为模式Behavioral】chain

    Python版 https://github.com/faif/python-patterns/blob/master/behavioral/chain.py #!/usr/bin/env pytho ...

  5. 【Java 基础】Java动态代理

    Java动态代理InvocationHandler和Proxy java动态代理机制中有两个重要的类和接口InvocationHandler(接口)和Proxy(类),这一个类Proxy和接口Invo ...

  6. ubuntu qq/微信

    Ubuntu qq&微信安装/启动脚本. Docker 本脚本依赖Docker,需要提前安装好Docker环境.参考https://yeasy.gitbooks.io/docker_pract ...

  7. leetcode,两个排序数组的中位数

    先上题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2 . 请找出这两个有序数组的中位数.要求算法的时间复杂度为 O(log (m+n)) . 你可以假设 nums1 和  ...

  8. 【Spark】【RDD】从HDFS创建RDD

    1.在HDFS根目录下创建目录(姓名学号) hdfs dfs -mkdir /zwj25 hdfs dfs -ls / 访问 http://[IP]:50070 2.上传本地文件到HDFS hdfs ...

  9. KrakenD url匹配通配符 url_pattern wildcard

    KrakenD是一个高性能Api网关,  api转发的推荐做法是每个api一个配置项,也就是一个endpoint,其开发者认为api网关和纯粹的L7路由不一样(文章链接). 因此社区版并没有提供通配符 ...

  10. log4j漏洞的产生原因和解决方案,小白都能看懂!!!!

    核弹级bug Log4j,相信很多人都有所耳闻了,这两天很多读者都在问我关于这个bug的原理等一些问题,今天咱们就专门写一篇文章,一起聊一聊这个核弹级别的bug的产生原理以及怎么防止 产生原因 其实这 ...