標籤:

如何讓編譯器在預處理時在一個項目中的某些指定文件中每一個函數定義前插入一個宏?

我想追蹤程序的函數調用過程,但是我並不想使用一些調試工具。我定義了一個宏,比如 #define TRACEFUNCTION std::cout &<&<__FUNCTION__ , 然後把這個宏添加到我所要調試的每一個函數實現的第一句話就可以實現這個功能。當然我可以手動完成這件事情,但是缺點是大批量的重複勞動。調試成功之後,雖然可以通過條件宏來關閉這個調試信息,但是宏調用的代碼留在函數實現文件裡面了,我並不想讓它們出現。

我的問題就是有沒有一個工具可以把我從手動插入宏的大批量重複勞動中解放出來。我希望這個工具具有以下基本功能:

1. 在指定的若干文件中的每一個函數實現第一句話插入一段宏。

2. 在1中,允許排除掉某些函數。因為一些函數我調試好了,就不需要看到這個宏了。再比如,我在main函數開始調用後需要做一些操作,以完成宏處理的準備工作。如果工具在main第一句話插入的話,宏準備工作沒做好,達不到預期效果。所以我需要排除main函數。


題主的直接問題我這裡就不展開回答了,知乎上肯定有不少其他同行知道怎麼做,如果沒有人來回答這方面我再回來更新。

一種解決思路是自己寫一個工具來實現題主這麼具體的需求。利用Clang來做source-to-source transformation是件直觀的事情,特別是只是在函數開頭插入一個特定調用這種簡單的需求。題主如果完全沒頭緒的話,可以參考這篇文章來起步:AST Instrumentation (examples by language)

@王濱 的回答提到了更方便的現成的工具,GCC的tracing功能,請參考他的回答~

我更想提到的東西是AspectC++。題主的需求其實就是AOP(Aspect-Oriented Programming)的最經典的場景之一——tracing。題主要安插的instrumentation代碼的位置是一種「before advice」。要是題主感興趣的話,在開發時用AspectC++從「外部」指定這些要調查的邏輯,然後要調查的事情都調查完了把AspectC++的部分扔掉,換回到用普通C++就是了。

也可以參考Wikipedia上的簡短介紹:AspectC++


我來回答 @RednaxelaFX 大大不屑回答的東西

只是 tracing 的話,gcc 有個參數叫 -finstrument-functions ,會在編譯時在所有函數中加入對一個特定函數的調用,具體 man 一下就可以了。看名字就知道了,就是給 tracing 用的。


我想說的是,手動添加也不是不可能!也未必有重複勞動!我用的是編輯器之神:Vim!

擅長做的事就是用「暴力」解決問題!


剛好最近自己碰到這個問題。。 隨手寫了一段代碼。 應該能用

std::vector& split(const std::string s, char delim, std::vector& elems) {
std::stringstream ss(s);
std::string item;
while (std::getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}

std::vector& split(const std::string s, char delim) {
std::vector& elems;
split(s, delim, elems);
return elems;
}

#define file "E:\user.cpp"
#define result "E:\newuser.cpp"

void f11(){

stringstream ss;

stringstream _path;

_path &<&< file; std::ifstream t(_path.str()); std::string str((std::istreambuf_iterator&(t)),
std::istreambuf_iterator&());

if (!str.size()) return;
vector& lines = split(str,"
");

stringstream result_text;

int q1 = 0;

bool isplzs = false;

for each (string line in lines)
{
result_text &<&< line &<&< " "; int _tmpq = 0; if ((_tmpq = line.find("/*")) != string::npos) { isplzs = true; } if ((_tmpq = line.find("*/")) != string::npos) { isplzs = false; } if (isplzs) continue; int maxsize = line.size(); if ((_tmpq=line.find("//")) != string::npos) { maxsize = _tmpq; } char* _begin = (char*)line.data(); for (int i = 0; i &< maxsize; i++) { char k = *(_begin + i); if (k == "{") { if (!q1) { result_text &<&< "hello world "; } q1++; } else if (k == "}") q1--; } } stringstream path1; path1 &<&< result; out.open(path1.str().c_str(), std::ios::app); out &<&< result_text.str(); out.flush(); out.close(); }


X86的話 PIN很好用


開放編譯器,有個叫open c++的,做代碼植入很強大,不知道還有沒有人維護


要是我做這事,就寫兩個python腳本,一個加標記,一個去標記,用正則表達式實現。正則不行就找個語法解析器找函數開頭的位置。


推薦閱讀:

C++ 03和C++11的vector在調用元素的構造函數時有什麼區別?
為什麼C++ 中 void * 能指向靜態成員函數但不能指非靜態向成員函數?
程序員對自己的語言有沒有感情的?
蘋果系統為什麼用Objective-C,而不用C++?
為什麼盡量不要使用using namespace std?

TAG:C編程語言 | C | CC |