记一个编译问题的解决过程

问题描述

最近在学习caffe,先是在服务器(Ubuntu 14.04)上安装测试,BLAS使用的是atlas。Intel的parallel studio太大了(3.9G),来不及下载,暂时就没有使用。后来又在自己的笔记本上安装,这次使用intel MKL库,结果就在一个问题上折腾了好久。

问题的大致情况是,编译好了libcaffe.so,后续编译其他工具,链接这个so的时候,报undefined reference错误。

CXX/LD -o .build_release/tools/convert_imageset.bin
.build_release/lib/libcaffe.so:对‘cblas_ddot’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_daxpy’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_dasum’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_dcopy’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_sdot’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_dscal’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_sgemm’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_dgemm’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_sgemv’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_sscal’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_scopy’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_saxpy’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_dgemv’未定义的引用
.build_release/lib/libcaffe.so:对‘cblas_sasum’未定义的引用

问题解决过程

undefined reference算是一个经典错误。如果出现在源码编译的时候,则可能是没有正确include头文件。这里的错误出现在链接阶段,根据出错信息,问题应该是libcaffe.so缺少需要的定义。

  1. 首先找到出错的地方,使用make -n输出具体的编译命令。
echo CXX/LD -o .build_release/tools/upgrade_net_proto_text.bin
g++ .build_release/tools/upgrade_net_proto_text.o -o .build_release/tools/upgrade_net_proto_text.bin -pthread -fPIC -DNDEBUG -O2 -DUSE_CUDNN -I/usr/include/python2.7 -I/usr/lib/python2.7/dist-packages/numpy/core/include -I/usr/local/include -I/opt/opencv/include -I.build_release/src -I./src -I./include -I/usr/local/cuda/include -Wall -Wno-sign-compare -lcaffe -L/usr/lib -L/usr/local/lib -L/usr/lib -L/opt/opencv/lib -L/usr/local/cuda/lib64 -L/usr/local/cuda/lib -L.build_release/lib -lcudart -lcublas -lcurand -lglog -lgflags -lprotobuf -lleveldb -lsnappy -llmdb -lboost_system -lhdf5_hl -lhdf5 -lm -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_imgcodecs -lboost_thread -lstdc++ -lcudnn \
-Wl,-rpath,\$ORIGIN/../lib

命令有点长,不过细看一下也不难。找到了出错命令,我们就可以直接执行这条语句测试问题所在了。

  1. 另一方面,错误出在cblas_*,所以在caffe目录搜索用到这些函数的hpp/cpp文件
find . -name *.[hc]pp |xargs grep cblas

vi include/caffe/util/mkl_alternate.hpp
#ifdef USE_MKL

#include <mkl.h>

#else  // If use MKL, simply include the MKL header

extern "C" {
#include <cblas.h>
}

如果定义了宏USE_MKL,则包含头文件mkl.h。继续在Makefile中搜索 USE_MKL:

# BLAS configuration (default = ATLAS)
BLAS ?= atlas
ifeq ($(BLAS), mkl)
    # MKL
    LIBRARIES += mkl_rt
    COMMON_FLAGS += -DUSE_MKL
    MKLROOT ?= /opt/intel/mkl
    BLAS_INCLUDE ?= $(MKLROOT)/include
    BLAS_LIB ?= $(MKLROOT)/lib $(MKLROOT)/lib/intel64
else ifeq ($(BLAS), open)

Makefile写得一点问题没有。查看Intel MKL安装目录/opt/intel,权限没有问题,头文件也在,这里找不到问题。

  1. 再回到出错命令,仔细想想应该是so文件有问题,去除-lcaffe参数,出错信息就变了,基本确定是libcaffe.so有问题。

没办法,搜索“link error so undefined reference to”,这个页面的问题有点类似。

  1. 使用两个分析so库文件的命令,ldd分析依赖,nm列出symbol。

我发现编译出的libcaffe.so的依赖里竟然没有找到关键字blasintel,而symbol里确实有一大串出错函数。学习了下nm输出结果的Undefined symbol的含义,这些symbol应该由其他库来定义,而现在依赖都没有!

/opt/intel/mkl/lib/intel64目录下,nm -A *.so | grep cblas_ddot,找到了3个so,那么到底需要哪一个呢?好不容易搜索找到一个页面,列出mkl里各个so文件的用途,我们要用的应该是libmkl_rt.so。(其实应该搜索intel mkl user guide)

在出错命令相应位置加入-L/opt/intel/mkl/lib/intel64 -lmkl_rt,运行成功!

  1. 可是为什么mkl的相关参数没有在Makefile中正确生成呢?

不知道怎么调试makefile,只能一步步echo变量内容,最后定位到问题所在:
编辑配置文件Makefile.config,设置BLASvim编辑的时候,dw删除atlas,光标移动到空格位置,i插入,按空格,输入mklmkl后面多了一个空格,导致后续Makefile条件判断出错!这里应该按a插入...

这鸟说明的意思有点难以理解,还是实操体验一下吧……

问题总结

低级错误,虽然又多了一点经验,但太耗时。只希望遇到问题时,能够冷静分析,精确定位到问题,不然绕了好长的路发现是无用功。

另外,不知道有什么makefile的调试工具,跟踪每个变量的实际内容,我估计实时打印到标准输出应该就很不错了。

已有 4 条评论
  1. 问题总结的很到位

  2. 学习了,谢谢分享

  3. 很好的分享

  4. 文章阐释的很简洁,顶一下

添加新评论