用戶棧的高效解析邏輯

一、背景

在之前的博客?內核邏輯里抓取用戶棧的幾種方法-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.pswp.cn/web/80800.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/80800.shtml
英文地址,請注明出處:http://en.pswp.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 測試方案性能測試工…