The following simple program segfaults on my mac (Lion) running gcc 4.2.1:
#include <iostream>
using namespace std;
struct A{
friend std::ostream& operator << (std::ostream& os, const A& a) {
os << 3 << endl;
}
};
template <typename T>
T f() { return T();}
int f() { return 2;}
int main() {
cout << f() << endl;
A a= f<A>();
cout << a << endl;
}
When I run the program, I get:
./a.out
2
3
Segmentation fault: 11
When I do a stacktrace, I get:
(gdb) run
Starting program: a.out
unable to read unknown load command 0x24
unable to read unknown load command 0x26
2
3
Program received signal SIGSEGV, Segmentation fault.
0x00007fff8b84fa49 in ?? ()
(gdb) bt
#0 0x00007fff8b84fa49 in ?? ()
#1 0x00007fff665c1ae8 in ?? ()
#2 0x0000000000000000 in ?? ()
The backtrace has no useful information (anyone know why?). This works
fine in linux.
Let's make it clearer what happens
(cout << a) << endl;
You forgot a return in your operator<<.
Related
My test code is like this:
#include <iostream>
#include <string>
#include <thread>
#include <unistd.h>
using namespace std;
class ns_string {
public:
ns_string(string s) : _s(s) {}
static ns_string make_ns_string(const string &s) {
return ns_string(s);
}
string ns() const { return _s; }
private:
string _s;
};
void f2(string s) {
cout << "f2:" << s << endl;
}
void thread_func() {
while (1) {
cout << "------------------ another thread -> ";
f2("thread_func");
sleep(5);
}
}
void f(int i, const ns_string& name) {
cout << i << ", " << name.ns() << endl;
f2(name.ns());
}
int main(int argc,char* argv[]) {
thread t(thread_func);
for (int i=0; i<10; i++) {
f(i, ns_string::make_ns_string(std::to_string(i)));
}
t.join();
return 0;
}
I want to set a breakpoint when f2 parameter s == "4".
I do that via b f2 if strcmp(s.c_str(), "4") == 0.
Here're three cases that I experiment.
GDB version is 11.1
case 1
DONOT start thread_func. Everything is ok. The program stops when s=="4"
(gdb) r
0, 0
f2:0
1, 1
f2:1
2, 2
f2:2
3, 3
f2:3
4, 4
Breakpoint 1, f2 (s="4") at gdb-crash.cc:21
21 cout << "f2:" << s << endl;
case 2
Start thread_func, but DONOT call f2. gdb outputs:
(gdb) r
[New Thread 0xffffbf17c1c0 (LWP 20299)]
------------------ another thread -> 0, 0
Error in testing breakpoint condition:
Unable to fetch general registers.: No such process.
An error occurred while in a function called from GDB.
Evaluation of the expression containing the function
(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const) will be abandoned.
When the function is done executing, GDB will silently stop.
Selected thread is running.
(gdb) c
Continuing.
f2:0
1, 1
f2:1
2, 2
f2:2
3, 3
f2:3
4, 4
Thread 1 "a.out" hit Breakpoint 1, f2 (s="4") at gdb-crash.cc:21
21 cout << "f2:" << s << endl;
However I try to continue, it still can stop at s=="4.
case 3
Start the thread_func, and call f2
Program die. gdb outputs:
Error in testing breakpoint condition:
The program stopped in another thread while making a function call from GDB.
Evaluation of the expression containing the function
(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::c_str() const) will be abandoned.
When the function is done executing, GDB will silently stop.
[Switching to Thread 0x7ffff7a47740 (LWP 1832)]
Thread 1 "a.out" hit Breakpoint 1, f2 (s="0") at a.cc:30
30 cout << "f2:" << s << endl;
Missing separate debuginfos, use: dnf debuginfo-install libgcc-11.2.1-9.fc34.x86_64 libstdc++-11.2.1-9.fc34.x86_64
../../gdb/infcall.c:1284: internal-error: value* call_function_by_hand_dummy(value*, type*, gdb::array_view<value*>, void (*)(void*, int), void*): Assertion `call_thread->thread_fsm == sm' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Obviously different threads access f2, which confuses gdb for some reason.
As a debugger, I don't care about which thread stops at that breakpoint. I only care about gdb makes it stop when my condition is met. How should I acheive that in multi-thread situation ?
This is a bug in GDB. Reproduced using current GDB trunk and GDB-10.0.
Assuming you are using g++ to build the code, here is a workaround:
(gdb) b f2 if s._M_dataplus._M_p[0] == '4' && s._M_dataplus._M_p[1] == '\0'
The most likely reason for the bug is that calling s.c_str() requires that the inferior (being debugged) program be resumed, and while it's running to evaluate s.c_str() another thread hits the breakpoint, causing GDB to become confused.
Similar (but not identical) bug.
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
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.
This question has been asked before and there have been windows-specific answers but no satisfactory gcc answer. I can use set_terminate() to set a function that will be called (in place of terminate()) when an unhandled exception is thrown. I know how to use the backtrace library to generate a stack trace from a given point in the program. However, this won't help when my terminate-replacement is called since at that point the stack has been unwound.
Yet if I simply allow the program to abort(), it will produce a core-dump which contains the full stack information from the point at which the exception was thrown. So the information is there -- but is there a programmatic way to get it, for example so it can be logged, rather than having to examine a core file?
Edited Answer:
You can use std::set_terminate
#include <cstdlib>
#include <iostream>
#include <stdexcept>
#include <execinfo.h>
void
handler()
{
void *trace_elems[20];
int trace_elem_count(backtrace( trace_elems, 20 ));
char **stack_syms(backtrace_symbols( trace_elems, trace_elem_count ));
for ( int i = 0 ; i < trace_elem_count ; ++i )
{
std::cout << stack_syms[i] << "\n";
}
free( stack_syms );
exit(1);
}
int foo()
{
throw std::runtime_error( "hello" );
}
void bar()
{
foo();
}
void baz()
{
bar();
}
int
main()
{
std::set_terminate( handler );
baz();
}
giving this output:
samm#macmini ~> ./a.out
./a.out [0x10000d20]
/usr/lib/libstdc++.so.6 [0xf9bb8c8]
/usr/lib/libstdc++.so.6 [0xf9bb90c]
/usr/lib/libstdc++.so.6 [0xf9bbaa0]
./a.out [0x10000c18]
./a.out [0x10000c70]
./a.out [0x10000ca0]
./a.out [0x10000cdc]
/lib/libc.so.6 [0xfe4dd80]
/lib/libc.so.6 [0xfe4dfc0]
samjmill#bgqfen4 ~>
assuming you have debug symbols in your binary, you can then use addr2line to construct a prettier stack trace postmortem
samm#macmini ~> addr2line 0x10000c18
/home/samm/foo.cc:23
samm#macmini ~>
original answer is below
I've done this in the past using boost::error_info to inject the stack trace using backtrace from execinfo.h into an exception that is thrown.
typedef boost::error_info<struct tag_stack_str,std::string> stack_info;
Then when catching the exceptions, you can do
} catch ( const std::exception& e ) {
if ( std::string const *stack boost::get_error_info<stack_error_info>(e) ) {
std::cout << stack << std::endl;
}
}
Yet if I simply allow the program to abort(), it will produce a core-dump which contains the full stack information from the point at which the exception was thrown. So the information is there -- but is there a programmatic way to get it, for example so it can be logged, rather than having to examine a core file?
I doubt my experience would fit your needs but here it goes anyway.
I was overloading abort(): either by adding my own object file before the libc or using LD_PRELOAD. In my own version of abort() I was starting the debugger telling it to attach to the process (well, I surely know my PID) and dump the stack trace into a file (commands were passed to the debugger via command line). After debugger had finished, terminate the process with e.g. _exit(100).
That was on Linux using GDB. On Solaris I routinely employ similar trick but due to unavailability of a sane debugger I use the pstack tool: system("pstack <PID>").
You can 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);
}
}
There is good article concerning the issue.