LeakTracer代码学习(1)

news2024/11/13 12:26:56

项目中有的时候会产生内存泄漏,以往的经验,检测工具更倾向于使用LeakTracer进行检测泄漏问题,但是直接使用会有些问题,比如堆栈不全都是??等问题,该专题希望自己能够坚持将LeakTracer的源码梳理清楚,以供后续的定制化开发,也可以根据这个源码的学历,扩充下自己的知识面

从哪里开始,随便吧,从下面这个函数

static void __attribute__ ((constructor)) MemoryTraceOnInit(void);
static void __attribute__ ((destructor)) MemoryTraceOnExit(void);

重点是__attribute__ ((constructor))和__attribute__ ((destructor))

constructor参数让系统执行main()函数之前调用函数(被__attribute__((constructor))修饰的函数).同理, destructor让系统在main()函数退出或者调用了exit()之后,调用我们的函数.带有这些修饰属性的函数,对于我们初始化一些在程序中使用的数据非常有用

测试发现,无论这个函数定义在main.cpp里面,还是另外一个test.cpp,只要编译或者链接到了执行程序,都会在执行main之前执行上述函数

一、初始化

1. MemoryTraceOnInit从名字可以看出,是内存检测的初始化逻辑

 新知识:pthread_once

int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));

本函数使用初值为PTHREAD_ONCE_INITonce_control变量保证init_routine()函数在本进程执行序列中仅执行一次

 可以看到的确是PTHREAD_ONCE_INIT

MemoryTrace::init_no_alloc_allowed函数在做什么呢?

 不急,一行一行分析

108行  libc_alloc_func_t是个结构体:

 111行遍历一个该结构体的数组

第 一个字符串,表示是什么函数,第二个参数就是libc的标准函数,第三个是什么?

那lt_malloc来说,就是定义了一个返回指针的函数指针,感觉这个地方有点绕,还没有理解这么设计的初衷

返回前面的代码,for循环其实就是将libc的标准函数指针设置给结构体第三个参数

问题?else不会执行吗,else里面是干嘛的?

函数定义 void *dlsym(void *handle, const char* symbol);

handle是由dlopen打开动态链接库后返回的指针,symbol就是要求获取的函数的名称。dlsym函数的返回值是void*,指向要查找的函数symbol的地址,供调用使用

使用RTLD_NEXT参数找到的的函数指针就是后面第一次出现这个函数名的函数指针。

我们可能会链接多个动态库,不同的动态库可能都会有symbol这个函数名,那么使用RTLD_NEXT参数后dlsym返回的就是第一个遇到(匹配上)symbol这个符号的函数的函数地址。进一步的我们使用dlsym的返回调用的也就是这个第一个匹配上的函数了

那感觉上面代码的意思是,要么调用__libc_malloc要呢调用malloc

继续后面的代码

这个感觉在创建一个实例,但是这个创建方式,以前也没有用过,具体如下

上述代码中的s_memoryTrace_instance,是下面的定义(一个对象大小的char数组)

 然后里面这个char数组强制转换成MemoryTrace的一个静态对象指针,然后调用构造函数

这个过程应该是说,C++里面对象的new其实分成了“内存创建” + “构造函数调用”,这里只是new的过程拆开做了,为什么?

是不是重载new之后,new就不能像上述流程一样工作了,所以创建对象必须拆分?

继续后面的代码,pthread_key_create是干嘛的?

键、键析构函数的创建(pthread_key_create)

#include <pthread.h>
int pthread_key_create(pthread_key_t *keyp,void (*destructor)(void*));//返回值:成功返回0;否则返回错误编号

  • 功能:在分配线程私有数据之前,需要创建与私有数据关联的键。这个键用于获取对线程私有数据的访问,使用pthread_key_create可以创建一个键(参数1)

keyp参数:
要先定义一个pthread_key_t类型的键变量,然后将该键变量的地址赋值给此参数,之后pthread_key_create就可以初始化该键
这个键可以被进程中的所有线程使用,然后线程把这个键与自己线程内的私有数据地址进行关联
destructor参数:
此函数是与键关联的析构函数,函数的参数就是keyp(因为键与线程私有数据地址相关联(相同),所以传入的也就是私有数据的地址)
如果线程使用malloc等函数为线程私有数据分配内存,此参数作为析构函数就会释放线程私有数据分配的内存。如果线程在没有释放内存之前就退出,那么这么内存就会丢失(造成线程所属的进程出现了内存泄漏)
如果此参数为空,就表明没有析构函数与这个键关联

