用户栈的高效解析逻辑

一、背景

在之前的博客 内核逻辑里抓取用户栈的几种方法-CSDN博客 里,介绍了使用内核逻辑进行用户栈的函数地址的抓取逻辑,但是并没有涉及如何解析出函数符号的逻辑。

就如perf工具一样,它也是分为两个步骤,一个步骤是内核态抓取函数地址的PC调用链,第二个步骤则是在用户态逻辑里根据抓取的PC转换成人可阅读的函数符号名字。

当前如果不去考虑解析的性能的话,那么只要拿到elf的路径及offset(具体参考之前的博客 内核逻辑里抓取用户栈的几种方法-CSDN博客),然后通过addr2line就可以解析出用户栈的调用链符号,但是这样解析的话,重复的进行进程创建及退出,性能是不够好的。这篇博客里,我们就讲如何高效地进行用户栈的调用链符号的解析。

二、高性能解析的核心思想

2.1 复用elf的解析结果,预解析常用的so

该高性能解析的核心思想就是启动一个用来解析elf和进程的maps表的应用服务,该应用服务可以以守护进程方式长期运行,被解析请求触发来执行。

由于该应用服务是常驻的,所以就避免了反复启动进程重复解析一样的elf文件导致性能损失。该应用服务会把已经解析elf文件得到的信息进行记录,下一次需要解析相同的elf文件时复用之前解析的结果,同样的,下一次需要解析相同的进程时,也可以复用之前的/proc/<pid>/maps的解析结果。

对于elf文件,一般来说,它是不变的。但是要注意,对于/proc/<pid>/maps里的内容,它是可能变化的,如程序使用dlopen/dlclose这种动态加载和解加载so库的函数,就会触发maps的变更,在实现时需要考虑这样的情况。

另外,为了进一步提升性能,我们可以在该进程启动后,先把一些常用的so库先解析出来,这样在真正接到要解析的请求时由于之前已经解析过了,就可以减少第一次解析某个so的elf文件的耗时。

2.2 使用map容器的upper_bound函数快速找到对应的符号及偏移

假设我们已经解析了某个elf的文件,拿到了函数表,我们如何能快速找到对应的符号呢?

我们可以使用map容器的upper_bound接口来实现这样的快速查找,并且map容器的下标得用地址区间的end,而不能用地址区间的start。

这算是一个小算法,但是也有一定的细节,不能用lower_bound,也不能用start作为key,否则会出现解析不符合预期及解析错误的情况。

这里面主要考虑的就是地址区间通常来说是指[start, end)这么一个区间,也就是start是大于等于,而end是小于。

三、完整的解析步骤

这里说的完整的解析步骤是假设了已经拿到了用户栈调用链的PC的情况下的。至于如何抓取用户栈PC,在之前的博客里 内核逻辑里抓取用户栈的几种方法-CSDN博客 给出了通过内核态逻辑抓取的方法。

我们分析,如果根据进程的pid及进程的PC地址的va,得到对应的函数符号和offset。

3.1 先根据进程的pid获取到进程的elf和maps信息的管理对象

每个进程都对应有一个管理对象,来管理进程的相关与解析函数符号逻辑有关的信息,最主要就是/proc/<pid>/maps,其他信息则是用于辅助输出的内容,比如cmdline内容,这些辅助内容的输出可以帮助定位是具体哪个进程,因为有时候进程名是一样的(/proc/<pid>/comm),但是cmdline是不一样的,可以看出一些细节信息。

关于/proc/<pid>/maps的解析,参考如下逻辑:

