使用GDB除錯Apollo單元測試程式
阿新 • • 發佈:2019-02-08
說明:本文修改後的程式碼已上傳到GitHub
網站Apollo專案
中。
Apollo使用Google Test框架撰寫單元測試用例。如果某個測試用例通不過,僅藉助日誌資訊,很難弄清楚詳細的失敗原因。本文介紹使用GDB除錯單元測試用例的方法,可供各位同學撰寫單元測試程式碼提供一定的幫助。
一、撰寫單元測試用例
以RelativeMap中的單元測試用例進行說明。
首先在資料夾modules/map/relative_map/testdata/multi_lane_map
中增加如下測試資料檔案:chassis_info.pb.txt
、localization_info.pb.txt
、left.smoothed
middle.smoothed
、right.smoothed
。接下來增加單元測試檔案
modules/map/relative_map/navigation_lane_test.cc
,內部程式碼如下(涉及到的Google Test框架知識不作介紹,大家自行上網查詢):
#include "modules/map/relative_map/navigation_lane.h" #include <string> #include <vector> #include "gtest/gtest.h" #include "third_party/json/json.hpp" #include "modules/canbus/proto/chassis.pb.h" #include "modules/common/adapters/adapter_manager.h" #include "modules/common/vehicle_state/vehicle_state_provider.h" #include "modules/map/relative_map/common/relative_map_gflags.h" #include "modules/map/relative_map/proto/navigation.pb.h" #include "modules/map/relative_map/proto/relative_map_config.pb.h" namespace apollo { namespace relative_map { using apollo::common::VehicleStateProvider; using apollo::common::adapter::AdapterConfig; using apollo::common::adapter::AdapterManager; using apollo::common::adapter::AdapterManagerConfig; using apollo::relative_map::NavigationInfo; using apollo::relative_map::NavigationLane; using apollo::relative_map::NavigationPath; using nlohmann::json; namespace { bool GetNavigationPathFromFile(const std::string& filename, NavigationPath* navigation_path) { CHECK_NOTNULL(navigation_path); std::ifstream ifs(filename, std::ios::in); if (!ifs.is_open()) { AERROR << "Failed to open " << filename; return false; } std::string line_str; while (std::getline(ifs, line_str)) { try { auto json_obj = json::parse(line_str); auto* point = navigation_path->mutable_path()->add_path_point(); point->set_x(json_obj["x"]); point->set_y(json_obj["y"]); point->set_s(json_obj["s"]); point->set_theta(json_obj["theta"]); point->set_kappa(json_obj["kappa"]); point->set_dkappa(json_obj["dkappa"]); } catch (const std::exception& e) { AERROR << "Failed to parse JSON data: " << e.what(); return false; } } return true; } bool GenerateNavigationInfo( const std::vector<std::string>& navigation_line_filenames, NavigationInfo* navigation_info) { CHECK_NOTNULL(navigation_info); int i = 0; for (const std::string& filename : navigation_line_filenames) { auto* navigation_path = navigation_info->add_navigation_path(); if (!GetNavigationPathFromFile(filename, navigation_path)) { continue; } navigation_path->set_path_priority(i); navigation_path->mutable_path()->set_name("Navigation path " + i); ++i; } return navigation_info->navigation_path_size() > 0; } } // namespace class NavigationLaneTest : public testing::Test { public: virtual void SetUp() { common::adapter::AdapterManagerConfig adapter_conf; RelativeMapConfig config; EXPECT_TRUE(common::util::GetProtoFromFile( FLAGS_relative_map_adapter_config_filename, &adapter_conf)); EXPECT_TRUE(common::util::GetProtoFromFile( FLAGS_relative_map_config_filename, &config)); navigation_lane_.SetConfig(config.navigation_lane()); map_param_ = config.map_param(); navigation_lane_.SetDefaultWidth(map_param_.default_left_width(), map_param_.default_right_width()); data_file_dir_ = "modules/map/relative_map/testdata/multi_lane_map/"; localization::LocalizationEstimate localization; canbus::Chassis chassis; EXPECT_TRUE(common::util::GetProtoFromFile( data_file_dir_ + "localization_info.pb.txt", &localization)); EXPECT_TRUE(common::util::GetProtoFromFile( data_file_dir_ + "chassis_info.pb.txt", &chassis)); VehicleStateProvider::instance()->Update(localization, chassis); } protected: NavigationLane navigation_lane_; NavigationInfo navigation_info_; MapGenerationParam map_param_; std::vector<std::string> navigation_line_filenames_; std::string data_file_dir_; }; TEST_F(NavigationLaneTest, GenerateOneLaneMap) { navigation_line_filenames_.clear(); navigation_info_.Clear(); navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed"); EXPECT_TRUE( GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_)); navigation_lane_.UpdateNavigationInfo(navigation_info_); EXPECT_TRUE(navigation_lane_.GeneratePath()); EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0); MapMsg map_msg; EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg)); EXPECT_EQ(1, map_msg.hdmap().lane_size()); } TEST_F(NavigationLaneTest, GenerateTwoLaneMap) { navigation_line_filenames_.clear(); navigation_info_.Clear(); navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed"); navigation_line_filenames_.emplace_back(data_file_dir_ + "right.smoothed"); EXPECT_TRUE( GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_)); navigation_lane_.UpdateNavigationInfo(navigation_info_); EXPECT_TRUE(navigation_lane_.GeneratePath()); EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0); MapMsg map_msg; EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg)); EXPECT_EQ(2, map_msg.hdmap().lane_size()); } TEST_F(NavigationLaneTest, GenerateThreeLaneMap) { navigation_line_filenames_.clear(); navigation_info_.Clear(); navigation_line_filenames_.emplace_back(data_file_dir_ + "left.smoothed"); navigation_line_filenames_.emplace_back(data_file_dir_ + "middle.smoothed"); navigation_line_filenames_.emplace_back(data_file_dir_ + "right.smoothed"); EXPECT_TRUE( GenerateNavigationInfo(navigation_line_filenames_, &navigation_info_)); navigation_lane_.UpdateNavigationInfo(navigation_info_); EXPECT_TRUE(navigation_lane_.GeneratePath()); EXPECT_GT(navigation_lane_.Path().path().path_point_size(), 0); MapMsg map_msg; EXPECT_TRUE(navigation_lane_.CreateMap(map_param_, &map_msg)); EXPECT_EQ(3, map_msg.hdmap().lane_size()); } } // namespace relative_map } // namespace apollo
最後,在編譯檔案modules/map/relative_map/BUILD
中新增如下內容:
filegroup( name = "testdata", srcs = glob([ "testdata/**", ]), ) cc_test( name = "navigation_lane_test", size = "small", srcs = [ "navigation_lane_test.cc", ], data = [ ":testdata", ], deps = [ ":relative_map_lib", "//modules/common", "//third_party/json", "@gtest//:main", ], )
重新編譯整個Apollo專案。
二、使用GDB除錯測試用例
重新編譯Apollo專案後,會在bazel-bin/modules/map/relative_map/
目錄中生成可執行程式navigation_lane_test
,直接使用GDB在Docker中除錯該程式即可,具體操作步驟如下:
1. 啟動並進入Apollo專案的Docker
# 進入Apollo專案根目錄(我的路徑為:`~/code/apollo`,你需要修改為自己的路徑)
cd ~/code/apollo
# 啟動Apollo專案的Docker(注意:2.0以上版本在後面加上一個“-C”選項,
# 表示從中國伺服器拉取映象檔案,以加快下載速度)
bash docker/scripts/dev_start.sh
# 進入Docker
bash docker/scripts/dev_into.sh
2. 在Docker內部使用GDB除錯
gdb -q bazel-bin/modules/map/relative_map/navigation_lane_test
進入GDB除錯介面後,使用l
命令檢視原始碼,使用b 138
在原始碼第138行(可根據需要修改為自己所需的程式碼位置 )設定斷點,使用r
命令執行navigation_lane_test
程式,進入斷點暫停後,使用p navigation_lane_
檢視當前變數值(可根據需要修改為其他變數名),使用n
單步除錯一條語句,使用s
單步除錯進入函式內部,使用c
繼續執行後續程式。如果哪個部分測試通不過,除錯資訊會立刻告訴你具體原因,可使用bt
檢視當前呼叫堆疊。