在Linux系統(tǒng)中,若程序異常終止,操作系統(tǒng)會將程序當時的內(nèi)存狀態(tài)記錄下來,保存在一個文件中,這種行為叫做Core Dump(中文一般譯為“核心轉儲”)。實際上,除內(nèi)存信息之外,核心轉儲還會記錄程序的一些關鍵運行狀態(tài),例如寄存器信息(包括程序指針、棧指針等)、內(nèi)存管理信息等。核心轉儲對于程序員調試程序非常有益,因為有些程序錯誤是很難重現(xiàn)的,例如指針異常,而核心轉儲文件可以重現(xiàn)程序出錯時的情景。
1
Ubuntu 16.04系統(tǒng)中打開核心轉儲功能
Ubuntu 16.04系統(tǒng)默認關閉了核心轉儲功能,需重新設置打開。
1檢查核心轉儲是否打開
按快捷鍵“Ctrl+Alt+T”打開命令終端,輸入命令:
ulimit -c |
若輸出的結果為 0,則說明默認是關閉核心轉儲功能的,即當程序異常終止時,不會生成核心轉儲文件。
2在當前命令終端中打開核心轉儲
使用命令:
ulimit -c unlimited |
可開啟當前命令終端的核心轉儲功能,并且不限制核心轉儲文件大小; 若需限制文件大小,將unlimited修改為你所需文件的大小,注意單位為KB。
3永久打開核心轉儲
若想永久打開核心轉儲功能,則使用命令:
sudo vi /etc/security/limits.conf |
修改配置文件(我這里是使用vi編輯器修改,你可以換成自己熟悉的編輯器,但建議修改配置文件還是采用vi較好,因為它是所有Unix/Linux系統(tǒng)標配的編輯器,并且簡單的操作并不困難),增加如下所示的一行內(nèi)容:
#Each line describes a limit for a user in the form:## |
4配置核心轉儲文件名是否添加PID號
默認的核心轉儲文件名稱為core。通過修改/proc/sys/kernel/core_uses_pid文件可以讓生成的core文件名是否自動加上pid號。使用命令:
sudo vi /proc/sys/kernel/core_uses_pid |
將/proc/sys/kernel/core_uses_pid文件里的“0”修改為“1”,然后保存退出,這樣生成的core文件名將會變成core.pid,其中pid表示該進程的PID。
5配置核心轉儲文件的生成位置及文件名格式
默認的核心轉儲文件保存在可執(zhí)行文件所在的目錄下,可以通過修改/proc/sys/kernel/core_pattern文件來控制core文件的生成位置以及文件名格式。使用命令:
sudo vi /proc/sys/kernel/core_pattern |
可對core文件的生成位置以及文件名格式進行配置,以下是幾種配置示例:
# 示例1:將生成的core文件保存在/apollo/data/core目錄下,# 文件名格式:“core_進程名.進程PID”/apollo/data/core/core_%e.%p# 示例2:將生成的core文件保存在/tmp/core目錄下,# 文件名格式:“core_進程名_進程PID.時間戳”/tmp/core/core_%e_%p.%t# 示例3:這是Ubuntu默認的core文件生成方式。# “apport”是一個用python寫的腳本程序,# 其作用是在可執(zhí)行文件目錄下生成core文件,# %p %s %c %d %P分別表示: |
注意:以上示例只能使用其中一個,關于core文件的詳細命名格式,可以通過man core命令查看。
2
Ubuntu 16.04系統(tǒng)中調試核心轉儲文件的一個示例
1生成核心轉儲文件
首先撰寫一個C++測試程序,代碼如下:
#include |
Linux系統(tǒng)中使用GCC編譯器的編譯命令如下:
g++ -g -Wall -std=c++11 *.cpp -o test |
注意,上述命令一定要加“-g”選項,生成調試信息,否則后面使用GDB調試核心轉儲文件時,仍然無法定位程序崩潰點。
運行該程序:
./test |
輸出結果為:
段錯誤 (核心已轉儲) |
ls -l的結果如下(我使用示例3所示的core文件生成方式):
總用量 584-rw------- 1 davidhopper davidhopper 565248 Mar 19 17:49 core-rw-rw-r-- 1 davidhopper davidhopper 163 Mar 19 16:27 main.cpp-rwxrwxr-x 1 davidhopper davidhopper 25968 Mar 19 17:49 test |
可見,已經(jīng)在當前可執(zhí)行文件目錄中生成了一個核心轉儲文件:core
2使用GDB調試器調試core文件
借助GDB調試器,使用如下命令,可調試core文件:
gdb ./test core |
輸出信息如下:
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1Copyright (C) 2016 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later |
可見,GDB調試器根據(jù)生成的core文件,已經(jīng)找到了程序崩潰點為原程序的第8行??吹竭@里,是不是感覺到了GDB調試核心轉儲文件的威力?
3
Ubuntu 16.04系統(tǒng)中調試Apollo項目核心轉儲文件的方法
由于Apollo項目是在Docker中運行,因此不能直接在Ubuntu 16.04系統(tǒng)中直接生成核心轉儲文件并使用GDB對其進行調試,所有的工作必須在Docker中完成。具體操作步驟如下:
1啟動并進入Apollo項目的Docker
# 進入Apollo項目根目錄(我的路徑為:~/code/apollo,你需要修改為自己的路徑)cd ~/code/apollo# 啟動Apollo項目的Docker(注意:2.0以上版本在后面加上一個“-C”選項,# 表示從中國服務器拉取鏡像文件,以加快下載速度)bash docker/scripts/dev_start.sh# 進入Dockerbash docker/scripts/dev_into.sh |
2在Docker內(nèi)部檢查并設置核心轉儲功能
在Docker內(nèi)部,使用本文第一部分內(nèi)容檢查并設置核心轉儲功能。在我的機器上,使用ulimit -c命令檢查的結果為:unlimited,表明已打開核心轉儲功能。假如在你的Docker內(nèi)部發(fā)現(xiàn)未開啟核心轉儲功能,該怎么辦?那就按照本文第一部分內(nèi)容重新打開唄。
同樣在我的Docker內(nèi)部,使用命令:cat /proc/sys/kernel/core_pattern查看核心轉儲文件的生成位置及文件名格式,得到的結果為:/apollo/data/core/core_%e.%p,表明Docker內(nèi)部的核心轉儲文件被保存在/apollo/data/core目錄下,文件名格式:core_進程名.進程PID。當然,你也可以按照本文第一部分內(nèi)容對核心轉儲文件的保存位置及文件名格式進行定制。
3在Docker內(nèi)部調試各功能模塊生成的核心轉儲文件的方法
在Apollo項目Docker內(nèi)部,所有功能模塊的可執(zhí)行文件均被放置于/apollo/bazel-bin/modules。下面以規(guī)劃(Planning)模塊為例進行說明。
最近,我修改了規(guī)劃模塊內(nèi)部的RTKReplayPlanner類。在通過Dreamview調試規(guī)劃模塊時,經(jīng)常發(fā)現(xiàn)該模塊莫名其妙地退出,看日志文件沒有任何可用信息,根據(jù)我的編程經(jīng)驗,這一定是我在某處的指針使用存在問題,要么是引用了空指針,要么是指針越界,如此等等,不一而足。是時候讓核心轉儲文件發(fā)揮作用了。
我打開/apollo/data/core目錄,果然找到了規(guī)劃模塊崩潰時生成的核心轉儲文件:core_planning.695,于是立刻在Docker內(nèi)部(即使用bash docker/scripts/dev_into.sh命令進入Docker后的命令行終端內(nèi)操作)借助GDB調試該文件,命令如下所示。注意:若需定位程序崩潰位置,必須在構建Apollo項目時,添加調試信息。也就是說,構建命令不能使用“build_opt”或“build_opt_gpu”等優(yōu)化選項,而應使用“build”或“build_gpu”等帶調試信息的選項。
gdb /apollo/bazel-bin/modules/planning/planning /apollo/data/core/core_planning.695 |
調試結果如下:
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1Copyright (C) 2014 Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later |
上述結果表明,在bazel-out/local-dbg/genfiles/modules/common/proto/pnc_point.pb.h文件的1514行返回relative_time_時,this指針為空,即引用了一個空指針。顯然,這里只是錯誤暴露處,而非錯誤產(chǎn)生處。聯(lián)想到我修改了RTKReplayPlanner類,于是我立即在modules/planning/planner/rtk/rtk_replay_planner.cc中查找關鍵字:relative_time,找到相關代碼處(注意:下面的代碼是我修改后的內(nèi)容,并非Apollo項目原有代碼):
// reset relative time double zero_time = current_trajectory[matched_index].relative_time(); for (auto& trajectory_point : trajectory_points) { // davidhopper // We shoud add the "planning_init_point.relative_time()" to // maintain the correct time sequence. trajectory_point.set_relative_time(trajectory_point.relative_time() - zero_time + planning_init_point.relative_time()); |
最終結果水落石出,原來是double zero_time = current_trajectory[matched_index].relative_time();作了越界引用。找到了錯誤產(chǎn)生原因,代碼修改方法也就比較容易了,對matched_index的范圍作出限制即可解決問題。
自Apollo平臺開放已來,我們收到了大量開發(fā)者的咨詢和反饋,越來越多開發(fā)者基于Apollo擦出了更多的火花,并愿意將自己的成果貢獻出來,這充分體現(xiàn)了Apollo『貢獻越多,獲得越多』的開源精神。為此我們開設了『開發(fā)者說』板塊,希望開發(fā)者們能夠踴躍投稿,更好地為廣大自動駕駛開發(fā)者營造一個共享交流的平臺!
-
Linux
+關注
關注
87文章
11209瀏覽量
208721 -
Ubuntu
+關注
關注
5文章
559瀏覽量
29503 -
編輯器
+關注
關注
1文章
800瀏覽量
31055 -
Apollo
+關注
關注
5文章
340瀏覽量
18378
原文標題:開發(fā)者說 | 在Docker內(nèi)使用GDB調試Apollo項目的核心轉儲(Core Dump)文件
文章出處:【微信號:Apollo_Developers,微信公眾號:Apollo開發(fā)者社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論