if (unlikely(!READ_PROC_MAPS(i_processid, [&](char *i_obuf, int i_obufsize) -> bool {unsigned long start;unsigned long end;char permissions[5]; // 读、写、执行、共享unsigned long offset;char pathname[HARDLINK_MAXBYTE];int ret = sscanf(i_obuf, "%lx-%lx %4s %lx %*x:%*x %*u %s",&start, &end, permissions, &offset,pathname);if (ret == 5 && permissions[2] == 'x') {...
#if (DEBUG_LOG == 1)printf("range: %lx-%lx, permission: %s, offset: %lx, hlink: %s\n",start, end, permissions, offset, pathname);
#endifreturn true;}else {return false;}}))) {...break;}

上面的逻辑里使用了lambda表达式,可以简化逻辑。

3.2 根据elf路径进行增量解析

所谓“增量”解析,也就是指已经解析过的elf文件不再重复解析,因为我们已经保存下来之前解析出来的结果了。

我们可以用一个map来保存已经解析过的内容,key表示elf绝对路径。

如果之前没有解析过相关的elf,则使用objdump -t来进行解析,要注意,务必使用objdump -t来解析,因为objdump -t可以解析出弱符号和static的局部符号,而objdump -T则解析不出这些符号。

objdump的命令如下:

"objdump -t %s | grep -E '\\.text'"

上面的%s替换成elf的绝对路径。

3.3 解析vdso及vsyscall的符号

不管哪个平台,一般都有vdso的符号,但是vsyscall则不同的平台不一样,x86上是有的。

有关vdso和vsyscall的基础介绍和相关内核逻辑和glibc逻辑的相关细节见之前的博客 vdso概念及原理,vdso_fault缺页异常,vdso符号的获取-CSDN博客 和 vdso内核与glibc配合的相关逻辑分析-CSDN博客。

3.3.1 vdso符号表的获取

vdso的符号表的获取,我们是通过dd命令从系统上一般都存在的systemd进程里捞取取出相关的so文件内容,并通过objdump进行解析。

通过dd命令捞取vdso.so文件的命令如下:

if (unlikely(!PROC1MAPS_GREP_VDSO([&](char *i_obuf, int i_obufsize) -> bool {unsigned long vdso_va_begin;unsigned long vdso_va_end;if (sscanf(i_obuf, "%lx%*c%lx", &vdso_va_begin, &vdso_va_end) == 2) {...
#if (DEBUG_LOG == 1)printf("vdso_va_begin:0x%lx, vdso_va_end:0x%lx, size:0x%lx\n", vdso_va_begin, vdso_va_end, vdso_va_end - vdso_va_begin);
#endifchar systemcmd[256];snprintf(systemcmd, 256, "dd if=/proc/1/mem of=" TEMP_VDSO_SO_FILE " skip=%lu ibs=1 count=%lu\n", vdso_va_begin, vdso_va_end - vdso_va_begin);system(systemcmd);return true;}return false;}))) {// 出错了return psyminfo;}

上面PROC1MAPS_GREP_VDSO则是执行如下的命令:

"cat /proc/1/maps | grep -E '\\[vdso\\]'"

然后通过sscanf解析出vdso.so的va的begin和end,然后通过dd命令去dump,dump到一个临时文件中。

然后再通过如下的objdump命令进行解析:

"objdump -T %s | grep -E '\\.text'"

上面的%s则是vdso.so的临时文件的路径。

3.3.2 vsyscall符号表的获取

vsyscall的符号表则是直接根据对应平台的内核里的相关符号的内容情况,手动进行组装。有关vsyscall的符号信息如何查看,参考之前的博客 vdso概念及原理,vdso_fault缺页异常,vdso符号的获取-CSDN博客 里的 4.2 一节。

下面的是大致的拼凑逻辑:

..* ...() {..* psyminfo;...// vsyscall目前只用考虑x86场景,x86的vsyscall的情况是固定的,就三个符号...unsigned long start = 0;unsigned long span = 1024;psysrange = ...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "gettimeofday", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifstart += 1024;...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "time", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifstart += 1024;...psysrange->start = start;psysrange->span = span;strscpy(psysrange->sym, "getcpu", SYM_MAXBYTE);
#if (DEBUG_LOG == 1)printf("start:0x%llx, span:0x%llx, typestr:%s, symname:%s \n",psysrange->start, psysrange->span, HLINK_VSYSCALL, psysrange->sym);
#endifreturn psyminfo;}

3.4 通过upper_bound来查找对应的符号及offset

有关为什么用upper_bound在上面 2.2 里做了简要说明。

大致的代码如下:

..* ...(u64 i_addr) {auto it = xx.upper_bound(i_addr);//printf("count[%d]\n", xx.size());if (it != xx.end()) {if (it->second->start <= i_addr && i_addr < it->second->start + it->second->span) {return it->second;}}return NULL;}

3.5 成果展示

我们使用perf来抓取进程pid是1的systemd这个进程的用户态符号,然后,再用该程序进行解析,看解析出的内容是否一致。

用perf record -g -p 1之后,用perf script得到的如下图的一处采样:

我们使用解析程序,输入pid和va,进行解析,可以看到解析出一样的函数符号名和offset。

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

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

相关文章

vue3 el-table 行号

在 Vue 3 中&#xff0c;使用 Element Plus 的 <el-table> 组件来创建表格时&#xff0c;如果你想添加行号&#xff08;即每一行的编号&#xff09;&#xff0c;可以通过自定义列来实现。下面是如何实现的步骤&#xff1a; 1. 安装 Element Plus 首先&#xff0c;确保你…

Linux:进程信号---信号的保存与处理

文章目录 1. 信号的保存1.1 信号的状态管理 2. 信号的处理2.1 用户态与内核态2.2 信号处理和捕捉的内核原理2.3 sigaction函数 3. 可重入函数4. Volatile5. SIGCHLD信号 序&#xff1a;在上一章中&#xff0c;我们对信号的概念及其识别的底层原理有了一定认识&#xff0c;也知道…

UML 图的细分类别及其应用

统一建模语言&#xff08;UML&#xff0c;Unified Modeling Language&#xff09;是一种用于软件系统建模的标准化语言&#xff0c;广泛应用于软件工程领域。UML 图分为多种类别&#xff0c;每种图都有其特定的用途和特点。本文将详细介绍 UML 图的细分类别&#xff0c;包括 类…

「极简」扣子(coze)教程 | 小程序UI设计进阶!控件可见性设置

大师兄在上一期的内容中对用户的UI做了一些简单的介绍。这期大师兄继续介绍UI设计上的进阶小技巧&#xff0c;帮我们获得更好的使用体验。 扣子&#xff08;coze&#xff09;编程 「极简」扣子(coze)教程 | 3分钟学会小程序UI设计&#xff01;从零开始创建页面和瓷片按钮 「极…

2025年渗透测试面试题总结-快手[实习]安全工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 快手[实习]安全工程师 一面问题分析与详细回答 1. 自我介绍 4. 项目问题与解决 7. 防止SQL注入&…

WordPress Madara插件存在文件包含漏洞(CVE-2025-4524)

免责声明 本文档所述漏洞详情及复现方法仅限用于合法授权的安全研究和学术教育用途。任何个人或组织不得利用本文内容从事未经许可的渗透测试、网络攻击或其他违法行为。使用者应确保其行为符合相关法律法规,并取得目标系统的明确授权。 对于因不当使用本文信息而造成的任何直…

互联网大厂Java面试场景:从Spring Boot到分布式缓存技术的探讨

互联网大厂Java面试场景&#xff1a;从Spring Boot到分布式缓存技术的探讨 场景描述 互联网大厂某次Java开发岗面试&#xff0c;主考官是一位严肃的技术专家&#xff0c;而应聘者则是搞笑的程序员“码农明哥”。面试围绕音视频场景的技术解决方案展开&#xff0c;探讨从Sprin…

leetcode hot100刷题日记——8.合并区间

class Solution { public:vector<vector<int>> merge(vector<vector<int>>& intervals) {if(intervals.empty()){//复习empty函数啊&#xff0c;日记1有的return {};}// 按照区间的起始位置进行排序sort(intervals.begin(), intervals.end());vect…

Unity中GPU Instancing使用整理

GPU Instancing是一种绘制调用优化方法,可在单个绘制调用中渲染具有相同材质Mesh的多个副本(实例),可用于绘制在场景中多次出现的几何体(例如,树木或灌木丛),在同一绘制调用中渲染相同的网格,每个实例可以具有不同的属性(如 Color 或 Scale),渲染多个实例的绘制调用…

【后端】【UV】【Django】 `uv` 管理的项目中搭建一个 Django 项目

&#x1f680; 一步步搭建 Django 项目&#xff08;适用于 uv pyproject.toml 项目结构&#xff09; &#x1f9f1; 第 1 步&#xff1a;初始化一个 uv 项目&#xff08;如果还没建好&#xff09; uv init django-project # 创建项目&#xff0c;类似npm create vue⚙️ 第 …

Linux操作系统之进程(二):进程状态

目录 前言 一、补充知识点 1、并行与并发 2、时间片 3、 等待的本质 4、挂起 二. 进程的基本状态 三、代码演示 1、R与S 2、T 3、Z 四、孤儿进程 总结&#xff1a; 前言 在操作系统中&#xff0c;进程是程序执行的基本单位。每个进程都有自己的状态&#xff0c;这些…

大数据技术全景解析:HDFS、HBase、MapReduce 与 Chukwa

大数据技术全景解析&#xff1a;HDFS、HBase、MapReduce 与 Chukwa 在当今这个信息爆炸的时代&#xff0c;大数据已经成为企业竞争力的重要组成部分。从电商的用户行为分析到金融的风险控制&#xff0c;从医疗健康的数据挖掘到智能制造的实时监控&#xff0c;大数据技术无处不…

学习 Android(十一)Service

简介 在 Android 中&#xff0c;Service 是一种无界面的组件&#xff0c;用于在后台执行长期运行或跨进程的任务&#xff0c;如播放音乐、网络下载或与远程服务通信 。Service 可分为“启动型&#xff08;Started&#xff09;”和“绑定型&#xff08;Bound&#xff09;”两大…

投标环节:如何科学、合理地介绍 Elasticsearch 国产化替代方案——Easysearch?

一、Easysearch 定义 Easysearch 是由极限科技&#xff08;INFINI Labs&#xff09;自主研发的分布式搜索型数据库&#xff0c;作为 Elasticsearch 的国产化替代方案&#xff0c;基于 Elasticsearch 7.10.2 开源版本深度优化[1]。 插一句&#xff1a;Elasticsearch 7.10.2 是里…

NVC++ 介绍与使用指南

文章目录 NVC 介绍与使用指南NVC 简介安装 NVC基本使用编译纯 C 程序编译 CUDA C 程序 关键编译选项示例代码使用标准并行算法 (STDPAR)混合 CUDA 和 C 优势与限制优势限制 调试与优化 NVC 介绍与使用指南 NVC 是 NVIDIA 提供的基于 LLVM 的 C 编译器&#xff0c;专为 GPU 加速…

Veo 3 可以生成视频,并附带配乐

谷歌最新的视频生成 AI 模型 Veo 3 可以创建与其生成的剪辑相配的音频。 周二&#xff0c;在谷歌 I/O 2025 开发者大会上&#xff0c;谷歌发布了 Veo 3。该公司声称&#xff0c;这款产品可以生成音效、背景噪音&#xff0c;甚至对话&#xff0c;为其制作的视频增添配乐。谷歌表…

Android本地语音识别引擎深度对比与集成指南:Vosk vs SherpaOnnx

技术选型对比矩阵 对比维度VoskSherpaOnnx核心架构基于Kaldi二次开发ONNX Runtime + K2新一代架构模型格式专用格式(需专用工具转换)ONNX标准格式(跨框架通用)中文识别精度89.2% (TDNN模型)92.7% (Zipformer流式模型)内存占用60-150MB30-80MB迟表现320-500ms180-300ms多线程…

十四、Hive 视图 Lateral View

作者&#xff1a;IvanCodes 日期&#xff1a;2025年5月20日 专栏&#xff1a;Hive教程 在Hive中&#xff0c;我们经常需要以不同于原始表结构的方式查看或处理数据。为了简化复杂查询、提供数据抽象&#xff0c;以及处理复杂数据类型&#xff08;如数组或Map&#xff09;&#…

微软开源GraphRAG的使用教程-使用自定义数据测试GraphRAG

微软在今年4月份的时候提出了GraphRAG的概念,然后在上周开源了GraphRAG,Github链接见https://github.com/microsoft/graphrag,截止当前,已有6900+Star。 安装教程 官方推荐使用Python3.10-3.12版本,我使用Python3.10版本安装时,在初始化项目过程中会报错,切换到Python3.…

XXX企业云桌面系统建设技术方案书——基于超融合架构的安全高效云办公平台设计与实施

目录 1. 项目背景与目标1.1 背景分析1.2 建设目标2. 需求分析2.1 功能需求用户规模与场景终端兼容性2.2 非功能需求3. 系统架构设计3.1 总体架构图流程图说明3.2 技术选型对比3.3 网络设计带宽规划公式4. 详细实施方案4.1 分阶段部署计划4.2 桌面模板配置4.3 测试方案性能测试工…