文档章节

用strace跟踪malloc内存分配

liupengs
 liupengs
发布于 2016/08/30 11:56
字数 1821
阅读 247
收藏 1

strace介绍

strace是一个非常有用的命令,它用于记录和跟踪程序运行期间收到的信号和调用的系统调用。

strace的简单使用

ubuntu64:~$ strace cat /dev/null
execve("/bin/cat", ["cat", "/dev/null"], [/* 32 vars */]) = 0
brk(NULL) = 0x112e000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)

. . .

read(3, "", 131072) = 0
munmap(0x7f19a65be000, 139264) = 0
close(3) = 0
close(1) = 0
close(2) = 0
exit_group(0) = ?
+++ exited with 0 +++

每一行表示一个系统调用, 左边为系统调用的名称和参数, 右边为系统调用返回的结果。

参数

-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

指定一个表达式,用来控制如何跟踪.格式如下:

[qualifier=][!]value1[,value2]...

qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:

-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.

注意有些shell使用!来执行历史记录里的命令,所以要使用\\.

-e trace=set

只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.

-e trace=file

只跟踪有关文件操作的系统调用.

-e trace=process

只跟踪有关进程控制的系统调用.

-e trace=network

跟踪与网络有关的所有系统调用.

-e strace=signal

跟踪所有与系统信号有关的 系统调用

-e trace=ipc

跟踪所有与进程通讯有关的系统调用

-e abbrev=set

设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.

-e raw=set

将指 定的系统调用的参数以十六进制显示.

-e signal=set

指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.

-e read=set

输出从指定文件中读出 的数据.例如:

-e read=3,5

-e write=set

输出写入到指定文件中的数据.

-o filename

将strace的输出写入文件filename

-p pid

跟踪指定的进程pid.

-s strsize

指定输出的字符串的最大长度.默认为32.文件名一直全部输出.

-u username

以username 的UID和GID执行被跟踪的命令

 

用strace查看malloc内存分配

我在博客 c语言内存无法释放 中曾经提到过 :malloc采用了两中不同的方式来处理内存申请。

1. 若分配内存小于 128k ,调用 sbrk() ,将堆顶指针向高地址移动,获得新的虚存空间。

2. 若分配内存大于 128k ,调用 mmap() ,在文件映射区域中分配匿名虚存空间。

现在我们就strace 跟踪在那篇博客中的 Case #3, 在这之前我先修改 Case #3的代码,添加代码(完整的代码在博客尾部):

int *list;
list = malloc(1024*1024);
free(list);

这段代码用于查看当申请的内存大于128k时, malloc的处理方法。

strace 跟踪结果如下:

ubuntu64:~$ strace ./test_mem
execve("./test_mem", ["./test_mem"], [/* 32 vars */]) = 0

. . .

brk(0xd0e000) = 0xd0e000  //通过将堆顶向高地址移动到0xd0e000为进程分配内存
write(1, "before malloc, the top of heap i"..., 43before malloc, the top of heap is 0xced000) = 43
write(1, "address of a is 0xced420 top of "..., 62address of a is 0xced420 top of heap after malloc is 0xd0e000) = 62
brk(0xd40000) = 0xd40000  // 同样通过移动堆顶分配内存
write(1, "address of a is 0xd06430 top of "..., 62address of a is 0xd06430 top of heap after malloc is 0xd40000) = 62
write(1, "address of a is 0xd1f440 top of "..., 62address of a is 0xd1f440 top of heap after malloc is 0xd40000) = 62
brk(0xd72000) = 0xd72000  // 同样通过移动堆顶分配内存
write(1, "address of a is 0xd38450 top of "..., 62address of a is 0xd38450 top of heap after malloc is 0xd72000) = 62
write(1, "address of a is 0xd51460 top of "..., 62address of a is 0xd51460 top of heap after malloc is 0xd72000) = 62
brk(0xda4000) = 0xda4000  // 同样通过移动堆顶分配内存
write(1, "address of a is 0xd6a470 top of "..., 62address of a is 0xd6a470 top of heap after malloc is 0xda4000) = 62
write(1, "address of a is 0xd83480 top of "..., 62address of a is 0xd83480 top of heap after malloc is 0xda4000) = 62
brk(0xdd6000) = 0xdd6000  // 同样通过移动堆顶分配内存
write(1, "address of a is 0xd9c490 top of "..., 62address of a is 0xd9c490 top of heap after malloc is 0xdd6000) = 62
write(1, "address of a is 0xdb54a0 top of "..., 62address of a is 0xdb54a0 top of heap after malloc is 0xdd6000) = 62
brk(0xe08000) = 0xe08000  // 同样通过移动堆顶分配内存
write(1, "address of a is 0xdce4b0 top of "..., 62address of a is 0xdce4b0 top of heap after malloc is 0xe08000) = 62
//用mmap在文件映射区为进程分配内存
mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0cd5140000
write(1, "malloc end\n", 11malloc end) = 11
write(1, "before free, the top of heap is "..., 41before free, the top of heap is 0xe08000) = 41
brk(0xd0e000) = 0xd0e000 //释放通过brk移动堆顶分配的内存
write(1, "after free, the top of heap is "..., 41after free, the top of heap is 0xd0e000) = 41
munmap(0x7f0cd5140000, 1052672) = 0 //释放mmap分配的内存
exit_group(0) = ?

