From f83d5add96f9890bafe532683142646f82704f69 Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Wed, 10 Jan 2018 10:00:30 +0300 Subject: [PATCH] Minor fixes and reference sections updates --- stacktrace.html | 303 ++++++++++++++++++++++++++---------------------- 1 file changed, 163 insertions(+), 140 deletions(-) diff --git a/stacktrace.html b/stacktrace.html index e80dd4b..b36b14f 100644 --- a/stacktrace.html +++ b/stacktrace.html @@ -24,28 +24,27 @@
Document number: D0881R0
Project: Programming Language C++
-
Audience: SG14 Library Evolution
+
Audience: Library Evolution
 
Alexey Gorgurov <leha-bot@yandex.ru>, <no-vista@yandex.ru>
Antony Polukhin <antoshkka@gmail.com>, <antoshkka@yandex-team.ru>
 
-
Date: 2017-12-12
+
Date: 2018-02-09

A Proposal to add stack trace library

- Significant changes to D0881R? are marked with blue. -

Green lines are notes for the editor or for the SG14 that must not be treated as part of the wording.

+

I. Motivation

-

At this time there is no standard solution to get the calls sequence that results in unhandled exception, std::terminate or assertion failure. That sequence is widely used in another programming languages (like Java, C#, Python) for debugging and post mortem debugging. -

-

Pretty often assertions can't describe the whole picture of bug and does not provide enough information to locate the problem. For example, you can see the following message on out-of-range access: -

-

- ../../../boost/array.hpp:123: T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul]: Assertion '(i < N)&&("out of range")' failed. -Aborted (core dumped) -

-

This assert can be in any translation unit including the boost/array.hpp header.

-

With adding of the proposed classes you may see the following message: -

+	

In the current working draft [N4713] there is no way to get,store and decode the current call sequence. + Such call sequences are useful for debugging and post mortem debugging. They are popular in other programming languages (like Java, C#, Python).

+ +

Pretty often assertions can't describe the whole picture of a bug and do not provide enough information to locate the problem. + For example, you can see the following message on out-of-range access:

+
+boost/array.hpp:123: T& boost::array<T, N>::operator[](boost::array<T, N>::size_type): Assertion '(i < N)&&("out of range")' failed.
+Aborted (core dumped)
+

That's not enough information in the assert message to locate the problem without debugger.

+

This paper proposes classes that could simplify debugging and may change the assertion meassage into the following:

+
 Expression 'i < N' is false in function 'T& boost::array<T, N>::operator[](boost::array<T, N>::size_type) [with T = int; long unsigned int N = 5ul; boost::array<T, N>::reference = int&; boost::array<T, N>::size_type = long unsigned int]': out of range.
 Backtrace:
  0# boost::assertion_failed_msg(char const*, char const*, char const*, char const*, long) at ../example/assert_handler.cpp:39
@@ -57,140 +56,164 @@ 

I. Motivation

6# main at ../example/assert_handler.cpp:54 7# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6 8# 0x0000000000401139 -
-

We propose one header (<stacktrace>), two classes (template <typename Allocator>class basic_stacktrace and class frame) and several functions for safe stack frames dump.

- - - - -

II. Impact on the Standard

-

This proposal is a pure library extension and it do not break the existing code and do not degrade performance. It is does not require any changes in the core language and could be implemented in standard C++.

-

III. Design Decisions

-

The design is based on Boost.Stacktrace, an popular library that does not depend on any non-standard library components and provides the STD-like interface.

-

Note about signal safety: we can't offer the proposal which could be signal-safe on any platform because it's not possible to implement.

-

The stack frame sequence is stored inside the basic_stacktrace class, the one stack frame is stored inside the frame class.

-

Note: the proposed classes decode stack only on user request. No implicit stack decoding.

- -

basic_stacktrace constructors

-

basic_stacktrace() noexcept;

-

Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.

-

explicit basic_stacktrace(const allocator_type & a) noexcept;

-

Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.

-

basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type & a = allocator_type()) noexcept;

-

Stores [skip; skip + max_depth) of the current function call sequence inside *this without any decoding or any heavy platform specific operations.

- -

basic_stacktrace member functions

-

size_type size() const noexcept;

-

const_reference operator[](std::size_t frame_no) const noexcept;

-

Returns frame that references the actual frame info, stored inside *this.

-

Parameters: frame_no - zero-based index of frame to return. 0 is the function index where stacktrace was constructed and index close to this->size() contains function main().

-

const_iterator begin() const noexcept;

-

const_iterator end() const noexcept;

-

explicit operator bool() const noexcept;

-

Allows to check that stack trace capturing was successful.

-

bool empty() const noexcept;

-

Allows to check that stack trace failed.

- -

basic_stacktrace operator member functions

-

