基于LLVM的memcpy靜態分析工具:設計思路與原理解析(C/C++代碼實現)

在程序開發中,內存復制操作(如memcpy)往往是性能瓶頸的關鍵來源——尤其是大型內存塊的復制,可能導致緩存失效、帶寬占用過高等問題。為了精準定位這些潛在的性能熱點,開發者需要一種能自動識別程序中memcpy調用,并提取其關鍵信息(如復制大小、所在位置)的工具。本文將解析一款基于LLVM的memcpy靜態分析工具,探討其設計思路、實現原理及相關技術背景。

工具功能概述

這款工具的核心目標是靜態掃描LLVM IR(中間表示)或bitcode文件,自動識別其中所有的llvm.memcpy調用(LLVM中內存復制的標準內在函數),并輸出以下關鍵信息:

  • 每次memcpy的復制大小(以字節為單位);
  • 調用所在的函數;
  • 對應的源代碼位置(文件名、行號)——依賴調試信息(Debug Info);
  • 支持按復制大小排序(從大到小),并可切換"詳細信息"和"摘要"兩種輸出模式。

簡單來說,它就像一個"內存復制探測器",能幫開發者快速找到程序中哪些地方在進行大型內存復制,以及這些操作對應源代碼的具體位置。

設計思路:從需求到方案的映射

工具的設計圍繞"如何高效定位并提取memcpy關鍵信息"展開,核心思路可概括為**“層次化遍歷+精準篩選+信息聚合”**。

1. 層次化遍歷:從文件到指令的解析路徑

要分析memcpy調用,首先需要"讀懂"LLVM IR文件。LLVM IR是一種結構化的中間表示,其核心層次結構為:模塊(Module)→ 函數(Function)→ 基本塊(BasicBlock)→ 指令(Instruction)

工具的遍歷邏輯正是遵循這一層次:

  • 先加載整個IR文件作為一個"模塊"(Module),這是LLVM IR的頂層容器;
  • 遍歷模塊中的所有函數(Function)——函數是程序行為的基本單元;
  • 每個函數由多個基本塊(BasicBlock)組成,遍歷每個基本塊;
  • 最終遍歷基本塊中的每一條指令(Instruction),因為memcpy調用本質上是一條函數調用指令。

這種層次化遍歷確保不會遺漏任何潛在的memcpy調用,符合LLVM IR的結構化設計特點。

2. 精準篩選:鎖定llvm.memcpy調用

在遍歷指令的過程中,需要從海量指令中精準識別出memcpy調用。關鍵篩選邏輯基于兩點:

  • 指令類型memcpy是函數調用,因此只關注CallInst(函數調用指令)類型的指令;
  • 被調用函數名:LLVM中內存復制操作統一通過內在函數llvm.memcpy實現(其變體如llvm.memcpy.p0i8.p0i8.i64等也包含"memcpy"標識),因此通過檢查被調用函數的名稱是否包含"memcpy"即可鎖定目標。

3. 信息聚合:提取關鍵數據

找到memcpy調用后,需要提取三類核心信息:

  • 復制大小llvm.memcpy的函數簽名為void @llvm.memcpy.p0i8.p0i8.i64(i8* %dst, i8* %src, i64 %size, i32 %align, i1 %isvolatile),其中第三個參數正是復制大小(%size)。工具通過解析該參數(需確保其為常量,否則無法確定具體值),獲取字節數;
  • 所在函數:記錄該memcpy調用屬于哪個函數,用于定位上下文;
  • 調試信息:通過LLVM的元數據(Metadata)中的"dbg"字段,解析出對應的源代碼文件名、行號甚至所屬子程序(Subprogram),實現IR指令到源代碼的映射。

4. 排序與輸出:聚焦關鍵熱點

為了讓開發者快速關注最可能影響性能的大型復制操作,工具會按復制大小從大到小排序結果。同時,提供"詳細信息"(包含調試信息、所在函數)和"摘要"(僅顯示大小)兩種輸出模式,兼顧深度分析與快速概覽的需求。

實現原理:依托LLVM的核心技術

