文章目錄
- 前言
- 找到源代碼的必要性
- 涉及到的指令
- gdb怎樣找源代碼
-
- 源代碼目錄集合
- 源代碼檔案
- 目錄集合的預設值
- 檢視各種目錄
-
- 檢視源代碼檔案名和編譯目錄
- 檢視源代碼搜尋目錄
- 檢視目前目錄
- 具體示例
-
- 使用 dir 指令解決
- 使用 cd 指令解決
- 使用 set substitute-path 指令解決
- 總結
前言
通過
gdb
啟動程式,打好斷點運作,開始調試輸入
list
指令,結果發現找不到源代碼,是不是很糟心,讓我們來看看怎麼解決這種情況。
先來說明我們要處理的情況,調試程式找不到源代碼首先你得有源代碼,如果編譯完程式你把源代碼删了,或者單獨把執行程式拷貝到一個沒有源代碼的機器上,那麼拜拜吧您嘞,這種情況不是本文能解決的。
如果你确實有源代碼,正常編譯源代碼并且加入了
-g
選項,編譯完之後沒有改變源代碼位置,那麼調試的時候基本都會找到源代碼,是以這種情況也不在我們的讨論範圍之内。
分析到現在就剩下一種情況,程式編譯完成之後我移動了代碼的位置。實際工作中可能不會這麼無聊,故意改變目錄位置讓調試程式找不到,但是工作中常常會出現釋出機編譯完代碼要在開發機調試的情況,兩台機器上的代碼時一樣的,但是源代碼的位置可能放置的不同,那麼在個人開發機上調試這樣的程式就會找不到源代碼,這也就是我們要解決的問題。
找到源代碼的必要性
其實在我看來找不到源代碼的問題沒有那麼嚴重,編譯程式裡記錄了檔案名,行号等資訊,可以在調試的時候對照着本地的源代碼進行“盲調”,這種“盲調”的操作之前可沒少幹,因為線上環境中沒有源代碼,我隻能一邊對照着
gdb
調試輸出的行号,一邊對照本地的源代碼進行程式分析,通過這種方法也解決了不少問題。
雖然看着源代碼調試沒有那麼必要,但是如果可以看見那肯定是更好了,是以本文還是列舉出最常見的處理方法,解決一下本來有代碼,但因為目錄不比對無法正常調試的問題。
涉及到的指令
下面幾個指令是
gdb
指令,注意要放到和
gdb
互動指令行輸入才可以,别管會不會,先混個臉熟,以後要經常用的:
-
show dir
-
dir 目錄
-
set dir 目錄1:目錄2:目錄3
-
dir
-
pwd
-
cd 目錄
-
set substitute-path from-path to-path
gdb怎樣找源代碼
有時候很奇怪,代碼明明就在那裡,
gdb
你睜開眼睛行不行,為什麼你就是找不到呢?其實
gdb
也很苦的好不好,一直幫你查問題還要忍受着你每天的埋怨,到底是什麼原因導緻
gdb
對眼前的代碼視而不見呢?
其實
gdb
查找代碼也要遵循一定的規則,不能每次都全盤掃描吧,那不是得給它累死。舉個例子吧,我們在安裝一些軟體,特别是一些指令行工具的時候,總是有一步要求你把工具或軟體所在目錄添加到環境變量中,這個變量的名字叫做
Path
。
這個
Path
其實就是電腦上衆多軟體所在目錄的集合,當你直接使用軟體的程式時,會優先從
Path
這個集合中的目錄下去找,成功找到就會直接調用,否則提醒你軟體不存在。
源代碼目錄集合
而在
gdb
的調試過程中也有這樣一個目錄集合,我暫且稱它為
SourcePathSet
,後面就用這個名字了,因為還要涉及到多種查找目錄,請注意區分。
gdb
在查找源碼的時候首先在
SourcePathSet
中所包含的目錄下找,如果找不到就會提示查找失敗了,也就是這篇文章所提到的問題。
源代碼檔案
程式在編譯的過程中會記錄源檔案的名字和路徑,這個路徑可能是絕對路徑,比如
/mnt/d/main.cpp
,也可能是相對路徑
../main.cpp
,究竟是哪一種取決于編譯時使用的參數。
我們以絕對路徑為例,比如檔案名為
/mnt/d/main.cpp
,我們可以把它拆分成包含路徑和不包含路徑兩種形式:
/mnt/d/main.cpp
和
main.cpp
,當
SourcePathSet
中包含一個路徑叫
/mnt/e
時,
gdb
搜尋的路徑包括以下幾種:
-
/mnt/d/main.cpp
-
/mnt/e/mnt/d/main.cpp
-
/mnt/e/main.cpp
當源檔案是相對路徑
../main.cpp
的時候,那麼搜尋的路徑就變成了下面兩個:
-
/mnt/e/../main.cpp
-
/mnt/e/main.cpp
說到這裡你可能就明白了,當
gdb
找不到源檔案的時候,修改
SourcePathSet
就可以了,把想讓它搜尋的路徑添加到
SourcePathSet
,如果符合它的搜尋規則,那麼就可以找到了。
目錄集合的預設值
SourcePathSet
在
gdb
啟動後開始生效,預設值并不是空,而是
$cdir:$cwd
,這又是什麼鬼?其中的
$cdir
叫做編譯目錄,是代碼在編譯時記錄到程式中的,
$cwd
表示目前的調試目錄,可以通過
cd
指令來修改,要注意這個
cd
修改的是
gdb
會話中的目前目錄,不會影響啟動
gdb
前檔案系統中的目錄位置。
假設
$cdir
的值是
/usr
,
cwd
的值是
/home/albert
,我們又添加了
/mnt/e
到
SourcePathSet
中,那麼此時
SourcePathSet
的值為
/mnt/e:$cdir:$cwd
,如果源檔案的是
/mnt/d/main.cpp
,查找的目錄就會出現以下幾種:
-
/mnt/d/main.cpp
-
/mnt/e/mnt/d/main.cpp
-
/usr/mnt/d/main.cpp
-
/home/albert/mnt/d/main.cpp
-
/mnt/e/main.cpp
-
/usr/main.cpp
-
/home/albert/main.cpp
檢視各種目錄
先做一下準備工作,編寫一段簡單代碼,另存檔案名為
main.cpp
,儲存在目錄
/mnt/d/cpp
下:
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int b = 2;
int c = a + b;
cout << "c = " << c << endl;
return 0;
}
切換到目錄
/mnt/d
下, 檢視
cpp
目錄下檔案并使用
g++
編譯,編譯完成後将檔案
mian.cpp
移動到
/mnt
目錄下:
[email protected]:/mnt/d$ ls cpp/
main.cpp
[email protected]:/mnt/d$ g++ /mnt/d/cpp/main.cpp -g -o main
[email protected]:/mnt/d$ ls main
main
[email protected]:/mnt/d$ sudo mv cpp/main.cpp ../
[sudo] password for albert:
[email protected]:/mnt/d$ ls ../
c d e f main.cpp
啟動
gdb
調試程式并打好斷點,輸入
run
運作發現,斷點被觸發,但是顯示出
No such file or directory.
,說明沒有找到源代碼檔案。
[email protected]:/mnt/d$ gdb -q main
Reading symbols from main...done.
(gdb) b 8
Breakpoint 1 at 0x4008ac: file /mnt/d/cpp/main.cpp, line 8.
(gdb) run
Starting program: /mnt/d/main
Breakpoint 1, main () at /mnt/d/cpp/main.cpp:8
8 /mnt/d/cpp/main.cpp: No such file or directory.
檢視源代碼檔案名和編譯目錄
直接在
gdb
指令行中輸入
info source
回車就可以了
(gdb) info source
Current source file is /mnt/d/cpp/main.cpp
Compilation directory is /mnt/d
Source language is c++.
Producer is GNU C++ 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong.
Compiled with DWARF 2 debugging format.
Does not include preprocessor macro info.
(gdb)
通過這個指令發現,源代碼檔案是
/mnt/d/cpp/main.cpp
,編譯目錄是
/mnt/d
檢視源代碼搜尋目錄
在
gdb
環境下輸入
show dir
指令就可以顯示
SourcePathSet
這個集合中都有哪些目錄,由于還沒有設定過現在還是預設值
$cdir:$cwd
(gdb) show dir
Source directories searched: $cdir:$cwd
(gdb)
檢視目前目錄
檢視目前目錄就比較簡單了,直接
pwd
就搞定了
(gdb) pwd
Working directory /mnt/d.
(gdb)
我們“如願以償”的讓
gdb
找不到代碼了,從現在的環境來看,
$cdir
和
$cwd
相同都是
/mnt/d
,是以此時搜尋的目錄隻有:
-
/mnt/d/cpp/main.cpp
-
/mnt/d/mnt/d/cpp/main.cpp
-
/mnt/d/main.cpp
而代碼被我們移動到了
/mnt/main.cpp
,
gdb
自然就找不到了,後面來看看具體怎麼處理這種情況。
具體示例
說了這麼多原理的東西,如果弄明白了這些很容易找到解決問題的辦法,下面寫一個完整點的例子,來感受一些具體怎麼修複這個問題,建立三個檔案
mainpro.cpp
、
mymath.h
、
mymath.cpp
,目錄結構和内容如下:
[email protected]:/mnt/d$ tree /mnt/d/mainpro/
/mnt/d/mainpro/
|-- core
| `-- mainpro.cpp
`-- kit
|-- mymath.cpp
`-- mymath.h
//mainpro.cpp
#include "../kit/mymath.h"
#include <iostream>
using namespace std;
int main()
{
int a = 1, b = 2;
mymath* m = new mymath();
int c = m->add(a, b);
cout << "c = " << c << endl;
return 0;
}
//mymath.h
class mymath
{
public:
int add(int a, int b);
};
//mymath.cpp
#include "mymath.h"
int mymath::add(int a, int b)
{
int c = a + b;
return c;
}
在
/mnt/d/mainpro
目錄下編譯代碼,然後将代碼檔案所在目錄
core
和
kit
拷貝到
/mnt/e/newpro
目錄下,将可執行檔案拷貝到
/home/albert
目錄下。
[email protected]:/mnt/d/mainpro$ g++ /mnt/d/mainpro/core/mainpro.cpp /mnt/d/mainpro/kit/mymath.cpp -g -o mainpro
[email protected]:/mnt/d/mainpro$ tree
.
|-- core
| `-- mainpro.cpp
|-- kit
| |-- mymath.cpp
| `-- mymath.h
`-- mainpro
2 directories, 4 files
[email protected]:/mnt/d/mainpro$ mkdir /mnt/e/newpro
[email protected]:/mnt/d/mainpro$ sudo mv core/ /mnt/e/newpro/
[email protected]:/mnt/d/mainpro$ sudo mv kit/ /mnt/e/newpro/
[email protected]:/mnt/d/mainpro$ mv mainpro /home/albert/
在
/home/albert
目錄下啟動
gdb
開始調試,先在
main
函數打斷點,查詢源檔案路徑和編譯目錄等資訊;
[email protected]:~$ gdb -q mainpro
Reading symbols from mainpro...done.
(gdb) b main
Breakpoint 1 at 0x4008de: file /mnt/d/mainpro/core/mainpro.cpp, line 7.
(gdb) run
Starting program: /home/albert/mainpro
Breakpoint 1, main () at /mnt/d/mainpro/core/mainpro.cpp:7
7 /mnt/d/mainpro/core/mainpro.cpp: No such file or directory.
(gdb) info source
Current source file is /mnt/d/mainpro/core/mainpro.cpp
Compilation directory is /mnt/d/mainpro
Source language is c++.
Producer is GNU C++ 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong.
Compiled with DWARF 2 debugging format.
Does not include preprocessor macro info.
(gdb) list
2 in /mnt/d/mainpro/core/mainpro.cpp
(gdb) pwd
Working directory /home/albert.
(gdb) show dir
Source directories searched: $cdir:$cwd
(gdb)
果然找不到源代碼了,從上面的調試資訊來看,可以得到以下資訊:
- 源代碼檔案為
/mnt/d/mainpro/core/mainpro.cpp
- 程式編譯目錄為
/mnt/d/mainpro
- 目前目錄為
/home/albert
而源代碼查找清單中隻有
$cdir:$cwd
,說明隻包含
/mnt/d/mainpro
和
/home/albert
,那麼查找的目錄有:
-
/mnt/d/mainpro/core/mainpro.cpp
-
/mnt/d/mainpro/mnt/d/mainpro/core/mainpro.cpp
-
/home/albert/mnt/d/mainpro/core/mainpro.cpp
-
/mnt/d/mainpro/mainpro.cpp
-
/home/albert/mainpro.cpp
這些目錄顯然找不到源代碼檔案了,因為檔案已經被我移動到
/mnt/e/newpro/
目錄下了,也就是
/mnt/e/newpro/core/mainpro.cpp
,下面來嘗試一些解決方法。
使用 dir 指令解決
剛才說了源代碼查找集合
SourcePathSet
中隻有
$cdir:$cwd
,我們可以自己加一個嘛,比如像下面這樣:
(gdb) dir /mnt/e/newpro/core/
Source directories searched: /mnt/e/newpro/core:$cdir:$cwd
(gdb) list
2 #include <iostream>
3 using namespace std;
4
5 int main()
6 {
7 int a = 1, b = 2;
8 mymath* m = new mymath();
9
10 int c = m->add(a, b);
11 cout << "c = " << c << endl;
(gdb)
這樣就可以找到了,我們接着在
add
函數上下個斷點,繼續執行
(gdb) b mymath::add
Breakpoint 2 at 0x4009a6: file /mnt/d/mainpro/kit/mymath.cpp, line 6.
(gdb) c
Continuing.
Breakpoint 2, mymath::add (this=0x613c20, a=1, b=2) at /mnt/d/mainpro/kit/mymath.cpp:6
6 /mnt/d/mainpro/kit/mymath.cpp: No such file or directory.
(gdb) list
1 in /mnt/d/mainpro/kit/mymath.cpp
(gdb) info source
Current source file is /mnt/d/mainpro/kit/mymath.cpp
Source language is c++.
Producer is GNU C++ 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong.
Compiled with DWARF 2 debugging format.
Does not include preprocessor macro info.
(gdb)
結果發現又找不到檔案
/mnt/d/mainpro/kit/mymath.cpp
了,因為和之前不是一個檔案,這個檔案在其他的目錄下,是以還要使用
dir
指令,把新的目錄加到源代碼查找集合
SourcePathSet
中:
(gdb) dir /mnt/e/newpro/kit/
Source directories searched: /mnt/e/newpro/kit:/mnt/e/newpro/core:$cdir:$cwd
(gdb) list
1 #include "../kit/mymath.h"
2 #include <iostream>
3 using namespace std;
4
5 int main()
6 {
7 int a = 1, b = 2;
8 mymath* m = new mymath();
9
10 int c = m->add(a, b);
(gdb)
這次又能成功找到了,可是如果有好多個檔案要調試,難道要把所有的目錄都加進去嗎?其實可以有簡便方法的,在啟動
gdb
的時候可以指定搜尋的源代碼路徑,這些路徑都會被加到到源代碼查找集合
SourcePathSet
中,具體操作如下,先退出
gdb
,然後重新加參數啟動如下:
[email protected]:~$ gdb -q mainpro `find /mnt/e/newpro/ -type d -printf '-d %p '`
Reading symbols from mainpro...done.
(gdb) show dir
Source directories searched: /mnt/e/newpro/kit:/mnt/e/newpro/core:/mnt/e/newpro:$cdir:$cwd
(gdb)
其實這條指令的本來面目是
gdb -q mainpro -d xxxxx
,隻不過這組合了
find
指令以後使用起來更加友善了,可以把指定目錄下的子目錄全都添加到參數中
使用 cd 指令解決
如果是臨時調試倒是用不到上面設定啟動參數那麼麻煩,因為變量
$cwd
也在搜尋集合中,既然在編譯時記錄的源檔案被改變了位置,那麼我們調整我們的目前位置,讓代碼出現搜尋路徑中,還是上面的這個例子:
[email protected]:~$ pwd
/home/albert
[email protected]:~$ gdb -q mainpro
Reading symbols from mainpro...done.
(gdb) b main
Breakpoint 1 at 0x4008de: file /mnt/d/mainpro/core/mainpro.cpp, line 7.
(gdb) r
Starting program: /home/albert/mainpro
Breakpoint 1, main () at /mnt/d/mainpro/core/mainpro.cpp:7
7 /mnt/d/mainpro/core/mainpro.cpp: No such file or directory.
(gdb) list
2 in /mnt/d/mainpro/core/mainpro.cpp
(gdb) cd /mnt/e/newpro/core/
Working directory /mnt/e/newpro/core.
(gdb) list
2 #include <iostream>
3 using namespace std;
4
5 int main()
6 {
7 int a = 1, b = 2;
8 mymath* m = new mymath();
9
10 int c = m->add(a, b);
11 cout << "c = " << c << endl;
(gdb)
上面的操作通過
cd /mnt/e/newpro/core/
指令直接進入了源代碼目錄,當然就找到了,但是這還是會有點問題,當碰到需要調試好幾個檔案的時候就需要使用
cd
指令跳來跳去,要想一勞永逸,請看下面這個方法。
使用 set substitute-path 指令解決
我們移動源代碼的時候往往會整個目錄移動,或者說開發機和釋出機上面的代碼檔案組織結構是一樣,隻是所在的磁盤位置是不一樣的,是以如果可以設定用一個路徑替換原代碼檔案的路徑就好了,
set substitute-path from-path to-path
這個指令就可以達到想要的目的,這個指令還可以簡寫成
set substitute from-path to-path
,比如還是前面的例子,源代碼從
/mnt/d/mainrpo
目錄整體移動到了
/mnt/e/newpro
目錄,調試時找不到源代碼可以使用
set substitute /mnt/d/mainrpo /mnt/e/newpro
指令來指定替換目錄,這樣就可以找到源代碼啦,下面來測試一下:
[email protected]:~$ gdb -q mainpro
Reading symbols from mainpro...done.
(gdb) set substitute-path /mnt/d/mainrpo /mnt/e/newpro
(gdb) b main
Breakpoint 1 at 0x4008de: file /mnt/d/mainpro/core/mainpro.cpp, line 7.
(gdb) run
Starting program: /home/albert/mainpro
Breakpoint 1, main () at /mnt/d/mainpro/core/mainpro.cpp:7
7 /mnt/d/mainpro/core/mainpro.cpp: No such file or directory.
(gdb) cd /mnt/e/newpro/
Working directory /mnt/e/newpro.
(gdb) list
2 /mnt/d/mainpro/core/mainpro.cpp: No such file or directory.
(gdb) set substitute-path /mnt/d/mainpro /mnt/e/newpro
(gdb) list 0
1 #include "../kit/mymath.h"
2 #include <iostream>
3 using namespace std;
4
5 int main()
6 {
7 int a = 1, b = 2;
8 mymath* m = new mymath();
9
10 int c = m->add(a, b);
(gdb) info source
Current source file is /mnt/d/mainpro/core/mainpro.cpp
Compilation directory is /mnt/d/mainpro
Located in /mnt/e/newpro/core/mainpro.cpp
Contains 14 lines.
Source language is c++.
Producer is GNU C++ 5.4.0 20160609 -mtune=generic -march=x86-64 -g -fstack-protector-strong.
Compiled with DWARF 2 debugging format.
Does not include preprocessor macro info.
(gdb) pwd
Working directory /home/albert.
(gdb)
通過調試資訊
Located in /mnt/e/newpro/core/mainpro.cpp
可以看到,果然在新的位置找到了源代碼。
總結
- 調試的時候找不到源碼有多種解決方法,需要根據實際情況選擇最合适的解決方案。
- 編譯時使用絕對路徑時,推薦使用
的方式。set substitute-path from-path to-path
- 編譯時使用相對路徑時,使用
或者set substitute from-path to-path
都可以。dir new-path
- 對于臨時查找一個問題,單獨調試某一個檔案時使用
指令就可以搞定了。cd
- 直接在
環境輸入gdb
指令回車确認,可以重置dir
或者dir 目錄
指令修改過的源代碼搜尋目錄集合。set dir 目錄
==>> 反爬連結,請勿點選,原地爆炸,概不負責!<<==
當人的才華不足以撐起個人的欲望時就會感到焦慮,當面對不利的情況和事件卻又無力改變時就會感到憤怒,而弱肉強食一直都是生活的本質,惟有強大才是解決這一切負面情緒的良藥~