OK前面基本介绍这个函数的内部逻辑 

就做了下面3件事情

  1. 初始化函数指针,比如malloc要调用哪个函数
  2. 构造一个MemoryTrace对象单例
  3. 创建一个线程私有数据__thread_internal_disabler_key


继续往下,可以看到做完init_no_alloc_allowed后,立马获取了上面第2步创建的单例对象,该对象调用了AllMonitoringIsDisabled()接口,判断是否执行后面的语句(通过代码名称可以猜测,在全局初始化之前需要进行检查)

该接口中使用到了前面第3步创建的线程私有数据

Linux线程私有数据Thread-specific Data(TSD) 详解 - 知乎

线程局部存储-pthread_getspecific和pthread_setspecific使用_tiny丶的博客-CSDN博客

通过上面博客讲解,大概明白,线程初始化内存检测之前,需要判断私有数据是否被设置过,像现在的情况,第一次执行,__monitoringDisabler初始值为0且没有调用pthread_setspecific为key绑定值,因此这个位置就会返回false,然后调用的地方继续往下走

这个判断是处理多线程调用的时候处理的


继续往下

 第一次执行判断返回false,那就回执行MemoryTrace::init_full_from_once函数

其实就是调用下面函数

 查看这个代码,主体逻辑都是在获取环境变量,判断是否设置了些参数,其实对于TSD还是有点没有理解透

这个位置创建一个线程私有数据的目的是什么?

(应该是在线程退出的时候,清理内存统计类的环境,这个地方创建了key,为什么不在这里给key绑定资源,而是在别的位置绑定?)

__monitoringDisabler这个参数不是原子变量,先++然后--,不会有多线程的问题吗


继续查看这个函数后续获取环境变量的代码,比如我们经常通过信号来开启和关闭内存泄漏检测,

LD_PRELOAD=/usr/lib/libleaktracer.so LEAKTRACER_ONSIG_STARTALLTHREAD=USR1 LEAKTRACER_ONSIG_REPORT=USR2 LEAKTRACER_ONSIG_REPORTFILENAME=leaks.out demo

 看下它在做什么

这个里面涉及到信号捕获函数

 #include <signal.h>
 int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

 ◆ signum:要操作的信号。
 ◆ act      :要设置的对信号的新处理方式。
 ◆ oldact :原来对信号的处理方式。
 ◆ 返回值:0 表示成功,-1 表示有错误发生

struct sigaction 类型用来描述对信号的处理,定义如下:
 struct sigaction
 {
  void     (*sa_handler)(int);
  void     (*sa_sigaction)(int, siginfo_t *, void *);
  sigset_t  sa_mask;
  int       sa_flags;
  void     (*sa_restorer)(void);
 };

◆ sa_handler 是一个函数指针

◆ sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息(当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数)

◆ sa_mask 成员用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生

◆ sa_flags 成员用于指定信号处理的行为,它可以是一下值的“按位或”组合(我们只学习SA_SIGINFO ,其他不去管)

 ◆  re_restorer 成员则是一个已经废弃的数据域,不要使用

这个信号触发的函数sigactionHandler到底在做什么操作?

可以看到这里有3个判断,

第一个判断信号是不是触发内存检测开始检测

第二个判断信号是不是停止内存检测

第二个判断信号是不是现在输出报告

先看怎么触发的内存检测开始

 这个位置为什么又调用了一次Setup?

后续的判断,猜测就是判断当前是不是已经停止了内存检测,如果当前仍然在内存检测过程中,这个时候再次触发开始检测,里面的代码不执行,如果当前处理非检测中,则将检测运行中标记设置为true

如果当前没有执行内存检测,那将上次的结果清理掉

 TMapMemoryInfo是一个模板类,还挺复杂的,其实就是维护每次申请内存的信息

再往后这个stopMonitoringPerThreadAllocations()是干嘛的,停止所有线程检测?

这个操作内部就是将线程参数中监测内存分配的标志设置为false

 上面是开始的处理方式

结束的处理方式:

上面其实就是将3个标志为全部设置为false

__monitoringAllThreads = false(如果有别的线程已经设置成了false,就将线程的检测分配标志设置为false)

__monitoringReleases  = false


