I have a class named GenericMessage shown in the 1st code snippet below (defined in GenericMessage.hxx).
I have a .cpp file named TestFE.cpp (see the 2nd code snippet below) that attempts to send an instance of class GenericMessage via a ZMQ queue (See also the 4th code snippet very below - ZmqHandler.hxx). TesfFE.cpp implements the ZMQ push pattern here by including ZmqHandler.hxx.
I have yet another .cpp file named TestBE.cpp (see the 3rd code snippet below) that receives so mentioned GenericMessage instance via the ZMQ queue. TestBE.cpp implements the ZMQ pull pattern here to retrive the GenericMessage instance over the ZMQ queue.
In the TestFE.cpp, I use the standard memcpy function in order to convert the GenericMessage object into a form that can be accepted by the ZMQ queue. On the line 21 of TestBE.cpp (marked in the 3rd code snippet in comments), I get a segmentation fault because it looks the memcpy does not work properly on the sender side which is TestFE.cpp. I got the below message when TestBE is executed. I am also providing the gdb backtrace just below. Could you please tell me what's wrong here? Why do you think memcpy cannot copy my GenericMessage object to ZMQ message_t format properly? Or do you think the problem is st else? Any comments would be appreciated.
ERROR MESSAGE
$ ./TestBE
Connecting to FE...
RECEIVED: 1
Segmentation fault (core dumped)
GDB Backtrace
(gdb) r
Starting program: /home/holb/HOLB_DESIGN/ZMQ/WORK1/TestBE
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
[New Thread 0xb7c84b40 (LWP 4252)]
[New Thread 0xb7483b40 (LWP 4253)]
Connecting to FE...
RECEIVED: 1
Program received signal SIGSEGV, Segmentation fault.
0xb7f371cc in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/i386-linux-gnu/libstdc++.so.6
(gdb) bt
#0 0xb7f371cc in std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::string const&) ()
from /usr/lib/i386-linux-gnu/libstdc++.so.6
#1 0x08049621 in GenericMessage<std::string>::getData (this=0xbffff06c)
at GenericMessage.hxx:18
#2 0x08049075 in main () at TestBE.cxx:21
(gdb)
CODE SNIPPET 1 (GenericMessage.hxx)
#include
#include
#include
template <class T>
class GenericMessage {
public:
GenericMessage(int id, T msg):
beId(id),
data(msg)
{}
~GenericMessage(){}
T getData()
{
//LINE 18 is the following line!
return data;
}
std::string toString()
{
std::ostringstream ss;
ss << getBeId();
std::string ret = ss.str();
return ret;
}
void setBeId(int id)
{
beId = id;
}
int getBeId()
{
return beId;
}
private:
int beId;
T data;
};
CODE SNIPPET 2 (TestFE.cxx ==> The sender)
#include "ZmqHandler.hxx"
//SEE THE 4th snippet at the bottom for the content of ZmqHandler.hxx
int main ()
{
ZmqHandler<std::string> zmqHandler;
int counter = 1;
while(1)
{
std::string data = "Hello there!\0";
GenericMessage<std::string> msg(counter, data);
zmqHandler.sendToBE(&msg);
counter++;
sleep(1);
}
return 0;
}
CODE SNIPPET 3 (TestBE.cxx ==> The receiver)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <string>
#include <iostream>
int main ()
{
// Prepare our context and socket
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_PULL);
std::cout << "Connecting to FE..." << std::endl;
socket.connect ("tcp://localhost:5555");
while(1){
zmq::message_t reply;
socket.recv (&reply);
GenericMessage<std::string> *msg = (GenericMessage<std::string>*)(reply.data());
std::cout << "RECEIVED: " << msg->toString() << std::endl;
/* ********************************* */
/* SEGMENTATION FAULT HAPPENS HERE */
/* The member "data" in class GenericMessage cannot be received while the member "id" in the previous line can be received. */
std::cout << "DATA: " << ((std::string)msg->getData()) << std::endl;
/* ********************************** */
}
return 0;
}
CODE SNIPPET 4 (ZMQHandler.hxx)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <pthread.h>
#include <unistd.h>
#include <cassert>
template <class T>
class ZmqHandler {
public:
ZmqHandler():
mContext(1),
mOutbHandlerSocket(mContext, ZMQ_PUSH)
{
mOutbHandlerSocket.bind ("tcp://*:5555");
}
~ZmqHandler() {}
void *sendToBE(GenericMessage<T> *theMsg)
{
// Place the new request to the zmq queue for BE consumption
zmq::message_t msgToSend(sizeof(*theMsg));
memcpy ( msgToSend.data(), ((GenericMessage<T>*)theMsg), sizeof(* ((GenericMessage<T>*)theMsg)));
mOutbHandlerSocket.send(msgToSend);
std::cout << "SENT request: [" << theMsg->toString() << "]" << std::endl;
return (NULL);
}
private:
zmq::context_t mContext;
zmq::socket_t mOutbHandlerSocket;
};
I'm beginning to see the problem. It's that you send the complete "structure", which contains a member variable that have pointers (the std::string). This you can not do, as pointers are only valid in the program that created them.
You have to serialize the structure before sending it, and then de-serialize on the receiving end.
You can use libraries such as Boost serialization for this, or Google protocol buffers, or any other number of libraries.
Related
I'm trying to restart the program when segmention fault occures.
I have following minimal reproducible code:-
#include <csignal>
#include <unistd.h>
#include <iostream>
int app();
void ouch(int sig) {
std::cout << "got signal " << sig << std::endl;
exit(app());
}
struct L { int l; };
static int i = 0;
int app() {
L *l= nullptr;
while(1) {
std::cout << ++i << std::endl;
sleep(1);
std::cout << l->l << std::endl; //crash
std::cout << "Ok" << std::endl;
}
}
int main() {
struct sigaction act;
act.sa_handler = ouch;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGKILL, &act, 0);
sigaction(SIGSEGV, &act, 0);
return app();
}
It successfully catches sigsegv first time but after it prints 2, it shows me segmentation fault (core dumped)
1
got signal 11
2
zsh: segmentation fault (core dumped) ./a.out
tested with clang 12.0.1 and gcc 11.1.0 on ArchLinux
Is this operating system specific behavior or is something wrong in my code
The problem is that when you restart the program by calling exit(app()) from inside ouch(), you are still technically inside the signal handler. The signal handler is blocked until you return from it. Since you never return, you therefore cannot catch a second SIGSEGV.
If you got a SIGSEGV, then something really bad has happened, and there is no guarantee that you can just "restart" the process by calling app() again.
The best solution to handle this is to have another program start your program, and restart it if it crashed. See this ServerFault question for some suggestions of how to handle this.
I am trying to parse a simple JSON string using Boost.PropertyTree in my C/C++ application.
{"header":{"version":42,"source":1,"destination":2},"coffee":"colombian"}
Here is how I've set it up in my C/C++ multi-threaded application (manually defining the JSON string to demonstrate the issue).
ParseJson.cpp
#ifdef __cplusplus
extern "C"
{
#endif
#include "ParseJson.hpp"
#ifdef __cplusplus
}
#endif
#include <iostream>
#include <sstream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
extern "C" MyStruct * const parseJsonMessage(char * jsonMessage, unsigned int const messageLength) {
MyStruct * myStruct = new MyStruct();
// Create empty property tree object.
ptree tree;
if (myStruct != nullptr) {
try {
// Create an istringstream from the JSON message.
std::string jsonMessageString("{\"header\":{\"version\":42,\"source\":1,\"destination\":2},\"coffee\":\"colombian\"}"); // doesn't work
std::istringstream isStreamJson(jsonMessageString);
// Parse the JSON into the property tree.
std::cout << "Reading JSON ..." << jsonMessageString << "...";
read_json(isStreamJson, tree);
std::cout << " Done!" << std::endl;
// Get the values from the property tree.
printf("version: %d\n", tree.get<int>("header.version"));
printf("source: %d\n", tree.get<int>("header.source"));
printf("coffee: %s\n", tree.get<std::string>("coffee").c_str());
}
catch (boost::property_tree::ptree_bad_path badPathException) {
std::cout << "Exception caught for bad path: " << badPathException.what() << std::endl;
return nullptr;
}
catch (boost::property_tree::ptree_bad_data badDataException) {
std::cout << "Exception caught for bad data: " << badDataException.what() << std::endl;
return nullptr;
}
catch (std::exception exception) {
std::cout << "Exception caught when parsing message into Boost.Property tree: " << exception.what() << std::endl;
return nullptr;
}
}
return myStruct;
}
The read_json() call appears to complete but the get() calls to retrieve the parsed data from the property tree fail:
Reading JSON ...{"header":{"version":42,"source":1,"destination":2},"coffee":"colombian"}... Done!
Exception caught for bad path: No such node (header.version)
I am using Boost 1.53 on RHEL 7 (compiler is gcc/g++ version 4.8.5), and I've tried both suggestions mentioned in this post related to Boost.PropertyTree and multi-threading. I've defined the BOOST_SPIRIT_THREADSAFE compile definition globally for the project. I also tried the atomic swap solution suggested for that post. Neither of these had any effect on the symptoms.
Oddly enough, I can use the other public methods for Boost.Property tree to grab the values manually:
std::cout << "front.key: " << tree.front().first << std::endl;
std::cout << "front.front.key: " << tree.front().second.front().first << std::endl;
std::cout << "front.front.value: " << tree.front().second.front().second.get_value_optional<std::string>() << std::endl;
which shows the JSON was actually parsed:
front.key: header
front.front.key: version
front.front.value: 42
Note, I had to use std::string to grab the header.version value, as trying to use get_value_optional<int>() crashes also.
However, this manual approach is not scalable; my application needs to accept several, more complicated JSON structures.
When I tried more complicated JSON strings, these were also successfully parsed, but accessing values using the get() methods similarly failed, this time crashing the program. Here is one of the GDB backtraces I pulled from the crashes, but I wasn't familiar enough with Boost to get anything useful from this:
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7fffebfff700 (LWP 7176)]
0x00007ffff5aa8200 in std::locale::locale(std::locale const&) () from /lib64/libstdc++.so.6
Missing separate debuginfos, use: debuginfo-install boost-system-1.53.0-28.el7.x86_64 boost-thread-1.53.0-28.el7.x86_64 bzip2-libs-1.0.6-13.el7.x86_64 elfutils-libelf-0.176-5.el7.x86_64 elfutils-libs-0.176-5.el7.x86_64 glibc-2.17-292.el7.x86_64 keyutils-libs-1.5.8-3.el7.x86_64 krb5-libs-1.15.1-37.el7_7.2.x86_64 libattr-2.4.46-13.el7.x86_64 libcap-2.22-10.el7.x86_64 libcom_err-1.42.9-16.el7.x86_64 libgcc-4.8.5-39.el7.x86_64 libselinux-2.5-14.1.el7.x86_64 libstdc++-4.8.5-39.el7.x86_64 openssl-libs-1.0.2k-19.el7.x86_64 pcre-8.32-17.el7.x86_64 systemd-libs-219-67.el7_7.2.x86_64 xz-libs-5.2.2-1.el7.x86_64 zlib-1.2.7-18.el7.x86_64
(gdb) bt
#0 0x00007ffff5aa8200 in std::locale::locale(std::locale const&) () from /lib64/libstdc++.so.6
#1 0x00007ffff5ab6051 in std::basic_ios<char, std::char_traits<char> >::imbue(std::locale const&) () from /lib64/libstdc++.so.6
#2 0x000000000041e322 in boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int>::get_value(std::string const&) ()
#3 0x000000000041c5b2 in boost::optional<int> boost::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value_optional<int, boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int> >(boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int>) const ()
#4 0x000000000041aa61 in boost::enable_if<boost::property_tree::detail::is_translator<boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int> >, int>::type boost::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<int, boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int> >(boost::property_tree::stream_translator<char, std::char_traits<char>, std::allocator<char>, int>) const ()
#5 0x000000000041985d in int boost::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get_value<int>() const ()
#6 0x0000000000418673 in int boost::property_tree::basic_ptree<std::string, std::string, std::less<std::string> >::get<int>(boost::property_tree::string_path<std::string, boost::property_tree::id_translator<std::string> > const&) const ()
#7 0x0000000000414f4a in parseJsonMessage ()
#8 0x000000000040d8cd in ProcessThread () at ../../src/Processing.c:906
#9 0x00007ffff7bc6ea5 in start_thread () from /lib64/libpthread.so.0
#10 0x00007ffff55538cd in clone () from /lib64/libc.so.6
FWIW, I tried putting this code into a simple (single-threaded) main.cpp:
#include <iostream>
#include <sstream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
int main(int numArgs, char * const * const args) {
ptree tree;
try {
// Create an istringstream from the JSON message.
std::string jsonMessageString("{\"header\":{\"version\":42,\"source\":1,\"destination\":2},\"coffee\":\"colombian\"}");
std::istringstream isStreamJson(jsonMessageString);
// Parse the JSON into the property tree.
std::cout << "Reading JSON..." << jsonMessageString << "...";
read_json(isStreamJson, tree);
std::cout << " Done!" << std::endl;
// Print what we parsed.
std::cout << "version: " << tree.get<int>("header.version") << std::endl;
std::cout << "source: " << tree.get<int>("header.source") << std::endl;
std::cout << "coffee: " << tree.get<std::string>("coffee") << std::endl;
}
catch (boost::property_tree::ptree_bad_path badPathException) {
std::cout << "Exception caught for bad path: " << badPathException.what() << std::endl;
return -1;
}
catch (boost::property_tree::ptree_bad_data badDataException) {
std::cout << "Exception caught for bad data: " << badDataException.what() << std::endl;
return -1;
}
catch (std::exception exception) {
std::cout << "Exception caught when parsing message into Boost.Property tree: " << exception.what() << std::endl;
return -1;
}
std::cout << "Program completed!" << std::endl;
return 0;
}
This code works just fine:
bash-4.2$ g++ -std=c++11 main.cpp -o main.exe
bash-4.2$ ./main.exe
Reading JSON...{"header":{"version":42,"source":1,"destination":2},"coffee":"colombian"}... Done!
version: 42
source: 1
coffee: colombian
Program completed!
So, why won't the Boost.PropertyTree get() methods work for a multi-threaded application? Could the fact that the multi-threaded application is a mix of C and C++ code be causing an issue? I see that my specific compiler version (GCC 4.8.5) hasn't been explicitly verified with this Boost library... Could this be a compiler issue? Or is the Boost 1.53 version buggy?
An update based on provided answer:
Admittedly, my original code for the parseJsonMessage method was messy (a product of dozens of debugging iterations and ripping out code that wasn't relevant to the problem). A more condensed version that is free of distractions (and possible red herrings) is the following:
#ifdef __cplusplus
extern "C"
{
#endif
#include "DirectIpRev3.hpp"
#ifdef __cplusplus
}
#endif
#include <iostream>
#include <sstream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
extern "C" void parseJsonMessage2() {
// Create empty property tree object.
ptree tree;
std::string jsonMessageString("{\"header\":{\"version\":42,\"source\":1,\"destination\":2},\"coffee\":\"colombian\"}"); //doesn't work
std::istringstream isStreamJson(jsonMessageString);
try {
read_json(isStreamJson, tree);
std::cout << tree.get<int>("header.version") << std::endl;
std::cout << tree.get<int>("header.source") << std::endl;
std::cout << tree.get<std::string>("coffee") << std::endl;
}
catch (boost::property_tree::ptree_bad_path const & badPathException) {
std::cerr << "Exception caught for bad path: " << badPathException.what() << std::endl;
}
catch (boost::property_tree::ptree_bad_data const & badDataException) {
std::cerr << "Exception caught for bad data: " << badDataException.what() << std::endl;
}
catch (std::exception const & exception) {
std::cerr << "Exception caught when parsing message into Boost.Property tree: " << exception.what() << std::endl;
}
}
Running this condensed function in my multi-threaded program yields an exception:
Exception caught when parsing message into Boost.Property tree: <unspecified file>(1): expected object or array
Without the exception handling, it prints a bit more info:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::property_tree::json_parser::json_parser_error> >'
what(): <unspecified file>(1): expected object or array
I'm still not too sure what might be causing the failure here, but leaning towards using nlohmann as suggested.
Please, don't use Property Tree to "parse" "JSON". See nlohmann or Boost.JSON
Further
you're using raw new and delete apparently without good reason
You have unused parameters
you're catching polymorphic exceptions by value
you're leaking memory on any exception and returning null pointers when there are allocation errors
Combining these, I'm 99% certain that your crashes are caused by something else: Undefined Behaviour has a tendency to show up elsewhere after memory corruption (e.g. stack thrashing or use-after-delete, out-of-bounds etc.).
Using My Crystal Ball
A guess: you didn't show, but the struct probably looks like
typedef struct MyStructT {
int version;
int source;
char const* coffee;
} MyStruct;
A naive mistake would be to assign coffee the same way you print it:
myStruct->coffee = tree.get<std::string>("coffee").c_str();
The "obvious"(?) problem here is that c_str() points to memory owned by the value node, and transitively by the ptree. When the function returns that pointer is stale. OOPS. UB
You are allocating the struct with new (even though it is likely POD because of the extern "C", so it gives you a false sense of security, since all the members have indeterminate values anyways).
Another naive mistake would be to pass that C code that de-allocates with ::free (like it does with everything malloc-ed, right). That's another potential source of UB.
If you had "nailed" the first idea e.g. with strdup you might run into problems with more leaked memory. Even if you use delete myStruct correctly (or start using malloc instead), you will have to remember to ::free the string allocated with strdup.
Your API is typical C-style (which is probably on purpose) but leaves the door wide open to passing the wrong messageLength causing out-of-bounds read. The chance of this happening is raised due to the observation that you're not even using the arguments in your own example code above.
MULTI-THREADED STRESS TEST
Here's a multi-threaded stress test live on Coliru. It does 1000 iterations on 25 threads.
Live On Coliru
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct MyStructT {
int version;
int source;
char* coffee;
} MyStruct;
//#include "ParseJson.hpp"
#ifdef __cplusplus
}
#endif
#include <iostream>
#include <sstream>
#include <string>
#define BOOST_BIND_GLOBAL_PLACEHOLDERS
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
using boost::property_tree::ptree;
using boost::property_tree::read_json;
using boost::property_tree::write_json;
extern "C" MyStruct* parseJsonMessage(char const* jsonMessage, unsigned int const messageLength) {
auto myStruct = std::make_unique<MyStruct>(); // make it exception safe
// Create empty property tree object.
ptree tree;
if (myStruct != nullptr) {
try {
// Create an istringstream from the JSON message.
std::istringstream isStreamJson(std::string(jsonMessage, messageLength));
// Parse the JSON into the property tree.
//std::cout << "Reading JSON ..." << isStreamJson.str() << "...";
read_json(isStreamJson, tree);
//std::cout << " Done!" << std::endl;
// Get the values from the property tree.
myStruct->version = tree.get<int>("header.version");
myStruct->source = tree.get<int>("header.source");
myStruct->coffee = ::strdup(tree.get<std::string>("coffee").c_str());
return myStruct.release();
}
catch (boost::property_tree::ptree_bad_path const& badPathException) {
std::cerr << "Exception caught for bad path: " << badPathException.what() << std::endl;
}
catch (boost::property_tree::ptree_bad_data const& badDataException) {
std::cerr << "Exception caught for bad data: " << badDataException.what() << std::endl;
}
catch (std::exception const& exception) {
std::cerr << "Exception caught when parsing message into Boost.Property tree: " << exception.what() << std::endl;
}
}
return nullptr;
}
#include <cstdlib>
#include <string>
#include <thread>
#include <list>
int main() {
static std::string_view msg = R"({"header":{"version":42,"source":1,"destination":2},"coffee":"colombian"})";
auto task = [] {
for (auto i = 1000; --i;) {
auto s = parseJsonMessage(msg.data(), msg.size());
::printf("version: %d\n", s->version);
::printf("source: %d\n", s->source);
::printf("coffee: %s\n", s->coffee);
::free(s->coffee);
delete s; // not ::free!
}
};
std::list<std::thread> pool;
for (int i = 0; i < 25; ++i)
pool.emplace_back(task);
for (auto& t : pool)
t.join();
}
The output (sorted and uniq-ed):
24975 coffee: colombian
24975 source: 1
24975 version: 42
How can I do async write and read using websockets from the Beast library? I have tried to adapted the synchronous write/read example provided in the Beast documentation here, but the code below does not behave as expected.
I expected the following output :
*launch application*
Written data ...
Received data : Hello world!
*Ctrl-C*
Closing application ...
I got this :
*launch application*
*Ctrl-C*
Closing application ...
Code :
#include <beast/core/to_string.hpp>
#include <beast/websocket.hpp>
#include <boost/asio.hpp>
#include <iostream>
#include <string>
/// Block until SIGINT or SIGTERM is received.
void sig_wait(beast::websocket::stream<boost::asio::ip::tcp::socket&>& ws)
{
boost::asio::io_service ios;
boost::asio::signal_set signals(ios, SIGINT, SIGTERM);
signals.async_wait(
[&](boost::system::error_code const&, int)
{
ws.close(beast::websocket::close_code::normal);
std::cout << "Closing application ..." << std::endl;
});
ios.run();
}
int main(int argc, char *argv[])
{
// Normal boost::asio setup
std::string const host = "echo.websocket.org";
boost::asio::io_service ios;
boost::asio::ip::tcp::resolver r{ios};
boost::asio::ip::tcp::socket sock{ios};
boost::asio::ip::tcp::resolver::iterator iter (r.resolve(boost::asio::ip::tcp::resolver::query{host, "80"}));
boost::asio::connect(sock,iter);
// WebSocket connect and send message
beast::websocket::stream<boost::asio::ip::tcp::socket&> ws{sock};
ws.handshake(host, "/");
ws.async_write(boost::asio::buffer(std::string("Hello world!")),
[&](beast::error_code const&)
{
std::cout << "Written data ..." << '\n';
}
);
// Register handle for async_read
beast::streambuf sb;
beast::websocket::opcode op;
ws.async_read(op,sb,
[&](beast::error_code const&)
{
std::cout << "Received data : " << to_string(sb.data()) << '\n';
}
);
sig_wait(ws);
}
Side note: I am fairly new to the Boost library in general, so I may have gotten some of the basics wrong ...
You must call io_service::run(), that's the blocking call that will animate the io_service.
There are now asynchronous WebSocket client examples that you can study or copy: http://www.boost.org/doc/libs/develop/libs/beast/doc/html/beast/examples.html
Here is an asynchronous client example which calls io_service::run() from main:
http://www.boost.org/doc/libs/develop/libs/beast/example/websocket/client/async/websocket_client_async.cpp
This is a follow up problem that I opened up earlier in another thread at Boost: De-serializing a custom C++ object passed over ZeroMQ pull socket. The problem in that thread has been resolved based on the answer provided. Now I have another problem at runtime. Please, see the below description. I am realtively new to C++ realm so I appreciate if you tell me any necessity for improvement in any part of the code provided in addition to what I descibed under problem statment.
Description:
I have a C++ class named GenericMessage which simply holds an id and data as its members (See code snippet 2 below - GenericMessage.hxx). My intention is to serialize an instance of this class and send it via a ZeroMQ socket which implements the push pattern.
The serialization and sending task has been implemented in class ZMQHandler (see sendToBE function) which is placed in a header file name ZMQHandler.hxx shown in the code snippet 3 below. This class is instantiated by TestFE.cxx shown in the 4rd code snippet below.
The receiving and de-serialization of the GenericMessage instance is implemented in TestBE.cxx available in the 5th code snippet below. My intention is to receive the GenericMessage instance over the ZMQ socket (i.e. pull socket), de-serialize it and then print its members to standard output.
I verified that seriazalition and the GenericMessage object that is transferred over the ZeroMQ socket works fine. Deserialization seems to work as well cause I dont get any exception or segmentation fault like thing.
Problem Statement:
What is expected from the code in TestBE.cxx (see code snippet 5) is to receive the GenericMessage object over the ZeroMQ socket deserialize it and then print its two members namely id and data which is a string object in this case. More precisely it should first print the content of the char stream it gets and then the members of the de-serialized object. Instead, it does not print these members at all. Al, it puts weird symbols including question marks into the received char stream. Please see the 1st code snippet below, you'll see my point.
QUESTIONs:
i) Why cannot I get the expected output? Why do I see the question marked weird symbols in the output? Why I don't see the id and data fields printed although they are visible in the printed char stream?
ii) The data field in the GenericMessage class is a template type which is set to std::string for testing purposes. However, in real use, I plan to transfer a much more complex object in a serialized form. In this respect, do you think the use of classes boost::archive::text_iarchive and boost::archive::text_oarchive is useful. Should I use binary instead? If so, is there some pitfalls/possible problems that you think I should be aware of? Thanks in advance.
SNIPPET 1: Program output vs. expected output
*******************
The EXPECTED OUTPUT
*******************
Connecting to FE...
CHAR [22 serialization::archive 9 0 1 0
0 1 12 Hello there!]
ID: 1
Data: Hello there!
CHAR [22 serialization::archive 9 0 1 0
0 2 12 Hello there!]
ID: 2
Data: Hello there!
CHAR [22 serialization::archive 9 0 1 0
0 3 12 Hello there!]
ID: 3
Data: Hello there!
......
*************************
PRINTED OUTPUT IN REALITY
*************************
Connecting to FE...
CHAR [22 serialization::archive 9 0 1 0
0 1 12 Hello there!]
ID: 1
Data: Hello there!
//continues in increasing order same as above until the 18th message in the following
CHAR [22 serialization::archive 9 0 1 0
0 18 12 Hello there!]
ID: 0
Data:
//!!!!AFTER the 18th message I got question marks in the printed char stream!!!!!
CHAR [22 serialization::archive 9 0 1 0
0 19 12 Hello there!���]
ID: 0
Data:
CHAR [22 serialization::archive 9 0 1 0
0 20 12 Hello there!���]
ID: 0
Data:
CODE SNIPPET 2 (GenericMessage.hxx)
#include <iostream>
#include <string>
#include <sstream>
#include <boost/serialization/serialization.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
template <class T>
class GenericMessage {
public:
GenericMessage():
beId(-1)
{}
GenericMessage(int id, T msg):
beId(id),
data(msg)
{}
~GenericMessage(){}
T getData()
{
return data;
}
std::string toString()
{
std::ostringstream ss;
ss << getBeId();
std::string ret = ss.str();
return ("ID: " + ret + " DATA: " + getData());
}
void setBeId(int id)
{
beId = id;
}
int getBeId()
{
return beId;
}
private:
friend class boost::serialization::access;
int beId;
T data;
template <class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & beId;
ar & data;
}
};
CODE SNIPPET 3 (ZmqHandler.hxx)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <unistd.h>
#include <cassert>
template <class A>
class ZmqHandler {
public:
ZmqHandler():
mContext(1),
mOutbHandlerSocket(mContext, ZMQ_PUSH)
{
mOutbHandlerSocket.bind ("tcp://*:5555");
}
~ZmqHandler() {}
void sendToBE(GenericMessage<A> *theMsg)
{
std::ostringstream archive_stream;
boost::archive::text_oarchive archive(archive_stream);
try
{
archive << theMsg;
} catch (boost::archive::archive_exception& ex) {
std::cout << "Archive Exception during deserializing:" << std::endl;
std::cout << ex.what() << std::endl;
} catch (int e) {
std::cout << "EXCEPTION " << e << std::endl;
}
std::string outbound_data_ = archive_stream.str();
// no need to use the c-style string function 'strlen'
int len = outbound_data_.length();
zmq::message_t msgToSend(len);
memcpy( msgToSend.data(), outbound_data_.data(), len );
mOutbHandlerSocket.send(msgToSend);
std::cout << "SENT request: [" << theMsg->toString() << "]" << std::endl;
std::cout << "LENGTH [" << len << "]" << std::endl;
}
private:
zmq::context_t mContext;
zmq::socket_t mOutbHandlerSocket;
};
CODE SNIPPET 4 (TestFE.cxx)
#include "ZmqHandler.hxx"
int main ()
{
ZmqHandler<std::string> zmqHandler;
int counter = 1;
while(1)
{
std::string data = "Hello there!";
GenericMessage<std::string> msg(counter, data);
zmqHandler.sendToBE(&msg);
counter++;
sleep(1);
}
return 0;
}
CODE SNIPPET 5 (TestBE.cxx)
#include "zmq.hpp"
#include "GenericMessage.hxx"
#include <fstream>
int main ()
{
// Prepare our context and socket
zmq::context_t context (1);
zmq::socket_t socket (context, ZMQ_PULL);
std::cout << "Connecting to FE..." << std::endl;
socket.connect ("tcp://localhost:5555");
while(1){
zmq::message_t reply;
socket.recv (&reply);
const char *buf = static_cast<const char*>(reply.data());
std::cout << "CHAR [" << buf << "]" << std::endl;
std::string input_data_( buf, reply.size() );
std::istringstream archive_stream(input_data_);
boost::archive::text_iarchive archive(archive_stream);
GenericMessage<std::string> theMsg;
try
{
archive >> theMsg;
} catch (boost::archive::archive_exception& ex) {
std::cout << "Archive Exception during deserializing:" << std::endl;
std::cout << ex.what() << std::endl;
} catch (int e) {
std::cout << "EXCEPTION " << e << std::endl;
}
std::cout << "ID: " << theMsg.getBeId() << std::endl;
std::cout << "Data: " << theMsg.getData() << std::endl;
}
return 0;
}
When I build and run your code on my system, TestBE does throw the deserialization exception (every time). Here's what I did to fix it:
In your ZmqHandler class, change the method void sendToBE(GenericMessage<A> *theMsg) to void sendToBE(GenericMessage<A> theMsg). You can use a const& if you want, but you probably don't want to use a pointer here. In the same method, you need to change theMsg->XXX to theMsg.XXX, since theMsg is no longer a pointer.
In TestFE, zmqHandler.sendToBE(&msg); becomes zmqHandler.sendToBE(msg);.
If theMsg must be a pointer
In ZmqHandler, just change the line archive << theMsg to archive << *theMsg. That way, the archive's operator<< is working with the object, rather than a pointer to the object. The rest of your code can remain the same.
I have a program that throws an uncaught exception somewhere. All I get is a report of an exception being thrown, and no information as to where it was thrown. It seems illogical for a program compiled to contain debug symbols not to notify me of where in my code an exception was generated.
Is there any way to tell where my exceptions are coming from short of setting 'catch throw' in gdb and calling a backtrace for every single thrown exception?
Here's some info that may be of use in debugging your problem
If an exception is uncaught, the special library function std::terminate() is automatically called. Terminate is actually a pointer to a function and default value is the Standard C library function std::abort(). If no cleanups occur for an uncaught exception†, it may actually be helpful in debugging this problem as no destructors are called.
†It is implementation-defined whether or not the stack is unwound before std::terminate() is called.
A call to abort() is often useful in generating a core dump that can be analyzed to determine the cause of the exception. Make sure that you enable core dumps via ulimit -c unlimited (Linux).
You can install your own terminate() function by using std::set_terminate(). You should be able to set a breakpoint on your terminate function in gdb. You may be able to generate a stack backtrace from your terminate() function and this backtrace may help in identifying the location of the exception.
There is a brief discussion on uncaught exceptions in Bruce Eckel's Thinking in C++, 2nd Ed that may be helpful as well.
Since terminate() calls abort() by default (which will cause a SIGABRT signal by default), you may be able to set a SIGABRT handler and then print a stack backtrace from within the signal handler. This backtrace may help in identifying the location of the exception.
Note: I say may because C++ supports non-local error handling through the use of language constructs to separate error handling and reporting code from ordinary code. The catch block can be, and often is, located in a different function/method than the point of throwing. It has also been pointed out to me in the comments (thanks Dan) that it is implementation-defined whether or not the stack is unwound before terminate() is called.
Update: I threw together a Linux test program called that generates a backtrace in a terminate() function set via set_terminate() and another in a signal handler for SIGABRT. Both backtraces correctly show the location of the unhandled exception.
Update 2: Thanks to a blog post on Catching uncaught exceptions within terminate, I learned a few new tricks; including the re-throwing of the uncaught exception within the terminate handler. It is important to note that the empty throw statement within the custom terminate handler works with GCC and is not a portable solution.
Code:
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <execinfo.h>
#include <signal.h>
#include <string.h>
#include <iostream>
#include <cstdlib>
#include <stdexcept>
void my_terminate(void);
namespace {
// invoke set_terminate as part of global constant initialization
static const bool SET_TERMINATE = std::set_terminate(my_terminate);
}
// This structure mirrors the one found in /usr/include/asm/ucontext.h
typedef struct _sig_ucontext {
unsigned long uc_flags;
struct ucontext *uc_link;
stack_t uc_stack;
struct sigcontext uc_mcontext;
sigset_t uc_sigmask;
} sig_ucontext_t;
void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) {
sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;
// Get the address at the time the signal was raised from the EIP (x86)
void * caller_address = (void *) uc->uc_mcontext.eip;
std::cerr << "signal " << sig_num
<< " (" << strsignal(sig_num) << "), address is "
<< info->si_addr << " from "
<< caller_address << std::endl;
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
// overwrite sigaction with caller's address
array[1] = caller_address;
char ** messages = backtrace_symbols(array, size);
// skip first stack frame (points here)
for (int i = 1; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
exit(EXIT_FAILURE);
}
void my_terminate() {
static bool tried_throw = false;
try {
// try once to re-throw currently active exception
if (!tried_throw++) throw;
}
catch (const std::exception &e) {
std::cerr << __FUNCTION__ << " caught unhandled exception. what(): "
<< e.what() << std::endl;
}
catch (...) {
std::cerr << __FUNCTION__ << " caught unknown/unhandled exception."
<< std::endl;
}
void * array[50];
int size = backtrace(array, 50);
std::cerr << __FUNCTION__ << " backtrace returned "
<< size << " frames\n\n";
char ** messages = backtrace_symbols(array, size);
for (int i = 0; i < size && messages != NULL; ++i) {
std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
}
std::cerr << std::endl;
free(messages);
abort();
}
int throw_exception() {
// throw an unhandled runtime error
throw std::runtime_error("RUNTIME ERROR!");
return 0;
}
int foo2() {
throw_exception();
return 0;
}
int foo1() {
foo2();
return 0;
}
int main(int argc, char ** argv) {
struct sigaction sigact;
sigact.sa_sigaction = crit_err_hdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGABRT, &sigact, (struct sigaction *)NULL) != 0) {
std::cerr << "error setting handler for signal " << SIGABRT
<< " (" << strsignal(SIGABRT) << ")\n";
exit(EXIT_FAILURE);
}
foo1();
exit(EXIT_SUCCESS);
}
Output:
my_terminate caught unhanded exception. what(): RUNTIME ERROR!
my_terminate backtrace returned 10 frames
[bt]: (0) ./test(my_terminate__Fv+0x1a) [0x8048e52]
[bt]: (1) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (2) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (3) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (4) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (5) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (6) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (7) ./test(main+0xc1) [0x8049121]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__eh_alloc+0x3d) [0x8048b21]
signal 6 (Aborted), address is 0x1239 from 0x42029331
crit_err_hdlr backtrace returned 13 frames
[bt]: (1) ./test(kill+0x11) [0x42029331]
[bt]: (2) ./test(abort+0x16e) [0x4202a8c2]
[bt]: (3) ./test [0x8048f9f]
[bt]: (4) /usr/lib/libstdc++-libc6.2-2.so.3 [0x40045baa]
[bt]: (5) /usr/lib/libstdc++-libc6.2-2.so.3 [0x400468e5]
[bt]: (6) /usr/lib/libstdc++-libc6.2-2.so.3(__rethrow+0xaf) [0x40046bdf]
[bt]: (7) ./test(throw_exception__Fv+0x68) [0x8049008]
[bt]: (8) ./test(foo2__Fv+0xb) [0x8049043]
[bt]: (9) ./test(foo1__Fv+0xb) [0x8049057]
[bt]: (10) ./test(main+0xc1) [0x8049121]
[bt]: (11) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (12) ./test(__eh_alloc+0x3d) [0x8048b21]
As you say, we can use 'catch throw' in gdb and call 'backtrace' for every single thrown exception. While that's usually too tedious to do manually, gdb allows automation of the process. That allows seeing the backtrace of all exceptions that are thrown, including the last uncaught one:
gdb>
set pagination off
catch throw
commands
backtrace
continue
end
run
Without further manual intervention, this generates lots of backtraces, including one for the last uncaught exception:
Catchpoint 1 (exception thrown), 0x00a30 in __cxa_throw () from libstdc++.so.6
#0 0x0da30 in __cxa_throw () from /usr/.../libstdc++.so.6
#1 0x021f2 in std::__throw_bad_weak_ptr () at .../shared_ptr_base.h:76
[...]
terminate called after throwing an instance of 'std::bad_weak_ptr'
what(): bad_weak_ptr
Program received signal SIGABRT, Aborted.
Here's a great blog post wrapping this up: http://741mhz.com/throw-stacktrace [on archive.org]
You can create a macro like:
#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, __LINE__, (message) )
...and it will give you the location where the exception is thrown (admittedly not the stack trace). It's necessary for you to derive your exceptions from some base class that takes the above constructor.
You can mark main tight places in your code as noexcept to locate an exception, then use libunwind (just add -lunwind to linker parameters) (tested with clang++ 3.6):
demagle.hpp:
#pragma once
char const *
get_demangled_name(char const * const symbol) noexcept;
demangle.cpp:
#include "demangle.hpp"
#include <memory>
#include <cstdlib>
#include <cxxabi.h>
namespace
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< char, decltype(std::free) & > demangled_name{nullptr, std::free};
#pragma clang diagnostic pop
}
char const *
get_demangled_name(char const * const symbol) noexcept
{
if (!symbol) {
return "<null>";
}
int status = -4;
demangled_name.reset(abi::__cxa_demangle(symbol, demangled_name.release(), nullptr, &status));
return ((status == 0) ? demangled_name.get() : symbol);
}
backtrace.hpp:
#pragma once
#include <ostream>
void
backtrace(std::ostream & _out) noexcept;
backtrace.cpp:
#include "backtrace.hpp"
#include <iostream>
#include <iomanip>
#include <limits>
#include <ostream>
#include <cstdint>
#define UNW_LOCAL_ONLY
#include <libunwind.h>
namespace
{
void
print_reg(std::ostream & _out, unw_word_t reg) noexcept
{
constexpr std::size_t address_width = std::numeric_limits< std::uintptr_t >::digits / 4;
_out << "0x" << std::setfill('0') << std::setw(address_width) << reg;
}
char symbol[1024];
}
void
backtrace(std::ostream & _out) noexcept
{
unw_cursor_t cursor;
unw_context_t context;
unw_getcontext(&context);
unw_init_local(&cursor, &context);
_out << std::hex << std::uppercase;
while (0 < unw_step(&cursor)) {
unw_word_t ip = 0;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
if (ip == 0) {
break;
}
unw_word_t sp = 0;
unw_get_reg(&cursor, UNW_REG_SP, &sp);
print_reg(_out, ip);
_out << ": (SP:";
print_reg(_out, sp);
_out << ") ";
unw_word_t offset = 0;
if (unw_get_proc_name(&cursor, symbol, sizeof(symbol), &offset) == 0) {
_out << "(" << get_demangled_name(symbol) << " + 0x" << offset << ")\n\n";
} else {
_out << "-- error: unable to obtain symbol name for this frame\n\n";
}
}
_out << std::flush;
}
backtrace_on_terminate.hpp:
#include "demangle.hpp"
#include "backtrace.hpp"
#include <iostream>
#include <type_traits>
#include <exception>
#include <memory>
#include <typeinfo>
#include <cstdlib>
#include <cxxabi.h>
namespace
{
[[noreturn]]
void
backtrace_on_terminate() noexcept;
static_assert(std::is_same< std::terminate_handler, decltype(&backtrace_on_terminate) >{});
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
std::unique_ptr< std::remove_pointer_t< std::terminate_handler >, decltype(std::set_terminate) & > terminate_handler{std::set_terminate(backtrace_on_terminate), std::set_terminate};
#pragma clang diagnostic pop
[[noreturn]]
void
backtrace_on_terminate() noexcept
{
std::set_terminate(terminate_handler.release()); // to avoid infinite looping if any
backtrace(std::clog);
if (std::exception_ptr ep = std::current_exception()) {
try {
std::rethrow_exception(ep);
} catch (std::exception const & e) {
std::clog << "backtrace: unhandled exception std::exception:what(): " << e.what() << std::endl;
} catch (...) {
if (std::type_info * et = abi::__cxa_current_exception_type()) {
std::clog << "backtrace: unhandled exception type: " << get_demangled_name(et->name()) << std::endl;
} else {
std::clog << "backtrace: unhandled unknown exception" << std::endl;
}
}
}
std::_Exit(EXIT_FAILURE); // change to desired return code
}
}
There is good article concerning the issue.
You did not pass information about what OS / Compiler you use.
In Visual Studio C++ Exceptions can be instrumented.
See
"Visual C++ Exception-Handling Instrumentation" on ddj.com
My article "Postmortem Debugging", also on ddj.com includes code to use Win32 structured exception handling (used by the instrumentation) for logging etc.
I've got code to do this in Windows/Visual Studio, let me know if you want an outline. Don't know how to do it for dwarf2 code though, a quick google suggests that there's a function _Unwind_Backtrace in libgcc that probably is part of what you need.
Check this thread, perhaps it helps:
Catching all unhandled C++ exceptions?
I made good experiences with that software:
http://www.codeproject.com/KB/applications/blackbox.aspx
It can print out a stack trace to a file for any unhandled exception.