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
Related
I wrote a program that copy a file from a given path(s) to another. It runs well until it meets special character in directory names or in file names. At that moment it stops and throws the error that "No such file or directory".
This is what I done until now:
#include <iostream>
#include <vector>
#include <filesystem>
#include <cxxabi.h>
#include <string>
#include <sstream>
#include <memory>
#include <windows.h>
#include <chrono>
#include <thread>
using namespace std;
namespace fs = std::filesystem;
int main(int argc, char **argv) {
vector<string> args(argv + 1, argv + argc);
auto target = args[args.size() - 1];
fs::path path = target;
cout << "Destination path: " << target << endl;
args.erase(args.end());
for (const auto &source : args) {
try {
for (const auto &entry : fs::recursive_directory_iterator(source)) {
std::string new_path = target + "\\" + entry.path().relative_path().string();
//if entry is directory:
while (true) {
if (GetDriveType(const_cast<char *>(path.root_path().string().c_str())) != DRIVE_NO_ROOT_DIR) {
if (fs::is_directory(entry)) {
//only if it NOT exists:
if (!fs::exists(new_path)) {
//create it only if not empty:
if (!fs::is_empty(entry)) {
//Creating directory tree structure with empty folders:
try {
fs::create_directories(new_path);
} catch (const std::exception &e) // caught by reference to base
{
std::cout << "When trying to create directory" << new_path
<< "A standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
}
}
break;
} else {
cout << "Destination path is not available. Sleeping for 3 minutes!" << endl;
std::this_thread::sleep_for(180000ms);
}
}
while (true) {
if (GetDriveType(const_cast<char *>(path.root_path().string().c_str())) != DRIVE_NO_ROOT_DIR) {
if ((fs::is_regular_file(entry)) && (fs::exists(entry))) {
if (!fs::is_empty(entry)) {
if (!fs::exists(new_path)) {
//file does NOT exists in new path:
try {
fs::copy_file(entry.path().string(), new_path);
cout << "Copy file: " << entry.path().string() << endl;
fs::remove(entry);
} catch (const std::exception &e) // caught by reference to base
{
std::cout
<< "When trying to get file size and source a standard exception was caught, with message '"
<< e.what() << "'\n";
}
} else {
//if it exists in new path:
//first try to get file size and if this gives an error then do not copy:
if (fs::file_size(entry.path().string()) >
fs::file_size(entry.path().string())) {
try {
fs::copy_file(entry.path().string(), new_path);
cout << "Replacing file: " << entry.path().string() << endl;
fs::remove(entry);
} catch (const std::exception &e) // caught by reference to base
{
std::cout
<< "When trying to get file size and source a standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
}
}
}
break;
} else {
cout << "Destination path is not available. Sleeping for 3 minutes!" << endl;
std::this_thread::sleep_for(180000ms);
}
}//end while!
}
} catch (const std::exception &e) // caught by reference to base
{
std::cout << "When recursive through directory tree a standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
return 0;
}
After searching on Google and mostly on stackoverflow for a solution conclusion is that none works.
I tried adding #define UNICODE and #define _UNICODE at the top of it but it gives even more errors.
I also added -municode flag in CMakeLists in CLion but also not working (it compiles but gives runtime error).
Also tried to replace all possible string to wstring or wchar_t * with L"where possible" and to convert this entry.path().relative_path().string() to entry.path().relative_path().wstring() and also cout to wcout. Still not working.
Also changed main to wmain( int argc, wchar_t *argv[ ]) or to wmain( int argc, wchar_t *argv[ ], wchar_t *envp[ ] ) and still not working.
Also added setlocale(LC_ALL, ""); after the main function as the other article on stackoverflow says and still no improvement.
I am asking for help because I didn't find a solution for this problem and also, more of the other solution are for printing special unicode characters to console but I need more to work with them (read files names and paths that contain special unicode characters) instead of printing them.
More than this, after I tried all of these possible not working solutions I am talking about above and reverted my program back to the original code that I just posted above now it is not working at all. It says "no such file or directory" even for normal latin characters and doesn't copy or delete anything at all anymore.
Go to the header file in which std::filesystem::path is defined.
(possibly in: PATH_TO_MINGW/usr/include/c++/YOUR_VERSION/bits/fs_path)
Look for using value_type =
Look for compiler macros that define which value_type is ultimately used.
an example from the version from my system:
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
using value_type = wchar_t;
static constexpr value_type preferred_separator = L'\\';
#else
When the macro _GLIBCXX_FILESYSTEM_IS_WINDOWS is set to 1 then a wchar_t will be used, which should solve your issue.
I have a member function within a verify class that generates a hash for a set of files.
The code compiles correctly though when the software reaches the hashing function it throws a seg fault. I have been looking for ages and I just can't see it. It seems to be something to do with the path vector and maybe the copy function.
Program received signal SIGSEGV, Segmentation fault.
0x000055c84fbbc058 in std::vector<boost::filesystem::path, std::allocator<boost::filesystem::path> >::push_back(boost::filesystem::path const&) ()
(gdb)
Single stepping until exit from function _ZNSt6vectorIN5boost10filesystem4pathESaIS2_EE9push_backERKS2_,
which has no line number information.
Program terminated with signal SIGSEGV, Segmentation fault.
output from gdb
code below:
#pragma once
#include <boost/filesystem.hpp>
#include <cryptopp/cryptlib.h>
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
#include <cryptopp/md5.h>
#include "cryptopp/hex.h"
#include "cryptopp/files.h"
class Verify {
private:
typedef std::vector<boost::filesystem::path> vec;
vec v;
public:
bool verify_files();
bool generate_hash(boost::filesystem::path source);
};
function implementation
bool Verify::generate_hash(boost::filesystem::path source) {
try {
if (boost::filesystem::exists(source)) { //make sure path is valid
if (boost::filesystem::is_regular_file(source)) { //no regular files
std::cout << "Please enter base path of directory only." << std::endl;
return false;
}
else if (boost::filesystem::is_directory(source)){ //process directory here
std::cout << "Scanning " << source << std::endl;
std::copy(boost::filesystem::recursive_directory_iterator(source), boost::filesystem::recursive_directory_iterator(), std::back_inserter(this->v));
std::sort(v.begin(), v.end());
for (vec::const_iterator it (v.begin()); it != v.end(); ++it){
std::string result;
if (boost::filesystem::is_regular_file(*it)) {
Weak::MD5 hash;
FileSource((*it).string().c_str(), true, new HashFilter(hash, new HexEncoder(new StringSink(result), false)));
}
std::cout << "Hashing: " << *it << " - " << result << std::endl;
}
}
}
} catch (const boost::filesystem::filesystem_error& e){
std::cout << e.what() << std::endl;
return false;
}
return true;
}
Your error is on this line:
std::copy(boost::filesystem::recursive_directory_iterator(source), boost::filesystem::recursive_directory_iterator(), std::back_inserter(this->v));
You should do a manual loop here instead to investigate further (if that doesn't eliminiate the issue)
Visual studio is hiding my exception messages. Take the following code example:
#include <stdio.h>
#include <iostream>
#include <exception>
void exceptional_method(){
throw std::runtime_error("Hello from exceptional_method!");
}
int main(){
std::cout << "Hello world!" << std::endl;
exceptional_method();
std::cin.get();
}
Visual studio gives me some vague addresses:
Unhandled exception at 0x76A9DDC2 in ExceptionTest.exe: Microsoft C++ exception: std::runtime_error at memory location 0x006FFD34.
Whereas linux mint gives me the following output on the terminal:
Hello world!
terminate called after throwing an instance of 'std::runtime_error'
what(): Hello from exceptional_method!
Aborted (core dumped)
I've googled a bunch, messed around with the settings in Visual studio, but cannot figure this out. My current workaround is writing the exception message to console before throwing so that I can at least catch the message so I know which exception was thrown.
inline void throw_exception(string& message)
{
cout << message << endl;
throw runtime_error(message);
}
This is not ideal. Any help would be greatly appreciated.
edit:
Getting the debugger to break on the actual exception instead of a few lines ahead was the problem, causing me to investigate the wrong code.
The following solution is what I was looking for.
#ifndef DEBUG_ASSERT_H
#define DEBUG_ASSERT_H
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
inline void log_failed_assert(const string message, const string expression, const string file, const long line) {
cout << "Assert failed." << endl;
cout << "Expression: " << expression << endl;
cout << "Message : " << message << endl;
cout << "File : " << file << endl;
cout << "Line : " << line << endl;
}
inline void windows_break()
{
#ifdef _WIN32
__debugbreak();
#endif
}
//do {
//} while (0)
#ifdef _DEBUG
#ifdef _WIN32
#define DEBUG_ASSERT(expr, s) do{\
if(!(expr)){\
log_failed_assert(s, #expr, __FILE__, __LINE__);\
__debugbreak();\
}\
} while(0)
#else
#define DEBUG_ASSERT(expr, s) do{\
if(!(expr)){\
log_failed_assert(s, #expr, __FILE__, __LINE__);\
}\
} while(0)
#endif
#else
#define DEBUG_ASSERT(expr, s)
#endif
#endif
Exceptions are there to be catched. If you dont catch it your program will terminate. If this is what you want, there are easier ways to terminate. If you catch the exception in main you can use the message to eg print it:
#include <exception>
#include <iostream>
void exceptional_method(){
throw std::runtime_error("Hello from exceptional_method!");
}
int main(){
std::cout << "Hello world!" << std::endl;
try {
exceptional_method();
} catch (std::exception& e) {
std::cout << e.what();
}
std::cin.get();
}
As RichardCritten pointed out, it is mint being nice to you rather than visual studio "hiding" the message, as there is no requirement to print the message when your program terminates.
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.
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.