-
template <typename Allocator1, typename Allocator2>
-	  std::strong_ordering operator <=>(const basic_stacktrace< Allocator1 > & lhs,
-                     const basic_stacktrace< Allocator2 > & rhs) = default;
 
-

-

- Auto-generated comparison operators that provide platform dependent ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe. -

- -

frame constructors

-

frame() noexcept;

-

explicit frame(native_frame_ptr_t addr) noexcept;

-

Constructs frame that references NULL address. Calls to source_file() and source_line() will return empty string. Calls to source_line() will return 0.

-

frame(const frame &) = default;

-

explicit frame(native_frame_ptr_t addr) noexcept;

-

Constructs frame that references addr and could later generate information about that address using platform specific features.

-

template<typename T> explicit frame(T * function_addr) noexcept;

-

Constructs frame that references function_addr and could later generate information about that function using platform specific features.

-

constexpr frame & operator=(const frame &) = default;

-

frame public member functions

-

std::string name() const;

-

Returns platform specific name of the frame (function name in a human readable form). Throws std::bad_alloc if not enough memory to construct resulting string.

-

constexpr native_frame_ptr_t address() const noexcept;

-

Returns address of the frame function.

-

std::string source_file() const;

-

Returns path to the source file, where the function of the frame is defined. Returns empty string if this->source_line() == 0. Throws std::bad_alloc if not enough memory to construct resulting string.

-

std::string source_line() const;

-

Returns code line in the source line, where the function of the frame is defined. Throws std::bad_alloc if not enough memory to construct resulting string.

-

constexpr bool empty() const;

-

Checks that frame is not references NULL address.

- -

frame operator member functions

-

std::strong_ordering operator <=>(const frame & lhs, const frame & rhs) = default;

-

- Comparison operators that provide platform dependent ordering and have O(1) complexity; are Async-Handler-Safe. -

- -

IV. Proposed Interface

-

Header <stacktrace>

-
+
+	

II. Impact on the Standard

+

This proposal is a pure library extension and it does not break the existing code and does not degrade performance. + It does not require any changes in the core language and could be implemented in the standard C++.

+ +

III. Design Decisions

+

The design is based on the Boost.Stacktrace library, a popular library that does not depend on any non-standard library components.

+

Note about signal safety: this proposal does not attempt to provide a signal-safe solution for capturing and decoding stacktraces. + Such functionality currently is not implementable on some of the popular platforms. However, the paper attempts to provide extendable solution, that may be made sygnal safe some day.

+

Note on performance: during Boost.Stacktrace development phase many users requested a fast way to store stack trace, without decoding the function names. This functionality is preserved in the paper.

+ + +

IV. Proposed Interface

+

Header <stacktrace>