+++ exited with 0 +++

结果中省略了程序加载部分内容,从输出结果可以看出:

1. 在为链表申请节点时, 因为每个节点size小于128k(128k是可以调整的), malloc直接通过brk()函数在堆上分配内存。

2. 为list分配内存时,因为它需要1M内存, 所以malloc通过mmap()函数在在文件映射区域分配内存。

附录

1. 32位系统中进程虚拟空间布局

进程虚拟空间布局

2. Case #3代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct node
{
         char a[1024*100];
         struct node *next;
 }node;

 int main()
{
         node *node_first = NULL, *node_now;
         node *a;
         int i;
         char s_cmd[100];
         sprintf(s_cmd, "pmap -d %lu | grep mapped", getpid());
 
         printf("before malloc, the top of heap is 0x%lx\n", sbrk(0));
 
         for(i=0;i<10;i++){
                 a=(node *)malloc(sizeof(node));
                 a->next=NULL;
                 if(node_first==NULL) node_first=a;
                 else node_now->next=a;
                 node_now=a;
 
                 printf("address of a is 0x%lx ", a);
                 printf("top of heap after malloc is 0x%lx\n", sbrk(0));
         }

         int *list;
         list = malloc(1024*1024);
         printf("malloc end\n");

         printf("before free, the top of heap is 0x%lx\n", sbrk(0));
         while(node_first!=NULL)
         {
                 node_now=node_first->next;
                 free(node_first);
                 node_first=node_now;
         }
         printf("after free, the top  of heap is 0x%lx\n", sbrk(0));

         free(list);

         return 0;
 }

参考

http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html

https://en.wikipedia.org/wiki/Strace

by@ liupengs

© 著作权归作者所有

共有 人打赏支持
liupengs
粉丝 4
博文 5
码字总数 6999
作品 0
武汉
程序员
私信 提问
自助Linux之问题诊断工具strace [转]

引言 “Oops,系统挂死了..." “Oops,程序崩溃了..." “Oops,命令执行报错..." 对于维护人员来说,这样的悲剧每天都在上演。理想情况下,系统或应用程序的错误日志提供了足够全面的信息,通...

文艺小青年
2017/07/13
0
0
Spring Boot引起的“堆外内存泄漏”排查及经验总结

背景 为了更好地实现对项目的管理,我们将组内一个项目迁移到MDP框架(基于Spring Boot),随后我们就发现系统会频繁报出Swap区域使用量过高的异常。笔者被叫去帮忙查看原因,发现配置了4G堆...

美团技术团队
01/04
0
0
强大的strace命令用法详解

strace是什么? 按照strace官网的描述, strace是一个可用于诊断、调试和教学的Linux用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。 st...

sailikung
2018/08/29
0
0
strace 分析 跟踪 进程错误

strace是什么? 按照strace官网的描述, strace是一个可用于诊断、调试和教学的Linux用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。 st...

jicki
2016/05/24
0
0
运维利器万能的 strace

运维利器万能的 strace 运维生存时间 - 运维生存时间2017-12-022 阅读 straceLinux命令 发表评论 A+ 所属分类: Linux命令 strace是什么? 按照strace官网的描述, strace是一个可用于诊断、调...

运维生存时间 - 运维生存时间
2017/12/02
0
0

没有更多内容

加载失败,请刷新页面

加载更多

泛型就这么简单

前言 从今天开始进入Java基础的复习,可能一个星期会有一篇的<十道简单算法>,我写博文的未必都是正确的~如果有写错的地方请大家多多包涵并指正~ 今天要复习的是泛型,泛型在Java中也是个很...

群星纪元
29分钟前
3
0
大数据提醒你:中国这些古建筑,可能是下一个巴黎圣母院!

大家晚上好,我是今天的提笔人嗅嗅。 巴黎圣母院失火事件让我的心情很沉重,一句无关痛痒的安慰“巴黎不哭”,已经不能表达我对这场文化之殇的惋惜之痛,人类伟大的建筑在一瞬间被毁灭。 世界...

forespider
40分钟前
0
0
mysql函数substring_index的用法

substring_index 按索引字符位进行截取字符串 substring_index(“待截取的字符串”,“截取数据依据的字符”,截取字符的位置N) 第三个参数可正,可负。正数表示索引字符前面的字符串,负数...

echojson
40分钟前
1
0
好程序员web前端分享用CSS和JS打造一个简单的图片编辑器

好程序员web前端分享用CSS和JS打造一个简单的图片编辑器,本文主要是利用CSS的 filter和简单的Jquery代码来实现一个简单的图片编辑器,包括对图片的透明度,黑白,图片亮度等调节。 CSS filt...

好程序员IT
50分钟前
2
0
浅析spring mvc的细节

spring mvc 整体结构 系统监听到请求 -> 通知tomcat -> 根据web.xml 通知相应的拦截器(spring mvc 通常指DispatcherServlet) --> 检查url是否有相匹配的请求实现 --> 拿到请求实现bean的适配...

最爱肉肉
51分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部