继续往后,init_full最后几步

 这里有个堆栈输出,不知道为什么,感觉像是初始化堆栈输出逻辑一样,因为输出之后bt没有用到,看注释好像也是这个意思

初始化完成

二、退出

MemoryTraceOnExit

 退出(这个只是将内存检测工具退出,并不是将主程序退出)需要使用环境变量设置才行,如果不设置该值,检测工具将会随着主程序一直工作(实际使用中没有必要设置该值)

看下这个接口做了什么

1. stopAllMonitoring

 和前面停止所有线程检测一样调用这个接口

然后输出报告(这个比较重要,可能有的需要定制

就是调用writeLeaksPrivate函数,函数的主体部分如下所有,就是遍历链表所有的节点信息,并输出到文件,包括申请的地址和大小等

第二节再详细的梳理这个链表中的数据结构是怎么样的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/733561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ModaHub魔搭社区:RESTful API 的方式访问全球领先的向量数据库Milvus

目录 Insert CreateIndex and Load Search or Query 作为全球领先的开源向量数据库,Milvus 一直致力于满足不同用户的场景和需求,聆听社区的声音。 最近, 我们发现,很多用户的数据中 常常包含各种不确定类型的数据,也有用户提出希望以 RESTful API 的方式访问 Milvus。…

【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答(三)

贴接上回。。。 【往期FAQ参考】 【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答&#xff08;一&#xff09; 【HarmonyOS】【FAQ】HarmonyOS应用开发相关问题解答&#xff08;二&#xff09; 【本期FAQ】 1、第一次调用geolocation.getCurrentLocation()接口&#xff…

一份非常牛逼的计算机相关技术资料整理

最近发现GitHub上一个非常牛逼的项目。作者收录了一整套 计算机相关的技术资料整理。 收录内容包括&#xff0c;但不仅仅包括&#xff0c;比如比较实用的计算机相关技术书籍&#xff0c;可以在短期之内入门的简单实用教程、一些技术网站以及一些写的比较好的博文。真的得给作者…

postman测试传参格式

postman测试传参格式 创建User实体 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;Data NoArgsConstructor AllArgsConstructor public class User {private Integer id;private String name; }接口参数是集合 PostMapping("…

大规模语言模型剪枝又一力作,比SparseGPT还快300倍!

©Paperweekly 原创 作者 | An. 单位 | 中科院自动化所 研究方向 | 计算机视觉、模型压缩 DenseNet、ConvNeXt、Network Slimming 一作刘壮研究员的剪枝新工作&#xff0c;针对 LLMs 特点设计的极低成本无需微调的剪枝算法&#xff0c;耗时接近幅值剪枝&#xff0c;性能表…

基于Spring Boot的高校实验室信息管理系统设计与实现(Java+spring boot+MySQL+VUE)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于Spring Boot的高校实验室信息管理系统设计与实现&#xff08;Javaspring bootMySQLVUE&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 后端&#xff1a;Java springboot…

玩转ChatGPT:Code interpreter (vol. 1)

一、写在前面 喜大普奔&#xff0c;GPT-4传说中的Code interpreter插件已经可以用了&#xff01;&#xff01;&#xff01; 二、什么是Code interpreter 代码解释器&#xff08;Code Interpreter&#xff09;是一个工具&#xff0c;它能够读取和执行编程代码。这里有几个主要…

程序请求报错java.lang.NoSuchMethodError

[23-7-3 9:09:19:069 CST] 00000017 ServletWrappe E com.ibm.ws.webcontainer.servlet.ServletWrapper service SRVE0068E:应用程序 east5_20230629_war 中 servlet XXX 的某一服务方法创建了未捕获到的异常。 创建的异常&#xff1a;org.springframework.web.util.NestedServ…

Android 短视频直播特效,音视频图像处理 FFmepg OpenGLES OpenCV开发详细内容

1 音视频开发基础 2 Android OpenGL ES开发基础 3 Android FFmpeg OpenGLES 音视频播放器核心开发 4 Android FFmpeg H.264 AAC 封装mp4 5 Android OpenCV 机器学习人脸标定SDK开发实战 6 Android OpenCV 开发实践 7 Android 短视频App FFmpeg OpenGL ES OpenCV人脸特效渲…

python pytorch 纯算法实现前馈神经网络训练(数据集随机生成)

python pytorch 纯算法实现前馈神经网络训练&#xff08;数据集随机生成&#xff09; 下面这个代码大家可以学习学习&#xff0c;这个代码难度最大的在于反向传播推导&#xff0c; 博主推了很久&#xff0c;整个过程都是纯算法去实现的&#xff0c;除了几个激活函数&#xff0…

【前端知识】React 基础巩固(十八)——组件化开发(二)

React 基础巩固(十八)——组件化开发&#xff08;二&#xff09; 生命周期 生命周期是一个抽象的概念&#xff0c;在生命周期的整个过程中&#xff0c;分成了很多个阶段 比如装载阶段&#xff08;Mount&#xff09;&#xff0c;组件第一次在 DOM 树中被渲染的过程比如更新过程…

【C语言】你知道浮点数是怎么存储的吗?

前言 &#x1f388;大家好&#xff0c;我是何小侠&#x1f388; &#x1f343;大家可以叫我小何或者小侠&#x1f343; &#x1f490;希望能通过写博客加深自己对于学习内容的理解&#x1f490; &#x1f338;也能帮助更多人理解和学习&#x1f338; 积学以储宝&#xff0c;酌…

Debian 11 x64 安装 MySQL 8.0.33

更新 sudo apt update sudo apt install gnupg安装 DEB Package wget -c https://dev.mysql.com/get/mysql-apt-config_0.8.25-1_all.deb sudo dpkg -i mysql-apt-config_0.8.25-1_all.deb具体版本见官方网站&#xff1a;MySQL Community Downloads&#xff0c;这里仅以版本 …

详解什么是新零售和新零售的四种商业模式

前言 自推出新零售概念以来&#xff0c;新零售已成为当前的热门话题。今天我们将进一步了解什么是新零售。 一、什么是新零售? 新零售&#xff0c;英文是New Retailing&#xff0c;即企业以互联网为依托&#xff0c;通过运用大数据、人工智能等先进技术手段&#xff0c;对商…

VMware虚拟机里的Ubuntu通过主机的代理联网

问题描述&#xff1a;主机win10&#xff0c;通过代理联网。主机里装有VMware的虚拟机Ubuntu&#xff0c;想要通过主机的代理进行上网。 步骤&#xff1a; 1 将虚拟机的网络设置为NAT模式。 2 在win10命令行中输入ipconfig&#xff0c;查询ipv4的局域网地址。&#xff08;注&…

使用docker安装Nacos,远程连接nacos报错,please check server x.x.x.x ,port 9848 is available

报错: please check server 127.0.0.1 ,port 9848 is available 原因: 当nacos客户端升级为2.x版本后&#xff0c;新增了gRPC的通信方式&#xff0c;新增了两个端口。这两个端口在nacos原先的端口上(默认8848)&#xff0c;进行一定偏移量自动生成.。 当客户端升级成2.x版本时&…

[工业互联-20]:常见EtherCAT主站方案:TwinCAT的Windows 解决方案

目录 第1章 TwinCAT简介 第2章 软件架构 第3章 应用程序架构 第1章 TwinCAT简介 TwinCAT是由德国Beckhoff公司开发的一套功能强大的自动化软件平台。 它是一个集成的开发环境&#xff0c;用于实现实时控制、PLC编程、运动控制、HMI&#xff08;人机界面&#xff09;设计和…

service 2 暴露服务的 3种 方式

【k8s 系列】k8s 学习十九&#xff0c;service 2 之前我们简单的了解一下 k8s 中 service 的玩法&#xff0c;今天我们来分享一下 service 涉及到的相关细节&#xff0c;我们开始吧 为什么要有 服务 Service&#xff1f; 因为服务可以做到让外部的客户端不用关心服务器的数量…

【二叉树part09】| 669.修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

目录 &#x1f388;LeetCode669. 修剪二叉搜索树 &#x1f388;LeetCode108.将有序数组转换为二叉搜索树 &#x1f388;LeetCode538.把二叉搜索树转换为累加树 &#x1f388;LeetCode669. 修剪二叉搜索树 链接&#xff1a;669.修剪二叉搜索树 给你二叉搜索树的根节点 root…

使用Go 语言的三个原因

几个星期前&#xff0c;我一个朋友问我&#xff1a;“为什么要关心 Go 语言”&#xff1f; 因为他们知道我热衷于 Go 语言&#xff0c;但他们想知道为什么我认为其他人也应该关心。有三个原因&#xff1a;安全性、生产力和并发性。有些语言可以涵盖一个也有可能是两个方面&…