当前位置: 首页 > news >正文

bug诞生记——动态库加载错乱导致程序执行异常

大纲

  • 背景
  • 问题发生
  • 问题猜测和分析过程
    • 是不是编译了本工程中的其他代码
    • 是不是有缓存
    • 是不是编译了非本工程的文件
    • 是不是调用了其他可执行文件
      • 查看CMakefiles
      • 分析源码
      • 检查正在运行程序的动态库
  • 解决方案

这个案例发生在我研究ROS 2的测试Demo时发生的。

整体现象是:修改了源码,编译也成功了,但是执行流程和没修改前一致,新代码的逻辑没有体现。

最后定位到“动态库加载错乱”这个根本的问题,方案也就呼之欲出。但是整个排查过程经历了若干假设和推导,还是值得记录下。

背景

在《Robot Operating System——Ubuntu上以二进制形式安装环境》这篇文章中,我们安装了二进制的ROS 2,并且通过下面的指令进行了测试

source /opt/ros/jazzy/setup.bash
ros2 run demo_nodes_cpp talker

在这里插入图片描述
后来为了研究它的一些源码,我从github上将demo_nodes_cpp的源码(https://github.com/ros2/demos/blob/rolling/demo_nodes_cpp)给下载到本地。执行编译后会生成build目录。在目录下会生成talker这类的可执行程序。然后我就用这些可执行程序进行编译结果测试。

问题发生

然后我看到demo_nodes_cpp/src/topics/talker_serialized_message.cpp源码时,有这么一段注释

        // We know the size of the data to be sent, and thus can pre-allocate the// necessary memory to hold all the data.// This is specifically interesting to do here, because this means// no dynamic memory allocation has to be done down the stack.// If we don't allocate enough memory, the serialized message will be// dynamically allocated before sending it to the wire.auto message_header_length = 8u;auto message_payload_length = static_cast<size_t>(string_msg->data.size());serialized_msg_.reserve(message_header_length + message_payload_length);

它表达的是:这段代码去掉,程序也可以正常运行。因为rclcpp::SerializedMessage的空间会根据内容而动态分配。

然后我就去掉了这段代码,并新增了一个printf。

        // We know the size of the data to be sent, and thus can pre-allocate the// necessary memory to hold all the data.// This is specifically interesting to do here, because this means// no dynamic memory allocation has to be done down the stack.// If we don't allocate enough memory, the serialized message will be// dynamically allocated before sending it to the wire.// auto message_header_length = 8u;// auto message_payload_length = static_cast<size_t>(string_msg->data.size());// serialized_msg_.reserve(message_header_length + message_payload_length);printf("serialized_msg_ allocate memory\n");

使用下面的指令编译后

colcon build --allow-overriding demo_nodes_cpp

在这里插入图片描述

再运行talker_serialized_message,发现“serialized_msg_ allocate memory”这句并没有输出。
在这里插入图片描述

问题猜测和分析过程

是不是编译了本工程中的其他代码

因为整个工程的编译模块我没细看,只能先盲猜一种最简单的原因,即:是不是编译了其他代码。

然后我搜索了上述输出中的关键字“serialized message”,发现源码文件中只有我修改的文件中才有。
在这里插入图片描述
这个猜测被排除!

是不是有缓存

我决定清掉build目录,重新执行编译。
中间也试过通过增加命令来在编译前清除缓存。

colcon build --cmake-clean-cache --cmake-clean-first --allow-overriding demo_nodes_cpp

很不幸,执行结果还是修改代码前的逻辑。
这个猜测排除!

是不是编译了非本工程的文件

这次测试比较暴力,直接将当前修改文件中printf的语法改错,看看编译是否报错。
在这里插入图片描述
报错了。

这个猜测排除!

将源文件还原成正确语法。

是不是调用了其他可执行文件

因为在《Robot Operating System——Ubuntu上以二进制形式安装环境》这篇文章中,我们使用安装的二进制文件,也运行成功了测试用例,所以怀疑通过源码编译的文件是不是在底层调用了之前通过二进制安装的另外一个环境的逻辑。

查看CMakefiles

在demo_nodes_cpp/build/demo_nodes_cpp/CMakeFiles目录下,有两个有关本例修改的目录。

  • talker_serialized_message_library.dir
  • talker_serialized_message.dir
    在这里插入图片描述
    通过名字可以看出来talker_serialized_message.dir对应于我们运行的可执行文件;talker_serialized_message_library.dir对应于某个库(是静态库还是动态库目前不明)。

我们将重点放在talker_serialized_message.dir上,因为我们运行的程序大概率就是通过它编译的。

在demo_nodes_cpp/build/demo_nodes_cpp/CMakeFiles/talker_serialized_message.dir/DependInfo.cmake文件中,我们看到一个比较陌生的文件node_main_talker_serialized_message.cpp

分析源码


# Consider dependencies only in project.
set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF)# The set of languages for which implicit dependencies are needed:
set(CMAKE_DEPENDS_LANGUAGES)# The set of dependency files which are needed:
set(CMAKE_DEPENDS_DEPENDENCY_FILES"/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp/rclcpp_components/node_main_talker_serialized_message.cpp" "CMakeFiles/talker_serialized_message.dir/rclcpp_components/node_main_talker_serialized_message.cpp.o" "gcc" "CMakeFiles/talker_serialized_message.dir/rclcpp_components/node_main_talker_serialized_message.cpp.o.d")# Targets to which this target links which contain Fortran sources.
set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES)# Targets to which this target links which contain Fortran sources.
set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES)# Fortran module output directory.
set(CMAKE_Fortran_TARGET_MODULE_DIR "")

