Compare commits

...

15 Commits

Author SHA1 Message Date
Antony Polukhin
351b03d522 Fix typo in documentation (fixes #158) 2024-04-01 12:17:55 +03:00
Antony Polukhin
a33e198cf6 More clarifications for async-signal-safety (refs #131) (#156) 2024-03-04 11:45:49 +03:00
Antony Polukhin
824f0c0ea7 Clarify the async-signal-safety guarantees in docs (refs #131) (#154) 2024-02-29 20:47:13 +03:00
Antony Polukhin
f783534b0f Fix frame_msvc.ipp compilation 2024-02-23 14:04:26 +03:00
Antony Polukhin
27093f24cb Update frame_msvc.ipp 2024-02-23 13:50:11 +03:00
Antony Polukhin
12e07fcca1 CI fixes (#153)
* Update thread_safety_checking.cpp to use Standard Library types
* Remove MinGW runs due to https://github.com/boostorg/system/issues/116
* Delete the shared memory example sources
2024-02-22 09:12:00 +03:00
Antony Polukhin
95caaeaf99 Option to disable boost_stacktrace_from_exception library build 2024-02-17 10:26:49 +03:00
Antony Polukhin
906bb0b551 Add unignorable info on memory leaks for affected platforms (#150) 2024-02-16 08:49:57 +03:00
Antony Polukhin
87ef7f6950 Fix build (failure was noted in #147) (#151) 2024-02-15 09:19:27 +03:00
Antony Polukhin
f6d4e117cd Add missing include 2024-02-12 22:21:35 +03:00
Antony Polukhin
0d8aed6bc9 Library for getting stacktraces from arbitrary exceptions (#147) 2024-02-07 11:27:45 +03:00
Antony Polukhin
3de5aea554 Merge pull request #149 from Lastique/feature/update_filesystem_usage
Update Boost.Filesystem usage
2024-01-27 11:54:24 +03:00
Andrey Semashev
e9e0b6c3d4 Updated Boost.Filesystem usage.
Removed usage of Boost.Filesystem APIs that were deprecated and
then removed.
2024-01-27 01:28:50 +03:00
Antony Polukhin
1fd3665a71 Allow building stacktrace libraries with global visibility (fixes #146) 2024-01-20 11:03:31 +03:00
Antony Polukhin
0ec802df84 Update copyright years 2024-01-07 13:03:02 +03:00
53 changed files with 851 additions and 249 deletions

View File

@@ -27,8 +27,7 @@ jobs:
- toolset: gcc-9
cxxstd: "03,11,14,17,2a"
os: ubuntu-22.04
- toolset: clang
compiler: clang++-14
- toolset: clang-15
cxxstd: "03,11,14,17,2a"
os: ubuntu-22.04
# TODO: fix and uncomment

View File

@@ -1,16 +1,18 @@
# Copyright (C) 2016-2023, Antony Polukhin.
# Copyright (C) 2016-2024, Antony Polukhin.
#
# Use, modification and distribution is subject to the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
#
import feature ;
import ../../config/checks/config : requires ;
project
: source-location .
: requirements
[ requires cxx11_rvalue_references ]
: default-build
<visibility>hidden
;
@@ -19,6 +21,7 @@ lib gcc_s ;
lib Dbgeng ;
lib ole32 ;
feature.feature boost.stacktrace.from_exception : on off : optional propagated ;
local LIBBACKTRACE_PATH = [ modules.peek : LIBBACKTRACE_PATH ] ;
lib backtrace
@@ -131,4 +134,22 @@ lib boost_stacktrace_windbg_cached
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
;
boost-install boost_stacktrace_noop boost_stacktrace_backtrace boost_stacktrace_addr2line boost_stacktrace_basic boost_stacktrace_windbg boost_stacktrace_windbg_cached ;
lib boost_stacktrace_from_exception
: # sources
../src/from_exception.cpp
: # requirements
<warnings>all
<target-os>linux:<library>dl
<target-os>windows:<build>no # not supported at the moment
# Command line option to disable build
<boost.stacktrace.from_exception>off:<build>no
# Require usable libbacktrace on other platforms
[ check-target-builds ../build//libbacktrace : : <build>no ]
: # default build
: # usage-requirements
#<link>shared:<define>BOOST_STACKTRACE_DYN_LINK=1
;
boost-install boost_stacktrace_noop boost_stacktrace_backtrace boost_stacktrace_addr2line boost_stacktrace_basic boost_stacktrace_windbg boost_stacktrace_windbg_cached boost_stacktrace_from_exception ;

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
# Copyright Antony Polukhin, 2016-2023.
# Copyright Antony Polukhin, 2016-2024.
# Use, modification, and distribution are
# subject to the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -22,6 +22,9 @@ doxygen autodoc
<doxygen:param>SEARCH_INCLUDES=YES
<doxygen:param>SHORT_NAMES=NO
<doxygen:param>INCLUDE_PATH=../../../
<doxygen:param>"ALIASES= \\
\"asyncsafe=\\xmlonly<link linkend='stacktrace.theoretical_async_signal_safety'>\\endxmlonly Theoretically async signal safe \\xmlonly</link>\\endxmlonly\" \\
"
<doxygen:param>"PREDEFINED=\"stl_type_info=std::type_info\" \\
\"BOOST_EXPLICIT_OPERATOR_BOOL_NOEXCEPT()=explicit operator bool() const noexcept;\" \\
\"BOOST_CONSTEXPR_EXPLICIT_OPERATOR_BOOL()=explicit constexpr operator bool() const noexcept;\" \\
@@ -39,9 +42,9 @@ boostbook standalone
:
stacktrace
:
<xsl:param>boost.root=http://www.boost.org/doc/libs/1_63_0
<xsl:param>boost.root=http\://www.boost.org/doc/libs/1_84_0
# <xsl:param>boost.root=../../../..
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/doc/html
<format>pdf:<xsl:param>boost.url.prefix=http\://www.boost.org/doc/libs/release/doc/html
;
###############################################################################

View File

@@ -2,7 +2,7 @@
[quickbook 1.6]
[version 1.0]
[id stacktrace]
[copyright 2016-2023 Antony Polukhin]
[copyright 2016-2024 Antony Polukhin]
[category Language Features Emulation]
[license
Distributed under the Boost Software License, Version 1.0.
@@ -120,14 +120,80 @@ Previous run crashed:
9# 0x0000000000402209
```
[warning There's a temptation to write a signal handler that prints the stacktrace on `SIGSEGV` or abort. Unfortunately, there's no cross platform way to do that without a risk of deadlocking. Not all the platforms provide means for even getting stacktrace in async signal safe way.
[warning There's a temptation to write a signal handler that prints the stacktrace on `SIGSEGV` or abort. Unfortunately,
there's no cross platform way to do that without a risk of deadlocking.
Not all the platforms provide means for even getting stacktrace in async signal safe way.
Signal handler is often invoked on a separate stack and trash is returned on attempt to get a trace!
Generic recommendation is to *avoid signal handlers! Use* platform specific ways to store and decode *core files*.
See [link stacktrace.theoretical_async_signal_safety "Theoretical async signal safety"] for more info.
]
[endsect]
[section Stacktrace from arbitrary exception]
[warning At the moment the functionality is only available for some of the
popular C++ runtimes for POSIX systems and requires *libbacktrace*.
Make sure that your platform is supported by running some tests.
]
The library provides a way to get stacktrace from an exception as if the
stacktrace was captured at the point the exception was thrown. Works even if
the exception was thrown from a third party binary library.
Link with
`boost_stacktrace_from_exception` library (or just `LD_PRELOAD` it!) and call
boost::stacktrace::stacktrace::from_current_exception() to get the trace:
```
#include <iostream>
#include <stdexcept>
#include <string_view>
#include <boost/stacktrace.hpp>
void foo(std::string_view key);
void bar(std::string_view key);
int main() {
try {
foo("test1");
bar("test2");
} catch (const std::exception& exc) {
boost::stacktrace::stacktrace trace = boost::stacktrace::from_current_exception(); // <---
std::cerr << "Caught exception: " << exc.what() << ", trace:\n" << trace;
}
}
```
The output of the above sample may be the following:
```
Caught exception: std::map::at, trace:
0# get_data_from_config(std::string_view) at /home/axolm/basic.cpp:600
1# bar(std::string_view) at /home/axolm/basic.cpp:6
2# main at /home/axolm/basic.cpp:17
```
With the above technique a developer can locate the source file and the function
that has thrown the exception without a debugger help. it is especially useful
for testing in containers (github CI, other CIs), where the developer has no
direct access to the testing environment and reproducing the issue is
complicated.
Note that linking with `boost_stacktrace_from_exception` may increase memory
consumption of the application, as the exceptions now additionally store traces.
At runtime switch boost::stacktrace::this_thread::set_capture_stacktraces_at_throw()
allows to disable/enable capturing and storing traces in exceptions.
To disable the `boost_stacktrace_from_exception` library builds the
`boost.stacktrace.from_exception=off` option, for example
`./b2 boost.stacktrace.from_exception=off`.
[endsect]
@@ -233,43 +299,6 @@ Terminate called:
[endsect]
[/
[section Store stacktraces into shared memory]
There's a way to serialize stacktrace in async safe manner and share that serialized representation with another process. Here's another example with signal handlers.
This example is very close to the [link stacktrace.getting_started.handle_terminates_aborts_and_seg "Handle terminates, aborts and Segmentation Faults"], but this time we are dumping stacktrace into shared memory:
[getting_started_terminate_handlers_shmem]
After registering signal handlers and catching a signal, we may print stacktrace dumps on program restart:
[getting_started_on_program_restart_shmem]
The program output will be the following:
```
Previous run crashed and left trace in shared memory:
0# 0x00007FD51C7218EF
1# my_signal_handler2(int) at ../example/terminate_handler.cpp:68
2# 0x00007FD51B833CB0
3# 0x00007FD51B833C37
4# 0x00007FD51B837028
5# 0x00007FD51BE44BBD
6# 0x00007FD51BE42B96
7# 0x00007FD51BE42BE1
8# bar(int) at ../example/terminate_handler.cpp:18
9# foo(int) at ../example/terminate_handler.cpp:22
10# bar(int) at ../example/terminate_handler.cpp:14
11# foo(int) at ../example/terminate_handler.cpp:22
12# run_3(char const**) at ../example/terminate_handler.cpp:152
13# main at ../example/terminate_handler.cpp:207
14# 0x00007FD51B81EF45
15# 0x0000000000402999
```
[endsect]
]
[endsect]
@@ -286,9 +315,9 @@ By default Boost.Stacktrace is a header-only library, but you may change that an
In header only mode library could be tuned by macro. If one of the link macro from above is defined, you have to manually link with one of the libraries:
[table:libconfig Config
[[Macro name or default] [Library] [Effect] [Platforms] [Uses debug information [footnote This will provide more readable backtraces with *source code locations* if the binary is built with debug information.]] [Uses dynamic exports information [footnote This will provide readable function names in backtrace for functions that are exported by the binary. Compiling with `-rdynamic` flag, without `-fvisibility=hidden` or marking functions as exported produce a better stacktraces.]] ]
[[['default for MSVC, Intel on Windows, MinGW-w64] / *BOOST_STACKTRACE_USE_WINDBG*] [*boost_stacktrace_windbg*] [ Uses `dbgeng.h` to show debug info. May require linking with *ole32* and *dbgeng*. ] [MSVC, MinGW-w64, Intel on Windows] [yes] [no]]
[[['default for MSVC, Intel on Windows, MinGW-w64] / *BOOST_STACKTRACE_USE_WINDBG*] [*boost_stacktrace_windbg*] [ Uses `dbgeng.h` to show debug info, stores the implementation internals in a static varaible protected with mutex. May require linking with *ole32* and *dbgeng*. ] [MSVC, MinGW-w64, Intel on Windows] [yes] [no]]
[[['default for other platforms]] [*boost_stacktrace_basic*] [Uses compiler intrinsics to collect stacktrace and if possible `::dladdr` to show information about the symbol. Requires linking with *libdl* library on POSIX platforms.] [Any compiler on POSIX or MinGW] [no] [yes]]
[[*BOOST_STACKTRACE_USE_WINDBG_CACHED*] [*boost_stacktrace_windbg_cached*] [ Uses `dbgeng.h` to show debug info and caches internals in TLS for better performance. Useful only for cases when traces are gathered very often. May require linking with *ole32* and *dbgeng*. ] [MSVC, Intel on Windows] [yes] [no]]
[[*BOOST_STACKTRACE_USE_WINDBG_CACHED*] [*boost_stacktrace_windbg_cached*] [ Uses `dbgeng.h` to show debug info and caches implementation internals in TLS for better performance. Useful only for cases when traces are gathered very often. May require linking with *ole32* and *dbgeng*. ] [MSVC, Intel on Windows] [yes] [no]]
[[*BOOST_STACKTRACE_USE_BACKTRACE*] [*boost_stacktrace_backtrace*] [Requires linking with *libdl* on POSIX and *libbacktrace* libraries[footnote Some *libbacktrace* packages SEGFAULT if there's a concurrent work with the same `backtrace_state` instance. To avoid that issue the Boost.Stacktrace library uses `thread_local` states, unfortunately this may consume a lot of memory if you often create and destroy execution threads in your application. Define *BOOST_STACKTRACE_BACKTRACE_FORCE_STATIC* to force single instance, but make sure that [@https://github.com/boostorg/stacktrace/blob/develop/test/thread_safety_checking.cpp thread_safety_checking.cpp] works well in your setup. ]. *libbacktrace* is probably already installed in your system[footnote If you are using Clang with libstdc++ you could get into troubles of including `<backtrace.h>`, because on some platforms Clang does not search for headers in the GCC's include paths and any attempt to add GCC's include path leads to linker errors. To explicitly specify a path to the `<backtrace.h>` header you could define the *BOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE* to a full path to the header. For example on Ubuntu Xenial use the command line option *-DBOOST_STACKTRACE_BACKTRACE_INCLUDE_FILE=</usr/lib/gcc/x86_64-linux-gnu/5/include/backtrace.h>* while building with Clang. ], or built into your compiler.
Otherwise (if you are a *MinGW*/*MinGW-w64* user for example) it can be downloaded [@https://github.com/ianlancetaylor/libbacktrace from here] or [@https://github.com/gcc-mirror/gcc/tree/master/libbacktrace from here]. ] [Any compiler on POSIX, or MinGW, or MinGW-w64] [yes] [yes]]
@@ -334,7 +363,33 @@ There are multiple ways to deal with that issue if you distribute PDB files alon
[endsect]
[section Acknowledgements]
[section Theoretical async signal safety]
In theory, walking the stack without decoding and demangling should be async signal safe.
In practice, it is not:
* Looks like a page fault while dumping the trace on a containerized/virtualized
Windows system has a chance to deadlock. Page fault could happen easily
as we have to write the dump either to memory or to a file.
* On POSIX systems a deadlock could happen if a signal is received when throwing
an exception [@https://github.com/boostorg/stacktrace/issues/131 #131].
Theoretically this could be worked around by bypassing the mutex locking
in C++-runtime at exception throw
([@https://github.com/userver-framework/userver/blob/4246909c99506d3ab34bd130a5154b4acc8e87de/core/src/engine/task/exception_hacks.cpp#L241-L244 sample implementation]
in the 🐙 userver framework), or by using a very modern runtime
(glibc-2.35+ with [@https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71744#c32 modern]
libgcc or [@https://reviews.llvm.org/D130668 modern] LLVM's libunwind).
* `-fomit-frame-pointer` like flags add additional complexity to the stack
walking implementation, which may also negatively affect the signal safety.
As a rule of thumb: do *not* capture stack traces in signal handlers unless you
are absolutely sure in your environment and inspected all of its source codes.
[endsect]
[section Acknowledgments]
In order of helping and advising:
@@ -342,6 +397,8 @@ In order of helping and advising:
* Great thanks to Nat Goodspeed for requesting [classref boost::stacktrace::frame] like class.
* Great thanks to Niall Douglas for making an initial review, helping with some platforms and giving great hints on library design.
* Great thanks to all the library reviewers.
* Great thanks to Andrei Nekrashevich for prototyping the idea of stacktraces
from arbitrary exception in `libsfe`.
[endsect]

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -63,38 +63,17 @@ void my_terminate_handler() {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
BOOST_CONSTEXPR_OR_CONST std::size_t shared_memory_size = 4096 * 8;
//[getting_started_terminate_handlers_shmem
#include <boost/stacktrace.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
boost::interprocess::shared_memory_object g_shm; // inited at program start
boost::interprocess::mapped_region g_region; // inited at program start
void my_signal_handler2(int signum) {
::signal(signum, SIG_DFL);
void** f = static_cast<void**>(g_region.get_address());
*f = reinterpret_cast<void*>(1); // Setting flag that shared memory now contains stacktrace.
boost::stacktrace::safe_dump_to(f + 1, g_region.get_size() - sizeof(void*));
::raise(SIGABRT);
}
//]
#include <iostream> // std::cerr
#include <fstream> // std::ifstream
#include <boost/filesystem/path.hpp>
#include <boost/filesystem/operations.hpp>
#ifndef BOOST_WINDOWS
inline void copy_and_run(const char* exec_name, char param, bool not_null) {
std::cout << "Running with param " << param << std::endl;
boost::filesystem::path command = exec_name;
command = command.parent_path() / (command.stem().string() + param + command.extension().string());
boost::filesystem::copy_file(exec_name, command, boost::filesystem::copy_option::overwrite_if_exists);
boost::filesystem::copy_file(exec_name, command, boost::filesystem::copy_options::overwrite_existing);
boost::filesystem::path command_args = command;
command_args += ' ';
@@ -110,6 +89,7 @@ inline void copy_and_run(const char* exec_name, char param, bool not_null) {
std::exit(ret);
}
}
#endif
int run_0(const char* /*argv*/[]) {
//[getting_started_setup_terminate_handlers
@@ -160,67 +140,6 @@ int run_2(const char* argv[]) {
return 0;
}
int run_3(const char* /*argv*/[]) {
using namespace boost::interprocess;
{
shared_memory_object shm_obj(open_or_create, "shared_memory", read_write);
shm_obj.swap(g_shm);
}
g_shm.truncate(shared_memory_size);
{
mapped_region m(g_shm, read_write, 0, shared_memory_size);
m.swap(g_region);
}
void** f = static_cast<void**>(g_region.get_address());
*f = 0;
::signal(SIGSEGV, &my_signal_handler2);
::signal(SIGABRT, &my_signal_handler2);
foo(5);
return 31;
}
int run_4(const char* argv[]) {
using namespace boost::interprocess;
{
shared_memory_object shm_obj(open_only, "shared_memory", read_write);
shm_obj.swap(g_shm);
}
{
mapped_region m(g_shm, read_write, 0, shared_memory_size);
m.swap(g_region);
}
//[getting_started_on_program_restart_shmem
void** f = static_cast<void**>(g_region.get_address());
if (*f) { // Checking if memory contains stacktrace.
boost::stacktrace::stacktrace st
= boost::stacktrace::stacktrace::from_dump(f + 1, g_region.get_size() - sizeof(bool));
std::cout << "Previous run crashed and left trace in shared memory:\n" << st << std::endl;
*f = 0; /*<-*/
shared_memory_object::remove("shared_memory");
if (std::string(argv[0]).find("noop") == std::string::npos) {
if (!st) {
return 43;
}
} else {
if (st) {
return 44;
}
}
} else {
return 42; /*->*/
}
//]
return 0;
}
#include <sstream>
int test_inplace() {
@@ -335,10 +254,6 @@ int main(int argc, const char* argv[]) {
// We are copying files to make sure that stacktrace printing works independently from executable name
copy_and_run(argv[0], '1', true);
copy_and_run(argv[0], '2', false);
// There are some issues with async-safety of shared memory writes on Windows.
copy_and_run(argv[0], '3', true);
copy_and_run(argv[0], '4', false);
#endif
return test_inplace();
@@ -347,9 +262,6 @@ int main(int argc, const char* argv[]) {
switch (argv[1][0]) {
case '0': return run_0(argv);
case '1': return run_1(argv);
case '2': return run_2(argv);
case '3': return run_3(argv);
case '4': return run_4(argv);
}
return 404;

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -13,7 +13,8 @@
#endif
#include <boost/stacktrace/frame.hpp>
#include <boost/stacktrace/stacktrace.hpp> // Actually already includes all the headers
#include <boost/stacktrace/stacktrace.hpp>
#include <boost/stacktrace/safe_dump_to.hpp>
#include <boost/stacktrace/this_thread.hpp>
#endif // BOOST_STACKTRACE_HPP

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -21,6 +21,8 @@
#include <windows.h>
#include "dbgeng.h"
#include <mutex>
#ifdef BOOST_MSVC
# pragma comment(lib, "ole32.lib")
# pragma comment(lib, "Dbgeng.lib")
@@ -138,14 +140,30 @@ class debugging_symbols: boost::noncopyable {
}
#ifndef BOOST_STACKTRACE_USE_WINDBG_CACHED
com_holder< ::IDebugSymbols> idebug_;
public:
debugging_symbols() noexcept
{
try_init_com(idebug_);
static std::mutex& get_mutex_inst() noexcept {
static std::mutex m;
return m;
}
static com_holder< ::IDebugSymbols>& get_static_debug_inst() noexcept {
// [class.mfct]: A static local variable or local type in a member function always refers to the same entity, whether
// or not the member function is inline.
static com_holder< ::IDebugSymbols> idebug;
if (!idebug.is_inited()) {
try_init_com(idebug);
}
return idebug;
}
std::lock_guard<std::mutex> guard_;
com_holder< ::IDebugSymbols>& idebug_;
public:
debugging_symbols() noexcept
: guard_( get_mutex_inst() )
, idebug_( get_static_debug_inst() )
{}
#else
#ifdef BOOST_NO_CXX11_THREAD_LOCAL

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,5 +1,5 @@
// Copyright 2014 Renato Tegon Forti, Antony Polukhin.
// Copyright Antony Polukhin, 2015-2023.
// Copyright Antony Polukhin, 2015-2024.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -12,6 +12,8 @@
# pragma once
#endif
#include <cstddef>
#if defined(BOOST_WINDOWS)
#include <boost/winapi/config.hpp>
#endif
@@ -23,8 +25,10 @@
# pragma warning(disable:2196) // warning #2196: routine is both "inline" and "noinline"
#endif
/// @file safe_dump_to.hpp This header contains low-level async-signal-safe functions for dumping call stacks. Dumps are binary serialized arrays of `void*`,
/// so you could read them by using 'od -tx8 -An stacktrace_dump_failename' Linux command or using boost::stacktrace::stacktrace::from_dump functions.
/// @file safe_dump_to.hpp \asyncsafe low-level
/// functions for dumping call stacks. Dumps are binary serialized arrays of `void*`,
/// so you could read them by using 'od -tx8 -An stacktrace_dump_failename'
/// Linux command or using boost::stacktrace::stacktrace::from_dump functions.
namespace boost { namespace stacktrace {
@@ -82,7 +86,7 @@ struct this_thread_frames { // struct is required to avoid warning about usage o
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame. To get the actually consumed bytes multiply this value by the sizeof(boost::stacktrace::frame::native_frame_ptr_t)
///
@@ -97,7 +101,7 @@ BOOST_FORCEINLINE std::size_t safe_dump_to(void* memory, std::size_t size) noexc
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame. To get the actually consumed bytes multiply this value by the sizeof(boost::stacktrace::frame::native_frame_ptr_t)
///
@@ -115,7 +119,7 @@ BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, void* memory, std::
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame.
///
@@ -128,7 +132,7 @@ BOOST_FORCEINLINE std::size_t safe_dump_to(const char* file) noexcept {
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame.
///
@@ -147,7 +151,7 @@ BOOST_FORCEINLINE std::size_t safe_dump_to(std::size_t skip, std::size_t max_dep
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame.
///
@@ -158,7 +162,7 @@ BOOST_FORCEINLINE std::size_t safe_dump_to(platform_specific_descriptor fd) noex
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
///
/// @returns Stored call sequence depth including terminating zero frame.
///

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -35,6 +35,20 @@
namespace boost { namespace stacktrace {
namespace impl {
#if defined(__GNUC__) && defined(__ELF__)
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
const char* current_exception_stacktrace() noexcept;
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
bool& ref_capture_stacktraces_at_throw() noexcept;
#endif
} // namespace impl
/// Class that on construction copies minimal information about call stack into its internals and provides access to that information.
/// @tparam Allocator Allocator to use during stack capture.
template <class Allocator>
@@ -65,7 +79,7 @@ class basic_stacktrace {
}
BOOST_NOINLINE void init(std::size_t frames_to_skip, std::size_t max_depth) {
BOOST_CONSTEXPR_OR_CONST std::size_t buffer_size = 128;
constexpr std::size_t buffer_size = 128;
if (!max_depth) {
return;
}
@@ -121,7 +135,7 @@ public:
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
BOOST_FORCEINLINE basic_stacktrace() noexcept
: impl_()
{
@@ -132,7 +146,7 @@ public:
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
///
/// @param a Allocator that would be passed to underlying storage.
BOOST_FORCEINLINE explicit basic_stacktrace(const allocator_type& a) noexcept
@@ -145,7 +159,7 @@ public:
///
/// @b Complexity: O(N) where N is call sequence length, O(1) if BOOST_STACKTRACE_USE_NOOP is defined.
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
///
/// @param skip How many top calls to skip and do not store in *this.
///
@@ -163,14 +177,14 @@ public:
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
basic_stacktrace(const basic_stacktrace& st)
: impl_(st.impl_)
{}
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction, copying, Allocator::allocate and Allocator::deallocate are async signal safe.
basic_stacktrace& operator=(const basic_stacktrace& st) {
impl_ = st.impl_;
return *this;
@@ -179,21 +193,21 @@ public:
#ifdef BOOST_STACKTRACE_DOXYGEN_INVOKED
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe if Allocator::deallocate is async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator::deallocate is async signal safe.
~basic_stacktrace() noexcept = default;
#endif
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
basic_stacktrace(basic_stacktrace&& st) noexcept
: impl_(std::move(st.impl_))
{}
/// @b Complexity: O(st.size())
///
/// @b Async-Handler-Safety: Safe if Allocator construction and copying are async signal safe.
/// @b Async-Handler-Safety: \asyncsafe if Allocator construction and copying are async signal safe.
basic_stacktrace& operator=(basic_stacktrace&& st)
#ifndef BOOST_NO_CXX11_HDR_TYPE_TRAITS
noexcept(( std::is_nothrow_move_assignable< std::vector<boost::stacktrace::frame, Allocator> >::value ))
@@ -210,7 +224,7 @@ public:
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
size_type size() const noexcept {
return impl_.size();
}
@@ -222,43 +236,43 @@ public:
///
/// @b Complexity: O(1).
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_reference operator[](std::size_t frame_no) const noexcept {
return impl_[frame_no];
}
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_iterator begin() const noexcept { return impl_.begin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_iterator cbegin() const noexcept { return impl_.begin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_iterator end() const noexcept { return impl_.end(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_iterator cend() const noexcept { return impl_.end(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_reverse_iterator rbegin() const noexcept { return impl_.rbegin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_reverse_iterator crbegin() const noexcept { return impl_.rbegin(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_reverse_iterator rend() const noexcept { return impl_.rend(); }
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
const_reverse_iterator crend() const noexcept { return impl_.rend(); }
@@ -267,7 +281,7 @@ public:
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
constexpr explicit operator bool () const noexcept { return !empty(); }
/// @brief Allows to check that stack trace failed.
@@ -275,7 +289,7 @@ public:
///
/// @b Complexity: O(1)
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
bool empty() const noexcept { return !size(); }
const std::vector<boost::stacktrace::frame, Allocator>& as_vector() const noexcept {
@@ -340,13 +354,51 @@ public:
return ret;
}
/// Returns a basic_stacktrace object containing a stacktrace captured at
/// the point where the currently handled exception was thrown by its
/// initial throw-expression (i.e. not a rethrow), or an empty
/// basic_stacktrace object if:
///
/// - the `boost_stacktrace_from_exception` library is not linked to the
/// current binary, or
/// - the initialization of stacktrace failed, or
/// - stacktrace captures are not enabled for the throwing thread, or
/// - no exception is being handled, or
/// - due to implementation-defined reasons.
///
/// `alloc` is passed to the constructor of the stacktrace object.
/// Rethrowing an exception using a throw-expression with no operand does
/// not alter the captured stacktrace.
///
/// Implements https://wg21.link/p2370r1
static basic_stacktrace<Allocator> from_current_exception(const allocator_type& alloc = allocator_type()) noexcept {
// Matches the constant from implementation
constexpr std::size_t kStacktraceDumpSize = 4096;
const char* trace = nullptr;
#if defined(__GNUC__) && defined(__ELF__)
if (impl::current_exception_stacktrace) {
trace = impl::current_exception_stacktrace();
}
#endif
if (trace) {
try {
return basic_stacktrace<Allocator>::from_dump(trace, kStacktraceDumpSize, alloc);
} catch (const std::exception&) {
// ignore
}
}
return basic_stacktrace<Allocator>{0, 0, alloc};
}
};
/// @brief Compares stacktraces for less, order is platform dependent.
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
template <class Allocator1, class Allocator2>
bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
return lhs.size() < rhs.size() || (lhs.size() == rhs.size() && lhs.as_vector() < rhs.as_vector());
@@ -356,7 +408,7 @@ bool operator< (const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<
///
/// @b Complexity: Amortized O(1); worst case O(size())
///
/// @b Async-Handler-Safety: Safe.
/// @b Async-Handler-Safety: \asyncsafe.
template <class Allocator1, class Allocator2>
bool operator==(const basic_stacktrace<Allocator1>& lhs, const basic_stacktrace<Allocator2>& rhs) noexcept {
return lhs.as_vector() == rhs.as_vector();

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -0,0 +1,54 @@
// Copyright Antony Polukhin, 2023-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_STACKTRACE_THIS_THREAD_HPP
#define BOOST_STACKTRACE_THIS_THREAD_HPP
#include <boost/config.hpp>
#ifdef BOOST_HAS_PRAGMA_ONCE
# pragma once
#endif
#include <boost/stacktrace/stacktrace.hpp>
namespace boost { namespace stacktrace { namespace this_thread {
/// @brief Invoking the function with the enable parameter equal to `true`
/// enables capturing of stacktraces by the current thread of execution at
/// exception object construction if the `boost_stacktrace_from_exception`
/// library is linked to the current binary; disables otherwise.
///
/// Implements https://wg21.link/p2370r1
inline void set_capture_stacktraces_at_throw(bool enable = true) noexcept {
#if defined(__GNUC__) && defined(__ELF__)
if (impl::ref_capture_stacktraces_at_throw) {
impl::ref_capture_stacktraces_at_throw() = enable;
}
#endif
(void)enable;
}
/// @return whether the capturing of stacktraces by the current thread of
/// execution is enabled and
/// boost::stacktrace::basic_stacktrace::from_current_exception may return a
/// non empty stacktrace.
///
/// Returns true if set_capture_stacktraces_at_throw(false) was not called
/// and the `boost_stacktrace_from_exception` is linked to the current binary.
///
/// Implements https://wg21.link/p2370r1
inline bool get_capture_stacktraces_at_throw() noexcept {
#if defined(__GNUC__) && defined(__ELF__)
if (impl::ref_capture_stacktraces_at_throw) {
return impl::ref_capture_stacktraces_at_throw();
}
#endif
return false;
}
}}} // namespace boost::stacktrace::this_thread
#endif // BOOST_STACKTRACE_THIS_THREAD_HPP

View File

@@ -1,6 +1,6 @@
<!DOCTYPE html>
<!--
Copyright (c) Antony Polukhin, 2014-2023
Copyright (c) Antony Polukhin, 2014-2024
antoshkka at gmail dot com
Distributed under the Boost Software License,
@@ -31,7 +31,7 @@ boost-no-inspect
<a href="../../doc/html/stacktrace.html">../../doc/html/stacktrace.html</a>
</p>
<p>
&copy; Antony Polukhin, 2014-2023
&copy; Antony Polukhin, 2014-2024
</p>
</body>
</html>

54
src/exception_headers.h Normal file
View File

@@ -0,0 +1,54 @@
// Copyright Antony Polukhin, 2023-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <stddef.h>
#if defined(__x86_64__) || defined(_M_X64) || defined(__MINGW32__)
#define BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING 1
#else
#define BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING 0
#endif
extern "C" {
// Developer note: helper to experiment with layouts of different
// exception headers https://godbolt.org/z/rrcdPbh1P
// https://github.com/llvm/llvm-project/blob/b3dd14ce07f2750ae1068fe62abbf2f3bd2cade8/libcxxabi/src/cxa_exception.h
struct cxa_exception_begin_llvm {
const char* reserve;
size_t referenceCount;
};
static cxa_exception_begin_llvm* exception_begin_llvm_ptr(void* ptr) {
size_t kExceptionBeginOffset = (
sizeof(void*) == 8 ? 128 : 80
);
return (cxa_exception_begin_llvm*)((char*)ptr - kExceptionBeginOffset);
}
// https://github.com/gcc-mirror/gcc/blob/5d2a360f0a541646abb11efdbabc33c6a04de7ee/libstdc%2B%2B-v3/libsupc%2B%2B/unwind-cxx.h#L100
struct cxa_exception_begin_gcc {
size_t referenceCount;
const char* reserve;
};
static cxa_exception_begin_gcc* exception_begin_gcc_ptr(void* ptr) {
size_t kExceptionBeginOffset = (
sizeof(void*) == 8 ? 128 : 96
);
return (cxa_exception_begin_gcc*)((char*)ptr - kExceptionBeginOffset);
}
static void* get_current_exception_raw_ptr(void* exc_ptr) {
// https://github.com/gcc-mirror/gcc/blob/16e2427f50c208dfe07d07f18009969502c25dc8/libstdc%2B%2B-v3/libsupc%2B%2B/eh_ptr.cc#L147
return *static_cast<void**>(exc_ptr);
}
} // extern "C"

224
src/from_exception.cpp Normal file
View File

@@ -0,0 +1,224 @@
// Copyright Antony Polukhin, 2023-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include "exception_headers.h"
// At the moment the file is used only on POSIX. _Unwind_Backtrace may be
// available on some platforms only if _GNU_SOURCE is defined.
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <boost/assert.hpp>
#include <boost/stacktrace/safe_dump_to.hpp>
#include <cstddef>
#include <dlfcn.h>
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
#include <mutex>
#include <unordered_map>
#ifndef BOOST_STACKTRACE_LIBCXX_RUNTIME_MAY_CAUSE_MEMORY_LEAK
#ifdef BOOST_HAS_THREADS
#error On this platform memory leaks are possible if capturing stacktrace from \
exceptions is enabled and exceptions are thrown concurrently \
and libc++ runtime is used. \
\
Define `BOOST_STACKTRACE_LIBCXX_RUNTIME_MAY_CAUSE_MEMORY_LEAK` to \
suppress this error if the library would not be used with libc++ \
runtime (for example, it would be only used with GCC runtime). \
\
Otherwise, disable the boost_stacktrace_from_exception library build \
(for example by `./b2 boost.stacktrace.from_exception=off` option).
#endif
#endif
#endif
namespace {
constexpr std::size_t kStacktraceDumpSize = 4096;
struct decrement_on_destroy {
std::size_t& to_decrement;
~decrement_on_destroy() { --to_decrement; }
};
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
// Inspired by the coursework by Andrei Nekrashevich in the `libsfe`
/*constinit*/ std::mutex g_mapping_mutex;
std::unordered_map<void*, const char*> g_exception_to_dump_mapping;
#endif
} // namespace
namespace boost { namespace stacktrace { namespace impl {
BOOST_SYMBOL_EXPORT bool& ref_capture_stacktraces_at_throw() noexcept {
/*constinit*/ thread_local bool g_capture_stacktraces_at_throw{true};
return g_capture_stacktraces_at_throw;
}
}}} // namespace boost::stacktrace::impl
namespace __cxxabiv1 {
#if defined(__GNUC__) && defined(__ELF__)
// libc++-runtime specific function
extern "C" BOOST_NOINLINE BOOST_SYMBOL_VISIBLE __attribute__((weak))
void __cxa_increment_exception_refcount(void *primary_exception) throw();
static bool is_libcpp_runtime() noexcept {
return __cxa_increment_exception_refcount;
}
#else
static bool is_libcpp_runtime() noexcept { return false; }
#endif
static const char*& reference_to_empty_padding(void* ptr) noexcept {
if (is_libcpp_runtime()) {
// libc++-runtime
BOOST_ASSERT_MSG(
sizeof(void*) == 4,
"32bit platforms are unsupported with libc++ runtime padding reusage. "
"Please report this issue to the library maintainters."
);
return exception_begin_llvm_ptr(ptr)->reserve;
}
return exception_begin_gcc_ptr(ptr)->reserve;
}
extern "C" BOOST_SYMBOL_EXPORT
void* __cxa_allocate_exception(size_t thrown_size) throw() {
static const auto orig_allocate_exception = []() {
void* const ptr = ::dlsym(RTLD_NEXT, "__cxa_allocate_exception");
BOOST_ASSERT_MSG(ptr, "Failed to find '__cxa_allocate_exception'");
return reinterpret_cast<void*(*)(size_t)>(ptr);
}();
if (!boost::stacktrace::impl::ref_capture_stacktraces_at_throw()) {
return orig_allocate_exception(thrown_size);
}
#ifndef NDEBUG
static thread_local std::size_t in_allocate_exception = 0;
BOOST_ASSERT_MSG(in_allocate_exception < 10, "Suspicious recursion");
++in_allocate_exception;
const decrement_on_destroy guard{in_allocate_exception};
#endif
static constexpr std::size_t kAlign = alignof(std::max_align_t);
thrown_size = (thrown_size + kAlign - 1) & (~(kAlign - 1));
void* const ptr = orig_allocate_exception(thrown_size + kStacktraceDumpSize);
char* const dump_ptr = static_cast<char*>(ptr) + thrown_size;
constexpr size_t kSkip = 1;
boost::stacktrace::safe_dump_to(kSkip, dump_ptr, kStacktraceDumpSize);
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{g_mapping_mutex};
g_exception_to_dump_mapping[ptr] = dump_ptr;
} else
#endif
{
BOOST_ASSERT_MSG(
reference_to_empty_padding(ptr) == nullptr,
"Not zeroed out, unsupported implementation"
);
reference_to_empty_padding(ptr) = dump_ptr;
}
return ptr;
}
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
// __cxa_free_exception is not called in libc++ as the
// __cxa_decrement_exception_refcount has an inlined call to
// __cxa_free_exception. Overriding libc++ specific function
extern "C" BOOST_SYMBOL_EXPORT
void __cxa_decrement_exception_refcount(void *thrown_object) throw() {
BOOST_ASSERT(is_libcpp_runtime());
if (!thrown_object) {
return;
}
static const auto orig_decrement_refcount = []() {
void* const ptr = ::dlsym(RTLD_NEXT, "__cxa_decrement_exception_refcount");
BOOST_ASSERT_MSG(ptr, "Failed to find '__cxa_decrement_exception_refcount'");
return reinterpret_cast<void(*)(void*)>(ptr);
}();
const auto* exception_header = exception_begin_llvm_ptr(thrown_object);
// The following line has a race and could give false positives and false
// negatives. In first case we remove the trace earlier, in the second case
// we get a memory leak.
if (exception_header->referenceCount == 1) {
const std::lock_guard<std::mutex> guard{g_mapping_mutex};
g_exception_to_dump_mapping.erase(thrown_object);
}
orig_decrement_refcount(thrown_object);
}
#endif
} // namespace __cxxabiv1
namespace boost { namespace stacktrace { namespace impl {
BOOST_SYMBOL_EXPORT const char* current_exception_stacktrace() noexcept {
if (!ref_capture_stacktraces_at_throw()) {
return nullptr;
}
auto exc_ptr = std::current_exception();
void* const exc_raw_ptr = get_current_exception_raw_ptr(&exc_ptr);
if (!exc_raw_ptr) {
return nullptr;
}
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (__cxxabiv1::is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{g_mapping_mutex};
const auto it = g_exception_to_dump_mapping.find(exc_raw_ptr);
if (it != g_exception_to_dump_mapping.end()) {
return it->second;
} else {
return nullptr;
}
} else
#endif
{
return __cxxabiv1::reference_to_empty_padding(exc_raw_ptr);
}
}
BOOST_SYMBOL_EXPORT void assert_no_pending_traces() noexcept {
#if !BOOST_STACKTRACE_ALWAYS_STORE_IN_PADDING
if (__cxxabiv1::is_libcpp_runtime()) {
const std::lock_guard<std::mutex> guard{g_mapping_mutex};
BOOST_ASSERT(g_exception_to_dump_mapping.empty());
}
#endif
}
}}} // namespace boost::stacktrace::impl

View File

@@ -1,4 +1,4 @@
# Copyright (C) 2016-2023, Antony Polukhin.
# Copyright (C) 2016-2024, Antony Polukhin.
#
# Use, modification and distribution is subject to the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
@@ -117,20 +117,20 @@ test-suite stacktrace_tests
# Thread safety with debug symbols
[ run thread_safety_checking.cpp
: : : <debug-symbols>on <library>/boost/thread//boost_thread <library>/boost/timer//boost_timer <library>.//test_impl_lib_backtrace $(LINKSHARED_BT)
: : : <debug-symbols>on <library>.//test_impl_lib_backtrace $(LINKSHARED_BT)
: backtrace_lib_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>on <library>/boost/thread//boost_thread <library>/boost/timer//boost_timer <library>.//test_impl_lib_backtrace $(LINKSHARED_BT)
: : : <debug-symbols>on <library>.//test_impl_lib_backtrace $(LINKSHARED_BT)
<define>BOOST_STACKTRACE_BACKTRACE_FORCE_STATIC
: backtrace_lib_threaded_static ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>on <library>/boost/thread//boost_thread <library>/boost/timer//boost_timer <library>.//test_impl_lib_windbg $(LINKSHARED_WIND)
: : : <debug-symbols>on <library>.//test_impl_lib_windbg $(LINKSHARED_WIND)
: windbg_lib_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>on <library>/boost/thread//boost_thread <library>/boost/timer//boost_timer <library>.//test_impl_lib_windbg_cached $(LINKSHARED_WIND_CACHED)
: : : <debug-symbols>on <library>.//test_impl_lib_windbg_cached $(LINKSHARED_WIND_CACHED)
: windbg_cached_lib_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>on <library>/boost/thread//boost_thread <library>/boost/timer//boost_timer <library>.//test_impl_lib_basic $(LINKSHARED_BASIC)
: : : <debug-symbols>on <library>.//test_impl_lib_basic $(LINKSHARED_BASIC)
: basic_lib_threaded ]
##### Tests with disabled debug symbols #####
@@ -157,35 +157,43 @@ test-suite stacktrace_tests
# Thread safety without debug symbols
[ run thread_safety_checking.cpp
: : : <debug-symbols>off
<library>/boost/thread//boost_thread
<library>/boost/timer//boost_timer
<library>.//test_impl_lib_backtrace_no_dbg
$(LINKSHARED_BT)
: backtrace_lib_no_dbg_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>off
<library>/boost/thread//boost_thread
<library>/boost/timer//boost_timer
<library>.//test_impl_lib_windbg_no_dbg
$(LINKSHARED_WIND)
: windbg_lib_no_dbg_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>off
<library>/boost/thread//boost_thread
<library>/boost/timer//boost_timer
<library>.//test_impl_lib_windbg_cached_no_dbg
$(LINKSHARED_WIND_CACHED)
: windbg_cached_lib_no_dbg_threaded ]
[ run thread_safety_checking.cpp
: : : <debug-symbols>off
<library>/boost/thread//boost_thread
<library>/boost/timer//boost_timer
<library>.//test_impl_lib_basic_no_dbg
$(LINKSHARED_BASIC)
: basic_lib_no_dbg_threaded ]
[ run test_void_ptr_cast.cpp ]
[ run test_num_conv.cpp ]
[ run test_from_exception_none.cpp : : : $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_none_basic ]
[ run test_from_exception_none.cpp : : : $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_none_basic_ho ]
[ run test_from_exception_none.cpp : : : $(LINKSHARED_BT) <debug-symbols>on : from_exception_none_bt ]
[ run test_from_exception_none.cpp : : : <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_none_bt_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_disabled_basic ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_disabled_basic_ho ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BT) <debug-symbols>on : from_exception_disabled_bt ]
[ run test_from_exception_none.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) : from_exception_disabled_bt_ho ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BASIC) <debug-symbols>on : from_exception_basic ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception $(LINKSHARED_BT) <debug-symbols>on : from_exception_bt ]
[ link test_from_exception.cpp : <library>/boost/stacktrace//boost_stacktrace_from_exception $(FORCE_SYMBOL_EXPORT) $(BASIC_DEPS) <debug-symbols>on : from_exception_basic_ho ]
[ run test_from_exception.cpp : : : <library>/boost/stacktrace//boost_stacktrace_from_exception <define>BOOST_STACKTRACE_USE_BACKTRACE $(BT_DEPS) <debug-symbols>on : from_exception_bt_ho ]
;
# Assuring that examples compile and run. Adding sources from `examples` directory to the `type_index` test suite.

View File

@@ -2,7 +2,7 @@
# subject to the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#
# Copyright Antony Polukhin, 2016-2023.
# Copyright Antony Polukhin, 2016-2024.
#
# See https://svn.boost.org/trac/boost/wiki/TravisCoverals for description of this file
@@ -33,7 +33,7 @@ skip_tags: true
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
TOOLSET: msvc-14.1 # ,clang-win
TOOLSET: msvc-14.1 #,clang-win
CXXSTD: 14,17
ADDRMD: 64
#- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
@@ -44,10 +44,11 @@ environment:
# ADDPATH: C:\cygwin64\bin;
# TOOLSET: gcc
# CXXSTD: 03,11,14,1z
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
ADDPATH: C:\mingw\bin;
TOOLSET: gcc
CXXSTD: 03,11,14,1z
# Waiting for https://github.com/boostorg/system/issues/116
#- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
# ADDPATH: C:\mingw\bin;
# TOOLSET: gcc
# CXXSTD: 03,11,14,1z
#- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
# ADDPATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;
# TOOLSET: gcc
@@ -61,7 +62,10 @@ before_build:
- set BOOST=C:/boost-local
- git clone -b %BOOST_BRANCH% --depth 10 https://github.com/boostorg/boost.git %BOOST%
- cd %BOOST%
- git submodule update --init --depth 10 tools/build tools/boostdep libs/filesystem libs/interprocess
- git submodule update --init --depth 10 tools/build tools/boostdep
libs/filesystem libs/atomic libs/system libs/interprocess libs/array
libs/iterator libs/detail libs/exception libs/smart_ptr libs/mpl
libs/align libs/container libs/tuple libs/intrusive libs/scope
- rm -rf %BOOST%/libs/%BOOST_LIBS_FOLDER%
- mv -f %APPVEYOR_BUILD_FOLDER% %BOOST%/libs/%BOOST_LIBS_FOLDER%

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -0,0 +1,154 @@
// Copyright Antony Polukhin, 2023-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/stacktrace.hpp>
#include <iostream>
#include <thread>
#include <boost/core/lightweight_test.hpp>
namespace boost { namespace stacktrace { namespace impl {
void assert_no_pending_traces() noexcept;
}}}
using boost::stacktrace::stacktrace;
struct test_no_pending_on_finish {
~test_no_pending_on_finish() {
boost::stacktrace::impl::assert_no_pending_traces();
}
};
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_1(const char* msg) {
std::string new_msg{msg};
throw std::runtime_error(new_msg);
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_2(const char* msg) {
std::string new_msg{msg};
throw std::runtime_error(new_msg);
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_no_exception() {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(!trace);
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_trace_from_exception() {
// const test_no_pending_on_finish guard{}; // something strange
try {
in_test_throw_1("testing basic");
} catch (const std::exception&) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_trace_from_exception(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") != std::string::npos);
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_after_other_exception() {
try {
in_test_throw_1("test_other_exception_active");
} catch (const std::exception&) {
try {
in_test_throw_2("test_other_exception_active 2");
} catch (const std::exception&) {}
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_after_other_exception(): " << trace;
BOOST_TEST(to_string(trace).find("in_test_throw_1") != std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_throw_2") == std::string::npos);
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_nested() {
try {
in_test_throw_1("test_other_exception_active");
} catch (const std::exception&) {
try {
in_test_throw_2("test_other_exception_active 2");
} catch (const std::exception&) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_nested(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
}
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_rethrow_nested() {
std::exception_ptr ptr;
try {
in_test_throw_1("test_other_exception_active");
} catch (const std::exception&) {
try {
in_test_throw_2("test_other_exception_active 2");
} catch (const std::exception&) {
ptr = std::current_exception();
}
}
try {
std::rethrow_exception(ptr);
} catch (...) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
}
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_from_other_thread() {
// MinGW error: 'thread' is not a member of 'std'
#ifndef __MINGW32__
std::exception_ptr ptr;
std::thread t([&ptr]{
try {
in_test_throw_1("test_other_exception_active");
} catch (const std::exception&) {
try {
in_test_throw_2("test_other_exception_active 2");
} catch (const std::exception&) {
ptr = std::current_exception();
}
}
});
t.join();
try {
std::rethrow_exception(ptr);
} catch (...) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(trace);
std::cout << "Tarce in test_rethrow_nested(): " << trace << '\n';
BOOST_TEST(to_string(trace).find("in_test_throw_1") == std::string::npos);
BOOST_TEST(to_string(trace).find("in_test_throw_2") != std::string::npos);
}
#endif
}
int main() {
const test_no_pending_on_finish guard{};
BOOST_TEST(boost::stacktrace::this_thread::get_capture_stacktraces_at_throw());
test_no_exception();
test_trace_from_exception();
test_after_other_exception();
test_nested();
test_rethrow_nested();
test_from_other_thread();
return boost::report_errors();
}

View File

@@ -0,0 +1,34 @@
// Copyright Antony Polukhin, 2023-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#include <boost/stacktrace.hpp>
#include <boost/core/lightweight_test.hpp>
using boost::stacktrace::stacktrace;
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void in_test_throw_1(const char* msg) {
std::string new_msg{msg};
throw std::runtime_error(new_msg);
}
BOOST_NOINLINE BOOST_SYMBOL_VISIBLE void test_no_trace_from_exception() {
try {
in_test_throw_1("testing basic");
} catch (const std::exception&) {
auto trace = stacktrace::from_current_exception();
BOOST_TEST(!trace);
}
}
int main() {
boost::stacktrace::this_thread::set_capture_stacktraces_at_throw(false);
BOOST_TEST(!boost::stacktrace::this_thread::get_capture_stacktraces_at_throw());
test_no_trace_from_exception();
return boost::report_errors();
}

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2022-2023.
// Copyright Antony Polukhin, 2022-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at

View File

@@ -1,4 +1,4 @@
// Copyright Antony Polukhin, 2016-2023.
// Copyright Antony Polukhin, 2016-2024.
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
@@ -7,15 +7,14 @@
#include "test_impl.hpp"
#include <boost/stacktrace/stacktrace_fwd.hpp>
#include <chrono>
#include <sstream>
#include <thread>
#include <boost/stacktrace.hpp>
#include <boost/thread.hpp>
#include <boost/optional.hpp>
#include <boost/core/lightweight_test.hpp>
#include <boost/timer/timer.hpp>
using boost::stacktrace::stacktrace;
@@ -42,16 +41,20 @@ void main_test_loop() {
}
int main() {
boost::timer::auto_cpu_timer t;
const auto t = std::chrono::steady_clock::now();
boost::thread t1(main_test_loop);
boost::thread t2(main_test_loop);
boost::thread t3(main_test_loop);
std::thread t1(main_test_loop);
std::thread t2(main_test_loop);
std::thread t3(main_test_loop);
main_test_loop();
t1.join();
t2.join();
t3.join();
const auto elapsed = t - std::chrono::steady_clock::now();
std::cout << "Elapsed: " << std::chrono::duration_cast<std::chrono::milliseconds>(
elapsed
). count() << "ms";
return boost::report_errors();
}