來看看代碼實現

#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Debug.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/SourceMgr.h"#include "llvm/Support/raw_ostream.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include <iostream>...void print_all(vector<tuple<CallInst*, uint64_t, Function*>> &memcpys) {for (auto& i : memcpys) {
...cout << "memcpy" << " of " << size << " in " << function->getName().data() << " @ " << std::endl;MDNode* metadata = callInst->getMetadata("dbg");if (!metadata) {cout << "  no debug info" << endl;continue;}DILocation *debugLocation = dyn_cast<DILocation>(metadata);while (debugLocation) {DILocalScope *scope = debugLocation->getScope();cout << "  ";if (scope) {DISubprogram *subprogram = scope->getSubprogram();if (subprogram) {const char* name = subprogram->getName().data();cout << name << " ";}}cout << debugLocation->getFilename().data() << ":" << debugLocation->getLine() << std::endl;debugLocation = debugLocation->getInlinedAt();}}
}void print_summary(vector<tuple<CallInst*, uint64_t, Function*>> &memcpys) {for (auto& i : memcpys) {auto callInst = get<0>(i);auto size = get<1>(i);auto function = get<2>(i);cout << "memcpy" << " of " << size << std::endl;}
}int main(int argc, char **argv) {
...if (argc > 2) {if (std::string(argv[2]) == "summary") {summary = true;}}Expected<std::unique_ptr<Module> > m = parseIRFile(fileName, Err, context);if (!m) {errs() << toString(m.takeError()) << "\n";}vector<tuple<CallInst*, uint64_t, Function*>> memcpys;{auto &functionList = m->get()->getFunctionList();for (auto &function : functionList) {//printf("%s\n", function.getName().data());for (auto &bb : function) {for (auto &instruction : bb) {//printf("  %s\n", instruction.getOpcodeName());CallInst *callInst = dyn_cast<CallInst>(&instruction);if (callInst == nullptr) {continue;}//printf("have call\n");Function *calledFunction = callInst->getCalledFunction();if (calledFunction == nullptr) {//printf("no calledFunction\n");continue;}StringRef cfName = calledFunction->getName();if (cfName.find("llvm.memcpy") != std::string::npos) {auto size_operand = callInst->getOperand(2);auto size_constant = dyn_cast<ConstantInt>(size_operand);if (!size_constant) {//printf("not constant\n");continue;}auto size = size_constant->getValue().getLimitedValue();memcpys.push_back({callInst, size, &function});}}}}}std::sort(memcpys.begin(), memcpys.end(), [](auto& x, auto &y) {if (get<1>(y) == get<1>(x)) {return get<2>(x) > get<2>(y);} else {return get<1>(x) > get<1>(y);}});if (summary) {print_summary(memcpys);} else {print_all(memcpys);}
}

If you need the complete source code, please add the WeChat number (c17865354792)

工具的實現深度依賴LLVM的API和中間表示特性,其核心原理可歸納為三點:

1. LLVM IR的結構化訪問

LLVM提供了一套完整的API用于遍歷和操作IR結構:

  • 通過parseIRFile加載IR文件并解析為Module對象;
  • 通過Module::getFunctionList()遍歷所有函數;
  • 函數的基本塊可通過Function::begin()Function::end()遍歷;
  • 基本塊中的指令可通過BasicBlock::begin()BasicBlock::end()遍歷。

這種結構化訪問方式讓工具能高效遍歷程序中的所有指令,無需關心IR的底層語法細節。

2. 內在函數與指令解析

LLVM的內在函數(如llvm.memcpy)是編譯器內部用于表達特定操作的特殊函數,其名稱和參數列表具有固定規范。工具通過以下步驟解析memcpy指令:

  • dyn_cast<CallInst>判斷指令是否為函數調用;
  • CallInst::getCalledFunction()獲取被調用函數;
  • 檢查函數名是否包含"memcpy",確認是內存復制操作;
  • 提取第三個參數(CallInst::getOperand(2)),并通過dyn_cast<ConstantInt>轉換為整數,獲取復制大小(僅處理常量大小,動態大小無法在靜態分析中確定具體值)。

3. 調試元數據的解析

LLVM IR中通過元數據(Metadata)存儲調試信息,遵循DWARF標準(一種廣泛使用的調試信息格式)。工具通過以下方式解析調試信息:

  • CallInst::getMetadata("dbg")獲取與指令關聯的調試元數據;
  • 元數據節點(MDNode)可轉換為DILocation對象,包含文件名(getFilename())、行號(getLine())等信息;
  • 對于內聯函數調用,通過DILocation::getInlinedAt()追溯原始調用位置,確保調試信息的完整性。

相關領域知識點

這款工具涉及多個編譯器與程序分析領域的核心概念,理解這些概念有助于深入把握工具的價值:

1. LLVM與中間表示(IR)

LLVM是一個模塊化的編譯器框架,其核心是中間表示(IR)。IR是一種與平臺無關、與源語言無關的中間代碼,既能被編譯器優化階段處理,也能被轉換為機器碼。由于IR的結構化和規范性,它成為程序靜態分析的理想載體——開發者無需針對C、Rust等不同源語言單獨開發分析工具,只需處理IR即可。

2. 靜態分析技術

靜態分析是指在不執行程序的情況下,通過分析代碼結構提取信息的技術。這款工具正是靜態分析的典型應用:它在編譯期(基于IR)識別memcpy調用,無需運行程序即可定位潛在的性能熱點。靜態分析廣泛用于代碼檢查、性能優化、漏洞檢測等場景。

3. memcpy的性能意義

memcpy是C標準庫中的內存復制函數,在程序中頻繁用于數據塊復制(如結構體復制、緩沖區操作等)。大型memcpy(如復制數MB甚至GB級數據)可能成為性能瓶頸:一方面,它會占用大量內存帶寬;另一方面,可能導致CPU緩存失效,增加訪問延遲。因此,定位并優化大型memcpy是性能調優的重要方向。

4. 調試信息與DWARF

調試信息是連接中間代碼/機器碼與源代碼的橋梁,包含變量名、函數名、文件名、行號等映射關系。DWARF是一種通用的調試信息格式,被LLVM、GCC等主流編譯器采用。工具通過解析DWARF格式的元數據,實現了從IR指令到源代碼位置的精準映射,讓開發者能直接在源代碼中找到需要優化的memcpy調用。

總結與應用場景

這款基于LLVM的memcpy分析工具,通過層次化遍歷IR、精準篩選指令、聚合關鍵信息,為開發者提供了定位大型內存復制操作的高效手段。其設計思路緊扣LLVM IR的結構特點,實現原理依托于LLVM的API和調試元數據機制,最終服務于程序性能優化這一核心需求。

在實際開發中,它可用于:

  • 性能瓶頸定位:快速找到大型memcpy調用,評估其對程序性能的影響;
  • 代碼優化指導:結合源代碼位置,將大型memcpy替換為更高效的實現(如分塊復制、利用SIMD指令等);
  • 編譯流程分析:輔助理解程序在編譯過程中的內存操作轉化(如Rust中某些數組操作可能被編譯為memcpy)。

總之,這款工具是LLVM生態在程序靜態分析領域的一個典型應用,展示了中間表示在連接編譯與開發優化中的關鍵作用。

Welcome to follow WeChat official account【程序猿編碼

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/92792.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/92792.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/92792.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 Conda 安裝 xinference[all](詳細版)

1. 安裝 Miniconda&#xff08;若未安裝&#xff09; Miniconda 是 Anaconda 的輕量版&#xff0c;僅包含 Conda 和 Python&#xff0c;適合服務器環境。 下載并安裝 Miniconda 下載地址&#xff1a;Index of /miniconda &#xff0c;可以自行選擇適合的版本 # 下載最新版 …

服務器登上去,顯示 failed to send WATCHDOG 重啟有效嗎?

文章目錄 概要整體架構流程技術名詞解釋技術細節小結 概要 當你登錄服務器時&#xff0c;看到類似以下提示&#xff1a; failed to send WATCHDOG: Resource temporarily unavailable這通常和系統的 systemd 服務有關&#xff0c;尤其是那些啟用了 watchdog&#xff08;看門…

重學React(五):脫圍機制一

背景&#xff1a; 之前將React的基礎知識以及狀態管理相關的知識都過了一遍&#xff0c;查漏補缺的同時對React也有了一些新鮮的認知&#xff0c;接下來這個模塊的名字很有意思&#xff1a;脫圍機制&#xff0c;內容也比之前的部分難理解一些。但整體看下來&#xff0c;理解之后…

去除Edge微軟瀏覽器與Chrome谷歌瀏覽器頂部出現“此版本的Windows不再支持升級Windows 10”的煩人提示

前言 在 Windows 7 中&#xff0c;安裝 Microsoft Edge 109 版本后&#xff0c;啟動瀏覽器時會彈出提示&#xff1a; 此版本的 Windows 不再支持 Microsoft Edge。升級到 Windows 10 或更高版本&#xff0c;以獲取常規功能和安全更新。 同樣地&#xff0c;安裝 Google Chrome 1…

PWM、脈沖

要求&#xff1a;一、PWM輸出PWM波生成原理在此處使用TIM2生成PWM&#xff0c;PA1輸出PWM波。CNT小于CCR時&#xff0c;輸出高電平&#xff1b;CNT大于CCR時&#xff0c;輸出低電平。 輸入捕獲測量頻率的原理輸入捕獲的捕獲意思是它在PWM波上升沿或者下降沿的時候&#xff0c;會…

文件IO(1)

.文件IO1.概念標準IO是有緩存的IO&#xff0c;文件IO沒有緩存&#xff0c;適合于通信、硬件設備操作標準IO是庫函數&#xff0c;文件IO是系統調用2.系統調用與庫函數系統調用&#xff1a;是Linux內核中的代碼&#xff0c;只能在Linux系統中使用庫函數&#xff1a;是對系統調用的…

【AI】Pycharm中要注意Python程序文件的位置

博主試著在本地電腦用Pycharm環境運行隨便一個機器學習然后做圖像識別的模型&#xff0c;Python的程序一直報博主學習圖片的路徑不正確&#xff0c;博主查了好幾遍&#xff0c;也沒找出問題&#xff0c;后來借助Deepseek才知道&#xff0c;Python主程序的位置一定要在Project下…

TDengine 可觀測性最佳實踐

TDengine 介紹 TDengine 是一款開源、高性能、云原生的時序數據庫&#xff0c;專為物聯網、車聯網、工業互聯網、金融、IT 運維等場景優化設計。它不僅提供了高效的數據存儲和查詢功能&#xff0c;還帶有內建的緩存、流式計算、數據訂閱等系統功能&#xff0c;能大幅減少系統設…

Jenkins 搭建鴻蒙打包

1、創建流水線工程 選擇 Freestyle project 2、配置模板倉庫、憑證 配置倉庫地址 創建憑證&#xff0c;憑證選擇賬號-密碼&#xff08;能夠訪問該倉庫的個人或管理員 Gitlab 賬密&#xff09; 到這里執行構建&#xff0c;便可以克隆倉庫到工作目錄 3、安裝插件 3.1 Rebuild…

【SpringBoot】02 基礎入門-什么是Spring Boot?:Spring與SpringBoot

文章目錄1、Spring能做什么1.1、Spring的能力1.2、Spring的生態1.3、Spring5重大升級1.3.1、響應式編程1.3.2、內部源碼設計2、為什么用SpringBoot2.1、SpringBoot優點2.2、SpringBoot缺點3、時代背景3.2、分布式分布式的困難分布式的解決3.3、云原生上云的困難4、如何學習Spri…

FFmpeg 編譯安裝和靜態安裝

FFmpeg 編譯安裝和靜態安裝 簡介 FFmpeg 是一個領先的多媒體框架&#xff0c;能夠解碼、編碼、轉碼、復用、解復用、流化、過濾和播放幾乎所有人類和機器創建的格式。本指南將詳細介紹如何在 CentOS 8.5.2111 系統上從源代碼編譯并安裝 FFmpeg 6.1.1 版本。從源代碼編譯安裝可…

人大BABEC地平線高效率具身導航!Aux-Think:探索視覺語言導航中數據高效的推理策略

作者&#xff1a; Shuo Wang1,3^{1,3}1,3, Yongcai Wang1^{1}1, Wanting Li1^{1}1 , Xudong Cai1^{1}1, Yucheng Wang3^{3}3, Maiyue Chen3^{3}3, Kaihui Wang3^{3}3, Zhizhong Su3^{3}3, Deying Li1^{1}1, Zhaoxin Fan2^{2}2單位&#xff1a;1^{1}1中國人民大學&#xff0c;2^…

01. maven的下載與配置

1.maven的下載與初步配置a.下載并配置倉庫地址下載maven壓縮包&#xff0c;并解壓&#xff0c;解壓后應有如下幾個文件點擊conf&#xff0c;打開settings.xml&#xff08;我用的VScode打開的&#xff09;&#xff0c;我們需要聲明一下內部倉庫的地址&#xff0c;以及私服的一些…

1701. 請輸出所有的3位對稱數

問題描述請輸出所有的 33 位對稱數&#xff0c;對稱數指的是一個整數 nn 正過來和倒過來是一樣的&#xff0c;比如&#xff1a;101、121、282…101、121、282…請從小到大輸出符合條件的3位對稱數&#xff0c;每行 11 個。輸入無。輸出從小到大按題意輸出符合條件的數&#xff…

C++算法·排序

排序的定義 這個不用說吧 就是根據某個條件對一個數列進行有序的操作 例如要求從小到大排序、從大到小排序等等 排序的分類 比較排序(Comparison(Comparison(Comparison Sorts)Sorts)Sorts) 特點&#xff1a;通過元素間的比較決定順序 時間復雜度下限&#xff1a;O(nO(nO(n…

微服務項目中的注冊中心——Nacos配置

從零開始&#xff1a;Nacos服務注冊與配置中心實戰教程 Nacos&#xff08;Dynamic Naming and Configuration Service&#xff09;是阿里巴巴開源的服務發現、配置管理工具&#xff0c;集注冊中心與配置中心于一體&#xff0c;廣泛應用于微服務架構。本文將從環境搭建到實戰配…

日期格式化成英文月,必須指定語言環境

如果不指定Locale.ENGLISH 在有些JDK下 輸出6月 INV USD 314,791.77,DUE 25-07 [PAID USD 503,389.56 ON 2025-07-16]Mar INV USD 52,042.00,DUE 25-07 [PAID USD 52,042.00 ON 2025-08-11]所以必…

【6】Transformers快速入門:Transformer 的注意力層 是啥?

一句話看懂注意力層作用&#xff1a;讓 AI 像人一樣 “抓重點” &#xff08;比如讀“貓追老鼠”&#xff0c;自動聚焦 “追” 這個動作&#xff0c;忽略無關詞&#xff09;1. 為什么需要注意力&#xff1f; 問題場景&#xff08;翻譯例子&#xff09;&#xff1a; 英文&#x…

集合,完整擴展

目錄 前言&#xff1a; 一、List接口 1.1 ArrayList 1.2 LinkedList 1.3 Vector 二、Set接口 2.1 HashSet 2.2 TreeSet 2.3 LinkedHashSet 三、應用選擇 前言&#xff1a; 本篇文章重點梳理 List 接口和 Set 接口的核心內容&#xff0c;結合代碼案例幫大家吃透它們的…

【doris基礎與進階】3-Doris安裝與部署

安裝前的準備 在windows系統上通過vmwareubuntu 22.04的方式進行安裝&#xff0c;由于資源有限&#xff0c;在同1臺機器上同時安裝fe和be&#xff08;broker本次不安裝&#xff0c;極簡化安裝&#xff09;&#xff0c;安裝版本為2.1.10&#xff0c;2.x版本架構不會有大的變化&a…