打开这个文件,我们发现它实际调用了libtalker_serialized_message_library.so来实现了整体功能。
在这里插入图片描述
这是一个非常重要的发现。它可以让我们将排查的方向指向动态库。

检查正在运行程序的动态库

我们先让程序运行起来
在这里插入图片描述
然后在另外一个终端中查找这个进程ID

ps -ef | grep talker_serialized_message

在这里插入图片描述
然后使用lsof来查看这个进程加载的是哪个目录下的动态库libtalker_serialized_message_library.so。

lsof -p 64759 | grep "libtalker_serialized_message_library.so"

在这里插入图片描述
可以发现它调用的是“/opt/ros/jazzy/lib/libtalker_serialized_message_library.so”,而不是我们编译的结果所在的目录(/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp)。

这样就可以确定这个离奇的问题发生的原因了:

  • 可执行程序调用了动态库来完成逻辑。
  • 系统中有两份同名动态库。
  • 可执行程序使用了错误路径下得动态库。

解决方案

解决方案也很简单,我们通过export LD_LIBRARY_PATH来修改优先级。
首先我们看下当前环境下的加载优先级(执行了source /opt/ros/jazzy/setup.bash导致环境是面向二进制ROS 2的)

echo $LD_LIBRARY_PATH

/opt/ros/jazzy/opt/rviz_ogre_vendor/lib:/opt/ros/jazzy/lib/x86_64-linux-gnu:/opt/ros/jazzy/opt/gz_math_vendor/lib:/opt/ros/jazzy/opt/gz_utils_vendor/lib:/opt/ros/jazzy/opt/gz_cmake_vendor/lib:/opt/ros/jazzy/lib

可以看到二进制安装的ROS 2环境位于高优先级。

我们只要将我们的路径提前即可

export LD_LIBRARY_PATH=/home/fangliang/demos/demo_nodes_cpp/build/demo_nodes_cpp:$LD_LIBRARY_PATH

然后执行程序,我们就看到我们修改的代码生效了。
在这里插入图片描述

相关文章:

bug诞生记——动态库加载错乱导致程序执行异常

大纲 背景问题发生问题猜测和分析过程是不是编译了本工程中的其他代码是不是有缓存是不是编译了非本工程的文件是不是调用了其他可执行文件查看CMakefiles分析源码检查正在运行程序的动态库 解决方案 这个案例发生在我研究ROS 2的测试Demo时发生的。 整体现象是&#xff1a;修改…...

Matlab演示三维坐标系旋转

