drogon icon indicating copy to clipboard operation
drogon copied to clipboard

Link Error With Torch Library

Open mf523 opened this issue 2 years ago • 14 comments

Describe the bug I am trying to bind a simple example software with drogon as interface, and torch is involved as core ML function.

To Reproduce Steps to reproduce the behavior:

  1. create a default drogon controller
  2. add torch library in CMakeList.txt file
  3. cmake --build
  4. See error like below
/usr/bin/c++   -D_GLIBCXX_USE_CXX11_ABI=0 CMakeFiles/CppModelServ.dir/main.cc.o CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o -o CppModelServ  -Wl,-rpath,/usr/local/libtorch/lib:/usr/local/cuda/lib64/stubs:/usr/local/cuda/lib64 /usr/local/lib/libdrogon.a /usr/local/libtorch/lib/libtorch.so /usr/local/libtorch/lib/libc10.so /usr/local/libtorch/lib/libkineto.a /usr/local/cuda/lib64/stubs/libcuda.so /usr/local/cuda/lib64/libnvrtc.so /usr/local/cuda/lib64/libnvToolsExt.so /usr/local/cuda/lib64/libcudart.so /usr/local/libtorch/lib/libc10_cuda.so /usr/local/lib/libtrantor.a -ldl -lstdc++fs /usr/lib/x86_64-linux-gnu/libjsoncpp.so /usr/lib/x86_64-linux-gnu/libuuid.so /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libssl.so /usr/lib/x86_64-linux-gnu/libcrypto.so -lpthread -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cpu.so" -Wl,--as-needed -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch_cuda.so" -Wl,--as-needed /usr/local/libtorch/lib/libc10_cuda.so /usr/local/libtorch/lib/libc10.so /usr/local/cuda/lib64/libcufft.so /usr/local/cuda/lib64/libcurand.so /usr/local/cuda/lib64/libcublas.so /usr/lib/x86_64-linux-gnu/libcudnn.so -Wl,--no-as-needed,"/usr/local/libtorch/lib/libtorch.so" -Wl,--as-needed /usr/local/cuda/lib64/libnvToolsExt.so /usr/local/cuda/lib64/libcudart.so 
/usr/bin/ld: CMakeFiles/CppModelServ.dir/main.cc.o: in function `std::enable_if<std::is_default_constructible<drogon::plugin::AccessLogger>::value, void>::type drogon::DrObject<drogon::plugin::AccessLogger>::DrAllocator::registerClass<drogon::plugin::AccessLogger>()':
main.cc:(.text._ZN6drogon8DrObjectINS_6plugin12AccessLoggerEE11DrAllocator13registerClassIS2_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectINS_6plugin12AccessLoggerEE11DrAllocator13registerClassIS2_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/main.cc.o: in function `std::enable_if<std::is_default_constructible<drogon::plugin::SecureSSLRedirector>::value, void>::type drogon::DrObject<drogon::plugin::SecureSSLRedirector>::DrAllocator::registerClass<drogon::plugin::SecureSSLRedirector>()':
main.cc:(.text._ZN6drogon8DrObjectINS_6plugin19SecureSSLRedirectorEE11DrAllocator13registerClassIS2_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectINS_6plugin19SecureSSLRedirectorEE11DrAllocator13registerClassIS2_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/main.cc.o: in function `std::enable_if<std::is_default_constructible<drogon::NotFound>::value, void>::type drogon::DrObject<drogon::NotFound>::DrAllocator::registerClass<drogon::NotFound>()':
main.cc:(.text._ZN6drogon8DrObjectINS_8NotFoundEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectINS_8NotFoundEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/main.cc.o: in function `std::enable_if<std::is_default_constructible<drogon::LocalHostFilter>::value, void>::type drogon::DrObject<drogon::LocalHostFilter>::DrAllocator::registerClass<drogon::LocalHostFilter>()':
main.cc:(.text._ZN6drogon8DrObjectINS_15LocalHostFilterEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectINS_15LocalHostFilterEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/main.cc.o: in function `std::enable_if<std::is_default_constructible<drogon::IntranetIpFilter>::value, void>::type drogon::DrObject<drogon::IntranetIpFilter>::DrAllocator::registerClass<drogon::IntranetIpFilter>()':
main.cc:(.text._ZN6drogon8DrObjectINS_16IntranetIpFilterEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectINS_16IntranetIpFilterEE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o: in function `MmsCtrl::get(std::shared_ptr<drogon::HttpRequest> const&, std::function<void (std::shared_ptr<drogon::HttpResponse> const&)>&&, std::string) const':
MmsCtrl.cc:(.text+0x12a): undefined reference to `Json::Value::Value(std::string const&)'
/usr/bin/ld: MmsCtrl.cc:(.text+0x1ba): undefined reference to `Json::Value::Value(std::string const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o: in function `MmsCtrl::list(std::shared_ptr<drogon::HttpRequest> const&, std::function<void (std::shared_ptr<drogon::HttpResponse> const&)>&&) const':
MmsCtrl.cc:(.text+0x494): undefined reference to `Json::Value::Value(std::string const&)'
/usr/bin/ld: MmsCtrl.cc:(.text+0x55c): undefined reference to `Json::Value::Value(std::string const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o: in function `MmsCtrl::invoke(std::shared_ptr<drogon::HttpRequest> const&, std::function<void (std::shared_ptr<drogon::HttpResponse> const&)>&&, std::string) const':
MmsCtrl.cc:(.text+0xdc8): undefined reference to `drogon::utils::getUuid()'
/usr/bin/ld: MmsCtrl.cc:(.text+0xde1): undefined reference to `Json::Value::Value(std::string const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o: in function `std::enable_if<std::is_default_constructible<MmsCtrl>::value, void>::type drogon::DrObject<MmsCtrl>::DrAllocator::registerClass<MmsCtrl>()':
MmsCtrl.cc:(.text._ZN6drogon8DrObjectI7MmsCtrlE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv[_ZN6drogon8DrObjectI7MmsCtrlE11DrAllocator13registerClassIS1_EENSt9enable_ifIXsrSt24is_default_constructibleIT_E5valueEvE4typeEv]+0x46): undefined reference to `drogon::DrClassMap::registerClass(std::string const&, std::function<drogon::DrObjectBase* ()> const&)'
/usr/bin/ld: CMakeFiles/CppModelServ.dir/controllers/MmsCtrl.cc.o: in function `std::shared_ptr<MmsCtrl> drogon::DrClassMap::getSingleInstance<MmsCtrl>()':
MmsCtrl.cc:(.text._ZN6drogon10DrClassMap17getSingleInstanceI7MmsCtrlEESt10shared_ptrIT_Ev[_ZN6drogon10DrClassMap17getSingleInstanceI7MmsCtrlEESt10shared_ptrIT_Ev]+0x56): undefined reference to `drogon::DrClassMap::getSingleInstance(std::string const&)'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/CppModelServ.dir/build.make:136: CppModelServ] Error 1
make[2]: Leaving directory '/mnt/cpp-model-serv.git'
make[1]: *** [CMakeFiles/Makefile2:100: CMakeFiles/CppModelServ.dir/all] Error 2
make[1]: Leaving directory '/mnt/cpp-model-serv.git'
make: *** [Makefile:91: all] Error 2

Expected behavior build pass

Screenshots If applicable, add screenshots to help explain your problem.

Additional context Add any other context about the problem here.

mf523 avatar Aug 28 '21 12:08 mf523

CMakeFile.txt


project(CppModelServ CXX)
project(CppModelServ VERSION 1.0)

configure_file(CppModelServConfig.h.in CppModelServConfig.h)

include(CheckIncludeFileCXX)

check_include_file_cxx(any HAS_ANY)
check_include_file_cxx(string_view HAS_STRING_VIEW)
check_include_file_cxx(coroutine HAS_COROUTINE)

# specify the C++ standard
if (HAS_ANY AND HAS_STRING_VIEW AND HAS_COROUTINE)
    set(CMAKE_CXX_STANDARD 20)
elseif (HAS_ANY AND HAS_STRING_VIEW)
    set(CMAKE_CXX_STANDARD 17)
else ()
    set(CMAKE_CXX_STANDARD 14)
endif ()

set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)


add_executable(${PROJECT_NAME} main.cc)


# ##############################################################################
# If you include the drogon source code locally in your project, use this method
# to add drogon 
# add_subdirectory(drogon) 
# target_link_libraries(${PROJECT_NAME} PRIVATE drogon)
#
# and comment out the following lines
find_package(Drogon CONFIG REQUIRED)


# ##############################################################################


find_package(Torch REQUIRED)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${DROGON_CXX_FLAGS} ${TORCH_CXX_FLAGS}")
target_link_libraries(${PROJECT_NAME} "${DROGON_LIBRARIES}" "${TORCH_LIBRARIES}")


if (CMAKE_CXX_STANDARD LESS 17)
    # With C++14, use boost to support any, string_view and filesystem
    message(STATUS "use c++14")
    find_package(Boost 1.61.0 REQUIRED)
    target_link_libraries(${PROJECT_NAME} PUBLIC Boost::boost)
elseif (CMAKE_CXX_STANDARD LESS 20)
    message(STATUS "use c++17")
else ()
    message(STATUS "use c++20")
endif ()

aux_source_directory(controllers CTL_SRC)
aux_source_directory(filters FILTER_SRC)
aux_source_directory(plugins PLUGIN_SRC)
aux_source_directory(models MODEL_SRC)

drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
                    ${CMAKE_CURRENT_BINARY_DIR})
# use the following line to create views with namespaces.
# drogon_create_views(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/views
#                     ${CMAKE_CURRENT_BINARY_DIR} TRUE)

target_include_directories(${PROJECT_NAME}
                           PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
                                   ${CMAKE_CURRENT_SOURCE_DIR}/models)
target_sources(${PROJECT_NAME}
               PRIVATE
               ${SRC_DIR}
               ${CTL_SRC}
               ${FILTER_SRC}
               ${PLUGIN_SRC}
               ${MODEL_SRC})
# ##############################################################################
# uncomment the following line for dynamically loading views 
# set_property(TARGET ${PROJECT_NAME} PROPERTY ENABLE_EXPORTS ON)

# ##############################################################################

add_subdirectory(test)

mf523 avatar Aug 28 '21 12:08 mf523

@mf523 Thanks for the issue. I'm not familiar with PyTorch w/ C++. May you share a minimal reproducible example? (An example repository that reproduces the issue).

marty1885 avatar Aug 28 '21 13:08 marty1885

@marty1885 , thanks for quick reply. please follow this https://pytorch.org/cppdocs/installing.html it's good enough for a simple pytorch startup, then the rest are all about how to put drogon and torch together, really simple, no ML knowledge requried.

mf523 avatar Aug 28 '21 13:08 mf523

I did (hopefully) the exact same setup and it compiled normally. The only thing I've to change being instead of

target_link_libraries(${PROJECT_NAME} "${DROGON_LIBRARIES}" "${TORCH_LIBRARIES}")

I wrote:

target_link_libraries(${PROJECT_NAME} PRIVATE "${DROGON_LIBRARIES}" "${TORCH_LIBRARIES}")

But this is a CMake issue. It has nothing to do with the linker.

image

marty1885 avatar Aug 28 '21 13:08 marty1885

If you can, please share you code that is known to fail. Otherwise I'm not sure if it's my environment making it pass or my code.

marty1885 avatar Aug 28 '21 13:08 marty1885

#pragma once
#include "CppModelServConfig.h"

#include <drogon/HttpController.h>


using namespace drogon;

struct Model{
    std::string modelName;
    std::string modelUrl;
};


class MmsCtrl:public drogon::HttpController<MmsCtrl>
{
  public:
    METHOD_LIST_BEGIN
    //use METHOD_ADD to add your custom processing function here;
    ADD_METHOD_TO(MmsCtrl::get,"/models/{1:model_name}", Get);//path is /models/{model_name}
    ADD_METHOD_TO(MmsCtrl::list,"/models", Get);//path is /models
    ADD_METHOD_TO(MmsCtrl::load,"/models", Post);//path is /models
    ADD_METHOD_TO(MmsCtrl::unload,"/models/{1:model_name}", Delete);//path is /models/{model_name}
    ADD_METHOD_TO(MmsCtrl::invoke,"/models/{1:model_name}/invoke", Post);//path is /models/{model_name}/invoke
    //ADD_METHOD_TO(models::your_method_name,"/absolute/path/{1}/{2}/list",Get);//path is /absolute/path/{arg1}/{arg2}/list

    METHOD_LIST_END
    // your declaration of processing function maybe like this:
    void get(const HttpRequestPtr &, std::function<void (const HttpResponsePtr &)> &&callback, std::string p1) const;
    void list(const HttpRequestPtr &, std::function<void (const HttpResponsePtr &)> &&callback) const;
    void load(const HttpRequestPtr &, std::function<void (const HttpResponsePtr &)> &&callback, Model &&pNewModel);
    void unload(const HttpRequestPtr &, std::function<void (const HttpResponsePtr &)> &&callback, std::string p1) const;
    void invoke(const HttpRequestPtr &, std::function<void (const HttpResponsePtr &)> &&callback, std::string p1) const;
    // void your_method_name(const HttpRequestPtr& req,std::function<void (const HttpResponsePtr &)> &&callback,double p1,int p2) const;
};

mf523 avatar Aug 28 '21 14:08 mf523

#include "MmsCtrl.h"
#include <torch/torch.h>
#include <ATen/ATen.h>
....

void MmsCtrl::invoke(const HttpRequestPtr &req,
           std::function<void (const HttpResponsePtr &)> &&callback,
           std::string model_name) const
{
    LOG_DEBUG <<"invoke model: " << model_name;
    //Authentication algorithm, read database, verify identity, etc...
    //...
    at::Tensor a = at::ones({2, 2}, at::kInt);
    at::Tensor b = at::randn({2, 2});
    auto c = a + b.to(at::kInt);
    Json::Value ret;
    ret["hello"] = "world";
    ret["uuid"] = drogon::utils::getUuid();
    auto resp=HttpResponse::newHttpJsonResponse(ret);
    callback(resp);
}


// omitted code that has nothing to do with pytorch.


mf523 avatar Aug 28 '21 14:08 mf523

@marty1885 per your request, above is the code I am working one, other than that, everything else is standard drogon framework. the error comes only when I include pytorch lib in cmakefile, when I comment out pytorch in cmakefile and the controller, the error disappear.

mf523 avatar Aug 28 '21 14:08 mf523

Thanks! I can confirm I've no problem building the sample code with my setup (and executing it).

May you try changing

target_link_libraries(${PROJECT_NAME} "${DROGON_LIBRARIES}" "${TORCH_LIBRARIES}")

into

target_link_libraries(${PROJECT_NAME} PRIVATE "${DROGON_LIBRARIES}" "${TORCH_LIBRARIES}")

And the only other reason I can guess being that I'm not using an official build of LibTorch. I'm using one built by Arch Linux maintainers. So maybe that also have something to do with it.

marty1885 avatar Aug 28 '21 14:08 marty1885

I tried the PRIVATE key word, doesn't work, my os is ubuntu, can you advice your libtorch version? will try your way.

mf523 avatar Aug 28 '21 14:08 mf523

btw, just out of curiosity, how come libtorch version could cause linker complain undefined reference on drogon side? @marty1885

mf523 avatar Aug 28 '21 14:08 mf523

I don't know. That's why I asked for a minimal reproducible example as a repository. Maybe I did something that made it work that I don't know I'm doing. The only way to know definitely is to run the exact same code you are using. Here's my version https://github.com/marty1885/drogon-pytorch-poc. Hope if helps.

I'm using PyTorch 1.8.1. To be specific the python-pytorch-opt-cuda-1.8.1-5 Arch Linux Package.

marty1885 avatar Aug 29 '21 00:08 marty1885

I don't know how relevant this is to your problem @mf523, but I've recently run into a problem with #include order between Qt 6.1.2 and PyTorch 1.9.0. Placing #include <torch/torch.h> below Qts includes resulted in a weird syntax error from the compiler on correct code. While placing torch first, fixed the issue. I tried with 1.8.1 and 1.7.1, all had the same bug.

Can't hurt to try.

interfector18 avatar Aug 30 '21 23:08 interfector18

here's a working example which serve imagenet class inference https://github.com/SABER-labs/Drogon-torch-serve.

viig99 avatar Jan 15 '22 02:01 viig99