oatpp-swagger
oatpp-swagger copied to clipboard
crash on exit
Not sure exactly what is going on to cause the crash when using the swagger controller but the symptom is memory corruption in the environment causing a SEGV when the ObjectMapper component gets cleaned up during shutdown.
This occurs when creating a swagger controller without first creating a Generator::Config component.
To get past the issue in our code, we've done a:
auto generator_config =
oatpp::base::Environment::Component<
std::shared_ptr<
oatpp::swagger::Generator::Config>>(
std::make_shared<oatpp::swagger::Generator::Config>());
prior to doing the oatpp::swagger::Controller::createShared(doc_endpoints)
.
I believe changing https://github.com/oatpp/oatpp-swagger/blob/d0495421914cc2f9ed0a4c625d215c3d140a339f/src/oatpp-swagger/Controller.hpp#L89 to something like:
generatorConfig =
oatpp::base::Environment::Component<std::shared_ptr<Generator::Config>>(
std::make_shared<Generator::Config>()).getObject();
will make the SEGV go away for all users.
That line gets called when no existing Generator::Config
component is found in the environment.
Hello @mheyman ,
This looks weird as Generator::Config
is not related to ObjectMapper
component by any means.
Also, after swagger document is generated(during controller construction) there is no need in Generator::Config
any more.
I believe the cause of the crash is something else, and the fix by creating a generator config component is most probably some kind of magical coincidence.
Can you please share your App.cpp
where you create components, and where you do application shutdown?
I agree that it is strange. Especially since the error occurs deep in stl and I've run with MSVC's stl debugging that should find the problem.
I ripped out a unit test I used to figure out the work around. It should be close to compilable - there may be missing include files at the top because I had to sanitize the source from the real source in order to put it here (I quickly made the dummy_controller
instead of using our "real" controller).
Also, there are object instances in the unit test that probably don't have to be components (they are not components in the real code and that code no longer crashes given the "fix"). They are still there as components in the unit test from the diagnosing phase...
Just below the // swagger_controller
comment is the line that, when comment it out, reliably gives me the crash at the end of the test.
#include <fmt/format.h>
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
#include <oatpp/network/ConnectionProvider.hpp>
#include <oatpp/network/Server.hpp>
#include <oatpp/network/tcp/server/ConnectionProvider.hpp>
#include <oatpp/parser/json/mapping/Deserializer.hpp>
#include <oatpp/parser/json/mapping/ObjectMapper.hpp>
#include <oatpp/parser/json/mapping/Serializer.hpp>
#include <oatpp/web/server/api/ApiController.hpp>
#include <oatpp/web/protocol/http/encoding/Chunked.hpp>
#include <oatpp-swagger/Controller.hpp>
#include <oatpp-swagger/Model.hpp>
#include <oatpp-swagger/Resources.hpp>
namespace web_service_base_test
{
// dummy_controller class to demo the swagger crash
#include "oatpp/core/macro/codegen.hpp"
#include OATPP_CODEGEN_BEGIN(ApiController) ///< Begin ApiController codegen section
class dummy_controller : public oatpp::web::server::api::ApiController {
public:
dummy_controller(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper) /* Inject object mapper */)
: oatpp::web::server::api::ApiController(objectMapper)
{}
ENDPOINT("GET", "/", root) {
return createResponse(Status::CODE_200, "Hello World!");
}
// TODO - more endpoints here
};
#include OATPP_CODEGEN_END(ApiController) ///< End ApiController codegen section
TEST(WebApp, OatppEnvironment)
{
static const std::string name(fmt::format("{}.{}", test_info_->test_suite_name(), test_info_->name()));
oatpp::base::Environment::init();
// swagger_component
auto const swagger_document_info =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::swagger::DocumentInfo>>(
oatpp::swagger::DocumentInfo::Builder()
.setTitle("title")
.setDescription("description")
.setVersion("version")
.setContactName("contact name")
.setContactUrl("contact url")
.setLicenseName("All Rights Reserved.")
.addServer("http://{localhost}:{45678}", "description")
.build());
auto const resources = oatpp::base::Environment::Component<std::shared_ptr<oatpp::swagger::Resources>>(
oatpp::swagger::Resources::loadResources("web_resources_dir"));
// serializer_config
auto serializer_config =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::parser::json::mapping::Serializer::Config>>(
oatpp::parser::json::mapping::Serializer::Config::createShared());
// deserializer_config
auto deserializer_config =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::parser::json::mapping::Deserializer::Config>>(
oatpp::parser::json::mapping::Deserializer::Config::createShared());
// server_connection_provider
auto server_connection_provider =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::network::ServerConnectionProvider>>(
std::make_shared<oatpp::network::tcp::server::ConnectionProvider>(
oatpp::network::Address{ "localhost", 56789, oatpp::network::Address::IP_4 }));
// http_router
auto http_router =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::web::server::HttpRouter>>(
oatpp::web::server::HttpRouter::createShared());
// server_connection_handler
auto server_connection_handler =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::network::ConnectionHandler>>(
std::make_shared<oatpp::web::server::HttpConnectionHandler>(http_router.getObject()));
// api_object_mapper
auto object_mapper =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::data::mapping::ObjectMapper>>(
oatpp::parser::json::mapping::ObjectMapper::createShared(
serializer_config.getObject(), deserializer_config.getObject()));
// user_controller
auto user_controller =
oatpp::base::Environment::Component<std::shared_ptr<dummy_controller>>(
std::make_shared<dummy_controller>());
// doc_endpoints
auto doc_endpoints =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::swagger::Controller::Endpoints>>(
oatpp::swagger::Controller::Endpoints::createShared());
std::shared_ptr<oatpp::collection::LinkedList<std::shared_ptr<oatpp::web::server::api::Endpoint>>> const endpoints =
user_controller.getObject()->getEndpoints();
doc_endpoints.getObject()->pushBackAll(endpoints);
// swagger_controller
auto generator_config =
oatpp::base::Environment::Component<
std::shared_ptr<
oatpp::swagger::Generator::Config>>(
std::make_shared<oatpp::swagger::Generator::Config>());
auto swagger_controller =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::swagger::Controller>>(
oatpp::swagger::Controller::createShared(doc_endpoints.getObject()));
swagger_controller.getObject()->addEndpointsToRouter(http_router.getObject());
// server
auto server = oatpp::network::Server::createShared(server_connection_provider.getObject(), server_connection_handler.getObject());
}
}
Hey @mheyman ,
I've played with the code, but I couldn't reproduce the exact crash.
The closest crash I managed to get is by disabling C++ exceptions with -fno-exceptions
.- In which case I have an exception thrown when (as you've said) commenting out the following line:
auto generator_config =
oatpp::base::Environment::Component<
std::shared_ptr<
oatpp::swagger::Generator::Config>>(
std::make_shared<oatpp::swagger::Generator::Config>());
Please note, I've removed the gtest
and fmt
, and reduced the code a bit - just to have less text before the eyes (hopefully I didn't remove the cause of the crash).
Systems I tried it on - Ubuntu 16.04
, Mac
, Windows (on Azure CI)
Here is the code:
#include <oatpp/network/ConnectionProvider.hpp>
#include <oatpp/network/Server.hpp>
#include <oatpp/network/tcp/server/ConnectionProvider.hpp>
#include <oatpp/parser/json/mapping/Deserializer.hpp>
#include <oatpp/parser/json/mapping/ObjectMapper.hpp>
#include <oatpp/parser/json/mapping/Serializer.hpp>
#include <oatpp/web/server/api/ApiController.hpp>
#include <oatpp/web/protocol/http/encoding/Chunked.hpp>
#include <oatpp/core/macro/codegen.hpp>
#include <oatpp-swagger/Controller.hpp>
#include <oatpp-swagger/Model.hpp>
#include <oatpp-swagger/Resources.hpp>
namespace web_service_base_test {
// dummy_controller class to demo the swagger crash
#include OATPP_CODEGEN_BEGIN(ApiController) ///< Begin ApiController codegen section
class dummy_controller : public oatpp::web::server::api::ApiController {
public:
dummy_controller(OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper) /* Inject object mapper */)
: oatpp::web::server::api::ApiController(objectMapper)
{}
ENDPOINT("GET", "/", root) {
return createResponse(Status::CODE_200, "Hello World!");
}
};
#include OATPP_CODEGEN_END(ApiController) ///< End ApiController codegen section
void test() {
auto swaggerDocInfo = oatpp::swagger::DocumentInfo::Builder()
.setTitle("title")
.setDescription("description")
.setVersion("version")
.setContactName("contact name")
.setContactUrl("contact url")
.setLicenseName("All Rights Reserved.")
.addServer("http://{localhost}:{45678}", "description")
.build();
auto const resources = oatpp::base::Environment::Component<std::shared_ptr<oatpp::swagger::Resources>>(
oatpp::swagger::Resources::loadResources(OATPP_SWAGGER_RES_PATH));
// server_connection_provider
auto server_connection_provider = std::make_shared<oatpp::network::tcp::server::ConnectionProvider>(
oatpp::network::Address{ "localhost", 56789, oatpp::network::Address::IP_4 });
// http_router
auto http_router = oatpp::web::server::HttpRouter::createShared();
// server_connection_handler
auto server_connection_handler = std::make_shared<oatpp::web::server::HttpConnectionHandler>(http_router);
// api_object_mapper
auto object_mapper =
oatpp::base::Environment::Component<
std::shared_ptr<oatpp::data::mapping::ObjectMapper>>(
oatpp::parser::json::mapping::ObjectMapper::createShared());
// user_controller
auto user_controller = std::make_shared<dummy_controller>();
// doc_endpoints
auto doc_endpoints = oatpp::swagger::Controller::Endpoints::createShared();
doc_endpoints->pushBackAll(user_controller->getEndpoints());
// swagger_controller
// auto generator_config =
// oatpp::base::Environment::Component<
// std::shared_ptr<
// oatpp::swagger::Generator::Config>>(
// std::make_shared<oatpp::swagger::Generator::Config>());
auto swagger_controller = oatpp::swagger::Controller::createShared(doc_endpoints, swaggerDocInfo);
swagger_controller->addEndpointsToRouter(http_router);
// server
auto server = oatpp::network::Server::createShared(server_connection_provider, server_connection_handler);
}
}
int main() {
// init env
oatpp::base::Environment::init();
// run test
web_service_base_test::test();
// Assert there is no leaking objects.
OATPP_ASSERT(oatpp::base::Environment::getObjectsCount() == 0);
// Destroy env. Should be called after all components are destroyed.
oatpp::base::Environment::destroy();
return 0;
}
Edit
This code is pushed to the dummy
branch of crud-example project (tests)
You may clone the code and run the tests.
To reproduce the crash - add the following to the CMakeLists.txt
if (MSVC)
target_compile_options(crud-test PUBLIC /EHs-c-)
else()
target_compile_options(crud-test PUBLIC -fno-exceptions)
endif()
Hey @mheyman ,
Do you have any updates on this issue?
Been too busy until this morning. Just reproduced the same behavior on a clean vcpkg, brand new CMake project with your source.
The MSVC command line:
cl.exe /TP -DFMT_LOCALE -DFMT_SHARED -DOATPP_SWAGGER_RES_PATH=\"C:/source/oatpp_crash/out/build/x64-Debug/vcpkg_installed/x64-windows/include/oatpp-1.2.0//bin/oatpp-swagger/res\" -Ivcpkg_installed\x64-windows\include -Ivcpkg_installed\x64-windows\include\oatpp-1.2.0\oatpp -Ivcpkg_installed\x64-windows\include\oatpp-1.2.0\oatpp-swagger /DWIN32 /D_WINDOWS /W3 /GR /EHsc /MDd /Zi /Ob0 /Od /RTC1 /showIncludes /Fooatpp_crash\CMakeFiles\oatpp_crash.dir\oatpp_crash.cpp.obj /Fdoatpp_crash\CMakeFiles\oatpp_crash.dir\ /FS -c ..\..\..\oatpp_crash\oatpp_crash.cpp
I cannot reproduce the error on linux.
It appears because Oat++ uses exceptions in some included types, I cannot compile with exceptions turned off.