+
 namespace std {
-  namespace stacktrace {
-    template<typename Allocator> class basic_stacktrace;
-
-    typedef basic_stacktrace stacktrace;  // This is the typedef to use unless you'd like to provide a specific allocator to std::stacktrace::basic_stacktrace.
-    // Fast hashing support, O(st.size()) complexity; Async-Handler-Safe. 
-    template<>
-      struct hash< basic_stacktrace >;
-
-    // Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers.
-    template<typename CharT, typename TraitsT, typename Allocator>
-      std::basic_ostream< CharT, TraitsT > &
-      operator<<(std::basic_ostream< CharT, TraitsT > & os,
-                 const basic_stacktrace< Allocator > & bt);
-
-    // Fast hashing support, O(1) complexity; Async-Handler-Safe. 
-    template<>
-      struct hash< frame >;
-
-    // Outputs stacktrace::frame in a human readable format to string; unsafe to use in async handlers. 
-    std::string to_string(const frame & f);
-
-    // Outputs stacktrace::frame in a human readable format to output stream; unsafe to use in async handlers. 
-    template<typename CharT, typename TraitsT> 
-      std::basic_ostream< CharT, TraitsT > & 
-      operator<<(std::basic_ostream< CharT, TraitsT > & os, const frame & f);
-  }
+  class stack_frame {
+  public:
+    using native_frame_ptr_t = unspecified;
+
+    // construct/copy/destruct
+    constexpr stack_frame() noexcept;
+    constexpr stack_frame(const stack_frame&) noexcept = default;
+    constexpr stack_frame& operator=(const stack_frame&) = default;
+
+    explicit stack_frame(native_frame_ptr_t f) noexcept;
+    template<typename T> explicit stack_frame(T* address) noexcept;
+
+    constexpr native_frame_ptr_t address() const noexcept;
+    constexpr bool empty() const noexcept;
+
+    strong_ordering operator <=>(const stack_frame& rhs) = default;
+
+    // functions that do decoding
+    string name() const;
+    string source_file() const;
+    size_t source_line() const;
+
+  private:
+    native_frame_ptr_t data; // exposiotion only
+  };
+
+
+  template<typename Allocator>
+  class basic_stacktrace {
+  public:
+    using value_type = stack_frame;
+    using const_reference = const value_type &;
+    using size_type = implementation-defined;
+    using const_iterator = implementation-defined;
+    using allocaotr_type = Allocator;
+
+    // functions that capture current call sequence without decoding it
+    basic_stacktrace() noexcept;
+    explicit basic_stacktrace(const allocator_type& a) noexcept;
+    basic_stacktrace(size_type skip, size_type max_depth, const allocator_type& a = allocator_type()) noexcept;
+
+    // construct/copy/destruct
+    basic_stacktrace(const basic_stacktrace &);
+    basic_stacktrace(basic_stacktrace &&) noexcept;
+    basic_stacktrace & operator=(const basic_stacktrace &);
+    basic_stacktrace & operator=(basic_stacktrace &&);
+    ~basic_stacktrace();
+
+    // public member functions
+    size_type size() const noexcept;
+    const_reference operator[](size_type ) const noexcept;
+    const_iterator begin() const noexcept;
+    const_iterator end() const noexcept;
+
+    explicit operator bool() const noexcept;
+
+    template <typename Allocator2>
+    strong_ordering operator <=>(const basic_stacktrace< Allocator2 >& rhs) = default;
+
+  private:
+    vector<value_type>    stack_frames; // exposition only
+  };
+
+  // This is the alias to use unless you'd like to provide a specific allocator to basic_stacktrace.
+  using stacktrace = basic_stacktrace<allocator<stack_frame>>;
+
+  // Outputs stacktrace in a human readable format to output stream; unsafe to use in async handlers.
+  template<typename CharT, typename TraitsT, typename Allocator>
+  basic_ostream< CharT, TraitsT > & operator<<(basic_ostream<CharT, TraitsT>& os, const basic_stacktrace<Allocator>& bt);
+
+  // Outputs frame in a human readable format to string; unsafe to use in async handlers.
+  string to_string(const stack_frame& f);
+
+  // Outputs frame in a human readable format to output stream; unsafe to use in async handlers.
+  template<typename CharT, typename TraitsT>
+  basic_ostream< CharT, TraitsT >& operator<<(basic_ostream<CharT, TraitsT>& os, const stack_frame& f);
 }
 		
-

V. Feature-testing macro

-

For the purposes of SG10 we recommend the feature-testing macro name __cpp_lib_stacktrace.

+ + +

stack_frame constructors

+
stack_frame() noexcept;
+
Constructs stack_frame that references NULL address. Calls to source_file() and source_line() will return empty string. Calls to source_line() will return 0.
+ +
explicit stack_frame(native_frame_ptr_t addr) noexcept;
+template<typename T> explicit stack_frame(T * addr) noexcept;
+
Constructs stack_frame that references addr and could later generate information about that address using platform specific features.
+ + +

stack_frame member functions

+
std::string name() const;
+
Returns platform specific name of the stack_frame (function name in a human readable form). Throws std::bad_alloc if not enough memory to construct resulting string.
+ +
constexpr native_frame_ptr_t address() const noexcept;
+
Returns address of the stack_frame.
+ +
std::string source_file() const;
+
Returns path to the source file, where the function of the frame is defined. Returns empty string if this->source_line() == 0. Throws std::bad_alloc if not enough memory to construct resulting string.
+ +
std::string source_line() const;
+
Returns code line in the source line, where the function of the frame is defined. Throws std::bad_alloc if not enough memory to construct resulting string.
+ +
constexpr bool empty() const;
+
Checks that stack_frame is not references NULL address.
+ + + + + + + +

basic_stacktrace constructors

+
basic_stacktrace() noexcept;
+explicit basic_stacktrace(const allocator_type & a) noexcept;
+ +
Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.
+
Any exception raised during this operation is + silently ignored. In case of exception (bool)*this is false
+ +
basic_stacktrace(size_type skip, size_type max_depth, const allocator_type& a = allocator_type()) noexcept;
+
Stores [skip; skip + max_depth) of the current function call sequence inside *this without any decoding or any heavy platform specific operations.
+
Any exception raised during this operation is + silently ignored. In case of exception (bool)*this is false
+ +

basic_stacktrace member functions

+
const_reference operator[](size_type frame_no) const noexcept;
+
Returns frame that references the actual frame info, stored inside *this.
+
Parameters: frame_no - zero-based index of frame to return. 0 is the function index where stacktrace was constructed and index close to this->size() contains function main().
+ +
explicit operator bool() const noexcept;
+
Allows to check that stack trace capturing was successful.
+ + +

V. Feature-testing macro

+

For the purposes of SG10 we recommend the feature-testing macro name __cpp_lib_stacktrace.