< 返回版块

Matheritasiv 发表于 2023-02-17 00:18

我用最新的stable版本1.67.1和最新的nightly版本1.69.0编译一个rust项目hola,运行出现了段错误,但把工具链降级到1.66.1版本后编译出来又能正常运行。用gdb对比调试两个版本编译出来的程序,发现区别在于alloc::raw_vec::RawVec<f64, alloc::alloc::Global>::allocate_in<f64, alloc::alloc::Global>,新版本执行这个函数后内存中数组指针和长度的布局是反过来的,后续代码把长度当作指针用,然后导致段错误了。有没有有经验的大佬知道这是怎么回事?是程序的问题还是编译器的问题?

评论区

写评论
Bai-Jinlin 2023-02-17 16:56

这个问题有非常多的解决方法,都是改dlib-face-recognition里的new_from_vec函数。其实这个bug最好的解决方法是直接传vector的指针,然后循环的时候用索引就好了,比如这样。

    pub fn new_from_vec(vector: Vec<f64>) -> Self {
        let len = vector.len();
        let ptr=vector.as_ptr();
        let inner = unsafe {
            cpp!([ptr as "double*", len as "size_t"] -> FaceEncodingInner as "dlib::matrix<double,0,1>" {
                auto inner = dlib::matrix<double,0,1>(len);
                for (size_t i = 0; i < len; i++) {
                    inner(i) = ptr[i];
                }

                return inner;
            })
        };

        Self { inner }
    }

--
👇
Matheritasiv: 学到了,谢谢前辈!

--
👇
Bai-Jinlin: 像我下面写的那样改saanuregh/dlib-face-recognition库的代码,或者只能锁定rustc的版本依赖这个编译器版本特有的内存布局了,话说这个库都三年没新的提交了

--
👇
Matheritasiv: 那这个问题有没有不依赖编译器版本的解决方案?

--
👇
Bai-Jinlin: 想修复也简单,在new_from_vec的34行下面 https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L34插入代码。

let hack_vector=[vector.as_ptr() as _,vector.len(),vector.capacity()];

在把下面cpp宏的vector as "dlib::matrix<double,0,1>"里的vector改成hack_vector。说到底就是作者太偷懒了。

👇
Bai-Jinlin: 我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

作者 Matheritasiv 2023-02-17 16:47

学到了,谢谢前辈!

--
👇
Bai-Jinlin: 像我下面写的那样改saanuregh/dlib-face-recognition库的代码,或者只能锁定rustc的版本依赖这个编译器版本特有的内存布局了,话说这个库都三年没新的提交了

--
👇
Matheritasiv: 那这个问题有没有不依赖编译器版本的解决方案?

--
👇
Bai-Jinlin: 想修复也简单,在new_from_vec的34行下面 https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L34插入代码。

let hack_vector=[vector.as_ptr() as _,vector.len(),vector.capacity()];

在把下面cpp宏的vector as "dlib::matrix<double,0,1>"里的vector改成hack_vector。说到底就是作者太偷懒了。

👇
Bai-Jinlin: 我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

Bai-Jinlin 2023-02-17 16:39

像我下面写的那样改saanuregh/dlib-face-recognition库的代码,或者只能锁定rustc的版本依赖这个编译器版本特有的内存布局了,话说这个库都三年没新的提交了

--
👇
Matheritasiv: 那这个问题有没有不依赖编译器版本的解决方案?

--
👇
Bai-Jinlin: 想修复也简单,在new_from_vec的34行下面 https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L34插入代码。

let hack_vector=[vector.as_ptr() as _,vector.len(),vector.capacity()];

在把下面cpp宏的vector as "dlib::matrix<double,0,1>"里的vector改成hack_vector。说到底就是作者太偷懒了。

👇
Bai-Jinlin: 我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

作者 Matheritasiv 2023-02-17 16:30

那这个问题有没有不依赖编译器版本的解决方案?

--
👇
Bai-Jinlin: 想修复也简单,在new_from_vec的34行下面 https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L34插入代码。

let hack_vector=[vector.as_ptr() as _,vector.len(),vector.capacity()];

在把下面cpp宏的vector as "dlib::matrix<double,0,1>"里的vector改成hack_vector。说到底就是作者太偷懒了。

👇
Bai-Jinlin: 我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

Bai-Jinlin 2023-02-17 16:27

想修复也简单,在new_from_vec的34行下面 https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L34插入代码。

let hack_vector=[vector.as_ptr() as _,vector.len(),vector.capacity()];

在把下面cpp宏的vector as "dlib::matrix<double,0,1>"里的vector改成hack_vector。说到底就是作者太偷懒了。

👇
Bai-Jinlin: 我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

作者 Matheritasiv 2023-02-17 16:12

这是再往前x.data.clone()调用完成后的内存布局,可以看到rsi所指向的内存就是长度在前指针在后的,用老版本rust编译出来的程序在这里是指针在前长度在后。 screenshot1.png

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

Bai-Jinlin 2023-02-17 16:12

我试了下,在最新的nightly确实三个字段的顺序和以前不一致了,但是我认为这不是bug,只是那个程序的作者自己假定了三个字段的顺序导致的,作者假设vec的字段是addr,len,cap这么排布可以直接映射成cpp的matrix类型导致的问题,毕竟这种强制类型转换是unsafe。

--
👇
Matheritasiv: 不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

作者 Matheritasiv 2023-02-17 15:37

不是索引的问题,这是发生段错误处的代码: screenshot.png 第89行rbx是0,说明当前是在第一轮循环中,rcx理应是数组指针,但这里成了数组的长度128,根据第64行,它的值来自[r14],但[r14+0x8]处才是数组指针。

👇
Bai-Jinlin: 我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

Bai-Jinlin 2023-02-17 15:04

我看了下代码,我觉得原因大概是这样,FaceEncoding里的new_from_vec函数:https://github.com/saanuregh/dlib-face-recognition/blob/encoding-patch/src/face_encoding/encoding.rs#L33 的cpp宏里的循环用的是size_t。 但是dlib::matrix里的operator()操作符的参数是32位的long:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix.h#L1290。我认为是循环的时候发生了截断导致long为负数,在索引date出现的段错误。 而且这个代码很取巧的把vec直接cast成了matrix里的layout<T,num_rows,num_cols,mem_manager,3>字段的类型,里的指针字段正好可以对上号:https://github.com/davisking/dlib/blob/master/dlib/matrix/matrix_data_layout.h#L356。

作者 Matheritasiv 2023-02-17 11:40

安装后首先运行

sudo hola model add 0

录入面部数据,再运行

sudo hola model check

就会出现段错误,出错位置是src/app.rs的第200行:

198    pub fn identify(&mut self, encoding: FaceEncoding) -> bool {
199        self.models.iter().any(|x| {
200            let x = FaceEncoding::new_from_vec(x.data.clone());
201            let distance = encoding.distance(&x);
202            distance < self.config.video.certainty
203        })
204    }

向量拷贝后传到C++闭包的过程中。我在Cargo.toml里面用的cpp_build版本是0.5.7

--
👇
hax10: 我刚在Linux下使用cargo 1.67.1试了一下。编译通过了,程序也能正常运行起来。你cargo clean再cargo build一下试试?

hax10 2023-02-17 09:58

我刚在Linux下使用cargo 1.67.1试了一下。编译通过了,程序也能正常运行起来。你cargo clean再cargo build一下试试?

1 共 11 条评论, 1 页