forked from procxx/cpp-stacktrace-proposal
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathstacktrace.html
More file actions
248 lines (228 loc) · 16 KB
/
Copy pathstacktrace.html
File metadata and controls
248 lines (228 loc) · 16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<title>A Proposal to add stack trace library</title>
<meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
<meta http-equiv="Content-Language" content="en-us">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
.addition { color: green; }
.right { float:right; }
.changed-deleted { background-color: #CFF0FC ; text-decoration: line-through; display: none; }
.addition.changed-deleted { color: green; background-color: #CFF0FC ; text-decoration: line-through; text-decoration: black double line-through; display: none; }
.changed-added { background-color: #CFF0FC ;}
.notes { background-color: #80D080 ;}
pre { line-height: 1.2; font-size: 10pt; margin-top: 25px; }
.desc { margin-left: 35px; margin-top: 10px; padding:0; white-space: normal; }
body {max-width: 1024px; margin-left: 25px;}
.cppkeyword { color: blue; }
.cppcomment { color: green; }
.cppcomment > .cppkeyword{ color: green; }
.cpptext { color: #2E8B57; }
</style>
</head>
<body bgcolor="#ffffff">
<address>Document number: D0881R0</address>
<address>Project: Programming Language C++</address>
<address>Audience: SG14 Library Evolution</address>
<address> </address>
<address>Alexey Gorgurov <<a href="mailto:leha-bot@yandex.ru">leha-bot@yandex.ru</a>>, <<a href="mailto:no-vista@yandex.ru">no-vista@yandex.ru</a>></address>
<address>Antony Polukhin <<a href="mailto:antoshkka@gmail.com">antoshkka@gmail.com</a>>, <<a href="mailto:antoshkka@yandex-team.ru">antoshkka@yandex-team.ru</a>></address>
<address> </address>
<address>Date: 2017-10-03</address>
<h1>A Proposal to add stack trace library</h1>
<span class='changed-added'>Significant changes to <a href="http://github.com/leha-bot/cpp-stacktrace-proposal">P????R?</a> are marked with blue.</span> <button onclick="show_hide_deleted()">Show/Hide deleted lines from P????R?</button>
<p class='notes'>Green lines are notes for the <b>editor</b> or for the <b>SG14</b> that must not be treated as part of the wording.</p>
<h2>I. Motivation</h2>
<p>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.
</p>
<p>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:
</p>
<p><code>
../../../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)</code>
</p>
<p>This assert can be in any translation unit including the <code>boost/array.hpp</code> header.</p>
<p>With adding of the proposed classes you may see the following message:
<pre>
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
1# boost::array<int, 5ul>::operator[](unsigned long) at ../../../boost/array.hpp:124
2# bar(int) at ../example/assert_handler.cpp:17
3# foo(int) at ../example/assert_handler.cpp:25
4# bar(int) at ../example/assert_handler.cpp:17
5# foo(int) at ../example/assert_handler.cpp:25
6# main at ../example/assert_handler.cpp:54
7# 0x00007F991FD69F45 in /lib/x86_64-linux-gnu/libc.so.6
8# 0x0000000000401139
</pre>
<p>We propose two headers (<code><stacktrace></code> and <code><frame></code>), two classes (<code>template <typename Allocator>class basic_stacktrace</code> and <code>class frame</code>) and several functions for safe stack frames dump.</p>
<!--<p>Current compilers can print a stack trace while debugging or crash dump for debugging purposes. The compiler's users also can get a stack trace via implementation-defined API. But there is no cross-platform solution (except Boost.Stacktrace on which this Proposal is based of).</p>
<p>This is an attempt to solve the problem in a generic way on a library level and provide wording for thing being a standard de-facto on all compilers.</p>
<p>A proof of concept implementation available at: <a href="https://github.com/boostorg/stacktrace">https://github.com/boostorg/stacktrace</a>.</p>
-->
<!--
NOTE: saved for example.
<h2>II. Changelog</h2>
<p>Differences with P????R0:</p>
<ul>
<li></li>
</ul>
-->
<!--
<h2>II. Proposed wording</h2>
<p>X. Stacktrace library <span class="right">[stacktrace]</span></p>
<p>X.1. General <span class="right">[stacktrace.general]</span></p>
<p>X.2. Classes <span class="right">[stacktrace.classes]</span></p>
<p>X.2.1. Header <stacktrace> <span class="right">[stacktrace.syn]</span></p>
<p>X.2.2. Class template basic_stacktrace <span class="right">[basic.stacktrace]</span></p>
<p>X.2.2.1. basic_stacktrace non-member functions <span class="right">[stacktrace.nonmembers]</span></p>
<p>X.2.2.1.1. </p>
<p>X.2.2. Header <stacktrace_fwd> <span class="right">[stacktrace.fwd]</span></p>
-->
<h2>II. Impact on the Standard</h2>
<p>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++.</p>
<h2>III. Design Decisions</h2>
<p>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.</p>
<p>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. <!-- add note about custom allocator? --></p>
<p>The stack frame sequence is stored inside the <code>basic_stacktrace</code> class, the one stack frame is stored inside the <code>frame</code> class.</p>
<h3><code>basic_stacktrace</code> constructors</h3>
<h4><code>basic_stacktrace() noexcept;</code></h4>
<p>Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.</p>
<h4><code>explicit basic_stacktrace(const allocator_type & a) noexcept;</code></h4>
<p>Stores the current function call sequence inside *this without any decoding or any other heavy platform specific operations.</p>
<h4><code>basic_stacktrace(std::size_t skip, std::size_t max_depth, const allocator_type & a = allocator_type()) noexcept;</code></h4>
<p>Stores [skip; skip + max_depth) of the current function call sequence inside *this without any decoding or any heavy platform specific operations.</p>
<h3><code>basic_stacktrace</code> member functions</h3>
<h4><code>size_type size() const noexcept;</code></h4>
<h4><code>const_reference operator[](std::size_t frame_no) const noexcept;</code></h4>
<p>Returns frame that references the actual frame info, stored inside *this.</p>
<p>Parameters: <code>frame_no</code> - 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().</p>
<h4><code>const_iterator begin() const noexcept;</code></h4>
<h4><code>const_iterator end() const noexcept;</code></h4>
<h4><code>explicit operator bool() const noexcept;</code></h4>
<p>Allows to check that stack trace capturing was successful.</p>
<h4><code>bool empty() const noexcept;</code></h4>
<p>Allows to check that stack trace failed.</p>
<h4><code>const std::vector< std::stacktrace::frame, Allocator > & as_vector() const noexcept;</code></h4>
<h3><code>basic_stacktrace</code> operator member functions</h3>
<h4>
<pre>template <typename Allocator1, typename Allocator2>
std::strong_ordering operator <=>(const basic_stacktrace< Allocator1 > & lhs,
const basic_stacktrace< Allocator2 > & rhs) = default;
</pre>
</h4>
<p>
Auto-generated comparison operators that provide platform dependent ordering and have amortized O(1) complexity; O(size()) worst case complexity; are Async-Handler-Safe.
</p>
<h3><code>basic_stacktrace</code> public static functions</h3>
<h4><code>template<typename Char, typename Trait>
static basic_stacktrace
from_dump(std::basic_istream< Char, Trait > & in,
const allocator_type & a = allocator_type());</code></h4>
<p>Constructs stacktrace from basic_istreamable that references the dumped stacktrace. Terminating zero frame is discarded.</p>
<h4><code>static basic_stacktrace
from_dump(const void * begin, std::size_t buffer_size_in_bytes,
const allocator_type & a = allocator_type());</code></h4>
<p>Constructs stacktrace from raw memory dump. Terminating zero frame is discarded.</p>
<h3><code>frame</code> constructors</h3>
<h4><code>frame() noexcept;</code></h4>
<h4><code>explicit frame(native_frame_ptr_t addr) noexcept;</code></h4>
<p>Constructs frame that references NULL address. Calls to source_file() and source_line() will return empty string. Calls to source_line() will return 0.</p>
<h4><code>frame(const frame &) = default;</code></h4>
<h4><code>explicit frame(native_frame_ptr_t addr) noexcept;</code></h4>
<p>Constructs frame that references addr and could later generate information about that address using platform specific features.</p>
<h4><code>template<typename T> explicit frame(T * function_addr) noexcept;</code></h4>
<p>Constructs frame that references function_addr and could later generate information about that function using platform specific features.</p>
<h4><code>constexpr frame & operator=(const frame &) = default;</code></h4>
<h3><code>frame</code> public member functions</h3>
<h4><code>std::string name() const;</code></h4>
<p>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.</p>
<h4><code>constexpr native_frame_ptr_t address() const noexcept;</code></h4>
<p>Returns address of the frame function.</p>
<h4><code>std::string source_file() const;</code></h4>
<p>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.</p>
<h4><code>std::string source_line() const;</code></h4>
<p>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.</p>
<h4><code>constexpr bool empty() const;</code></h4>
<p>Checks that frame is not references NULL address.</p>
<h3><code>frame</code> operator member functions</h3>
<h4>std::strong_ordering operator <=>(const frame & lhs, const frame & rhs) = default;</h4>
<p>
Comparison operators that provide platform dependent ordering and have O(1) complexity; are Async-Handler-Safe.
</p>
<h2>IV. Proposed Interface</h2>
<h3>Header <stacktrace></h3>
<pre>
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.
<!-- template<typename Allocator1, typename Allocator2>
bool operator<(const basic_stacktrace< Allocator1 > &,
const basic_stacktrace< Allocator2 > &);
template<typename Allocator1, typename Allocator2>
bool operator==(const basic_stacktrace< Allocator1 > &,
const basic_stacktrace< Allocator2 > &); -->
<!-- template<typename Allocator1, typename Allocator2>
bool operator>(const basic_stacktrace< Allocator1 > & lhs,
const basic_stacktrace< Allocator2 > & rhs);
template<typename Allocator1, typename Allocator2>
bool operator<=(const basic_stacktrace< Allocator1 > & lhs,
const basic_stacktrace< Allocator2 > & rhs);
template<typename Allocator1, typename Allocator2>
bool operator>=(const basic_stacktrace< Allocator1 > & lhs,
const basic_stacktrace< Allocator2 > & rhs);
template<typename Allocator1, typename Allocator2>
bool operator!=(const basic_stacktrace< Allocator1 > & lhs,
const basic_stacktrace< Allocator2 > & rhs); -->
// Fast hashing support, O(st.size()) complexity; Async-Handler-Safe.
template<typename Allocator>
std::size_t hash_value(const basic_stacktrace< Allocator > & st);
// 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);
<!-- constexpr bool operator<(const frame & lhs, const frame & rhs);
constexpr bool operator>(const frame & lhs, const frame & rhs);
constexpr bool operator<=(const frame & lhs, const frame & rhs);
constexpr bool operator>=(const frame & lhs, const frame & rhs);
constexpr bool operator==(const frame & lhs, const frame & rhs);
constexpr bool operator!=(const frame & lhs, const frame & rhs);
-->
// Fast hashing support, O(1) complexity; Async-Handler-Safe.
std::size_t hash_value(const frame & f);
// 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);
}
}
</pre>
<h2>V. Feature-testing macro</h2>
<p>For the purposes of SG10 we recommend the feature-testing macro name <code>__cpp_lib_stacktrace</code>.</p>
<script type="text/javascript">
function colorize_texts(texts) {
for (var i = 0; i < texts.length; ++i) {
var text = texts[i].innerHTML;
text = text.replace(/namespace|enum|void|constexpr|extern|noexcept|bool|template|class |struct|auto|const|typename|explicit|public|private|operator|#include|inline| char|typedef|static_assert|static_cast|static/g,"<span class='cppkeyword'>$&<\/span>");
text = text.replace(/\/\/[\s\S]+?\n/g,"<span class='cppcomment'>$&<\/span>");
texts[i].innerHTML = text;
}
}
colorize_texts(document.getElementsByTagName("pre"));
colorize_texts(document.getElementsByTagName("code"));
var show = false;
function show_hide_deleted() {
var to_change = document.getElementsByClassName('changed-deleted');
for (var i = 0; i < to_change.length; ++i) {
to_change[i].style.display = (show ? 'block' : 'none');
}
show = !show;
}
show_hide_deleted()
</script>
</body></html>