function showTwo3DCoordinateSystemsWithAngleDifference() clear all close all % 第一个三维坐标系 origin1 [0 0 0]; x_axis1 [1 0 0]; y_axis1 [0 1 0]; z_axis1 [0 0 1];% 绕 x 轴旋转 30 度的旋转矩阵 theta_x 30 * pi / 180; rotation_matrix_x [1 0 0; 0 cos(th…...

redis的持久化机制以及集群模式

1.redis的持久化机制 内存数据库具有高速读写的优势&#xff0c;但由于数据存储在内存中&#xff0c;一旦服务器停止或崩溃&#xff0c;所有数据将会丢失。持久化机制的引入旨在将内存中的数据持久化到磁盘上&#xff0c;从而在服务器重启后能够恢复数据&#xff0c;提供更好的…...

【论文解读】大模型算法发展

一、简要介绍 论文研究了自深度学习出现以来&#xff0c;预训练语言模型的算法的改进速度。使用Wikitext和Penn Treebank上超过200个语言模型评估的数据集(2012-2023年)&#xff0c;论文发现达到设定性能阈值所需的计算大约每8个月减半一次&#xff0c;95%置信区间约为5到14个月…...

WebApi配置Swagger、Serilog、NewtonsoftJson、Sqlsugar、依赖注入框架Autofac、MD5加密

文章目录 项目准备1、创建WebApi项目配置Swagger、Serilog、NewtonsoftJsonNewtonsoftJsonSwaggerSerilog 使用ORM框架SqlSugar创建Service类库构成MVC框架使用AutoFac进行依赖注入 创建用户登录接口添加用户时进行安全防护 项目准备 1、创建WebApi项目 配置Swagger、Serilog…...

【ffmpeg命令基础】视频选项讲解

文章目录 前言设置输出文件的帧数设置每秒播放的帧数设置输出视频的帧率示例1&#xff1a;更改输出视频的帧率示例2&#xff1a;将图像序列转换为视频 设置输入视频的帧率示例3&#xff1a;处理高帧率视频示例4&#xff1a;处理低帧率视频 同时设置输入和输出帧率示例5&#xf…...

使用uniapp开发小程序(基础篇)

本文章只介绍微信小程序的开发流程&#xff0c;如果需要了解其他平台的开发的流程的话&#xff0c;后续根据情况更新相应的文章,也可以根据uniapp官网的链接了解不同平台的开发流程 HBuilderX使用&#xff1a;https://uniapp.dcloud.net.cn/quickstart-hx.html 开发工具 开始…...

vue3【详解】组合式函数

什么是组合式函数&#xff1f; 利用 Vue 的组合式 API 来封装和复用有状态逻辑的函数&#xff0c;用于实现逻辑复用&#xff0c;类似 react18 中的 hook 函数名称 – 以 use 开头&#xff0c;采用驼峰命名&#xff0c;如 useTitle参数 – 建议使用 toValue() 处理&#xff08;…...

微服务实战系列之玩转Docker(六)

前言 刚进入大暑&#xff0c;“清凉不肯来&#xff0c;烈日不肯暮”&#xff0c;空调开到晚&#xff0c;还是满身汗。——碎碎念 我们知道&#xff0c;仓库可见于不同领域&#xff0c;比如粮食仓库、数据仓库。在容器领域&#xff0c;自然也有镜像仓库&#xff08;registry&…...

Python题解Leetcode Hot100之动态规划

动态规划解题步骤-5部曲 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 70. 爬楼梯 题目描述 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到…...

你了解GD32 MCU上下电要求吗

你了解GD32 MCU的上下电要求吗&#xff1f;MCU的上下电对于系统的稳定运行非常重要。 以GD32F30X为例&#xff0c;上电/掉电复位波形如如下图所示。 上电过程中&#xff0c;VDD/VDDA电压上电爬坡&#xff0c;当电压高于VPOR&#xff08;上电复位电压&#xff09;MCU开始启动&a…...

二、【Python】入门 - 【PyCharm】安装教程

往期博主文章分享文章&#xff1a; 【机器学习】专栏http://t.csdnimg.cn/sQBvw 目录 第一步&#xff1a;PyCharm下载 第二步&#xff1a;安装&#xff08;点击安装包打开下图页面&#xff09; 第三步&#xff1a;科学使用&#xff0c;请前往下载最新工具及教程&#xff1a…...

2、程序设计语言基础知识

这一章节的内容在我们的软件设计师考试当中&#xff0c;考的题型比较固定&#xff0c;基本都是选择题&#xff0c;分值大概在2~4分左右。 而且考的还多是程序设计语言的一些基本语法&#xff0c;特别是这两年比较火的Python。 所以对于有一定要编程基础的即使本章的内容不学习&…...

ARM/Linux嵌入式面经(十八):TP-Link联洲

文章目录 虚拟内存,页表,copy on write面试题1:面试题2:面试题3:进程和线程的区别红黑树和b+树的应用红黑树的应用B+树的应用视频会议用了哪些协议1. H.323协议2. SIP协议(会话发起协议)3. WebRTC(网页实时通信)4. 其他协议io多路复用(select,poll,epoll)面试题li…...

解读vue3源码-响应式篇2

提示&#xff1a;看到我 请让我滚去学习 文章目录 vue3源码剖析reactivereactive使用proxy代理一个对象1.首先我们会走isObject(target)判断&#xff0c;我们reactive全家桶仅对对象类型有效&#xff08;对象、数组和 Map、Set 这样的集合类型&#xff09;&#xff0c;而对 str…...

【测开能力提升-fastapi框架】fastapi能力提升 - 中间件与CORS

1. 中间件 1.1 介绍&#xff08;ChatGPT抄的&#xff0c;大致可以理解&#xff09; 一种机制&#xff0c;用于在处理请求和响应之前对其进行拦截、处理或修改。中间件可以在应用程序的请求处理管道中插入自定义逻辑&#xff0c;以实现一些通用的功能&#xff0c;如身份验证、…...

centos7安装es及简单使用

为了方便日后查看&#xff0c;简单记录下&#xff01; 【启动es前,需要调整这个配置文件(/opt/elasticsearch-6.3.0/config/elasticsearch.yml)的两处ip地址,同时访问页面地址的ip:9200时,ip地址也对应修改】 【启动kibana前,需要调整这个配置文件(/opt/kibana-6.3.0/config/k…...

2024年自动驾驶SLAM面试题及答案(更新中)

自动驾驶中的SLAM&#xff08;Simultaneous Localization and Mapping&#xff0c;即同步定位与地图构建&#xff09;是关键技术&#xff0c;它能够让车辆在未知环境中进行自主定位和地图建构。秋招来临之际&#xff0c;相信大家都已经在忙碌的准备当中了&#xff0c;尤其是应届…...

HTML零基础自学笔记(上)-7.18

HTML零基础自学笔记&#xff08;上&#xff09; 参考&#xff1a;pink老师一、HTML, Javascript, CSS的关系是什么?二、什么是HTML?1、网页&#xff0c;网站的概念2、THML的基本概念3、THML的骨架标签/基本结构标签 三、HTML标签1、THML标签介绍2、常用标签图像标签&#xff…...

数学建模--图论与最短路径

目录 图论与最短路径问题 最短路径问题定义 常用的最短路径算法 Dijkstra算法 Floyd算法 Bellman-Ford算法 SPFA算法 应用实例 结论 延伸 如何在实际应用中优化Dijkstra算法以提高效率&#xff1f; 数据结构优化&#xff1a; 边的优化&#xff1a; 并行计算&…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

在Ubuntu中设置开机自动运行(sudo)指令的指南

在Ubuntu系统中&#xff0c;有时需要在系统启动时自动执行某些命令&#xff0c;特别是需要 sudo权限的指令。为了实现这一功能&#xff0c;可以使用多种方法&#xff0c;包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法&#xff0c;并提供…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)

RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发&#xff0c;后来由Pivotal Software Inc.&#xff08;现为VMware子公司&#xff09;接管。RabbitMQ 是一个开源的消息代理和队列服务器&#xff0c;用 Erlang 语言编写。广泛应用于各种分布…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...