Line data Source code
1 : // Copyright (c) 2011-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : // self
21 : //
22 : #include "./exception.h"
23 :
24 : #include "./demangle.h"
25 :
26 :
27 : // C++ includes
28 : //
29 : #include <iostream>
30 : #include <memory>
31 : #include <vector>
32 :
33 :
34 : // C lib includes
35 : //
36 : #include <execinfo.h>
37 : #include <link.h>
38 : #include <unistd.h>
39 :
40 :
41 : /** \file
42 : * \brief Implementation of the libexcept classes.
43 : *
44 : * This file includes the library implementation. Especially, it has the
45 : * code that generates a stack trace and converts the results to a C++
46 : * vector of strings.
47 : */
48 :
49 :
50 : /** \mainpage
51 : *
52 : * The libexcept library offers us a way to automatically get a stack trace
53 : * every time an exception occurs.
54 : *
55 : * \section introduction Introduction
56 : *
57 : * The Snap! C++ environment uses a lot of exceptions, but nearly only when
58 : * the exception can't be avoided (i.e. more or less a fatal error in the
59 : * current situation.) Therefore, having a way to immediately discover where
60 : * the exception occurred by using the libexcept exception classes gives you
61 : * a way to immediately find out which function raised the exception nearly
62 : * 99% of the time including in the runtime environment of Snap! C++ and
63 : * any other project using the libexcept library.
64 : *
65 : * \section classes Classes to Derive From
66 : *
67 : * This library gives you two exception classes to derive from:
68 : *
69 : * \subsection logic_exception libexcept::logic_exception_t
70 : *
71 : * Used to raise an exception about logic; although this is often an
72 : * "emergency" type of error (even worse than a fatal error), we have
73 : * a definitions for it because we raise many logic errors.
74 : *
75 : * Example of a logic error:
76 : *
77 : * A function is expected to receive two parameters, say both are enumerations.
78 : * When the first enumeration is set to `FOO` then the second is expected
79 : * to be one of `BAR` or `BAZ`. If the second is set to `NOT_SO_GOOD` instead,
80 : * then the function raises a logic error because the programmer made a
81 : * mistake and the problem can be fixed by fixing the code (i.e. once the
82 : * code is fixed, you should then never see the error again.)
83 : *
84 : * \subsection out_of_range_exception libexcept::out_of_range_t
85 : *
86 : * This is an extension of the std::logic_error which is expected to be used
87 : * whenever an out of range error occurs. This is mainly for when an index
88 : * is out of range when attempting to retrieve an item from an array or
89 : * similar concept.
90 : *
91 : * \subsection runtime_exception libexcept::exception_t
92 : *
93 : * Used for most of our exceptions. This is based on the
94 : * `std::runtime_error` base class.
95 : *
96 : * \section object_stack_trace Collect a Stack Trace Creating an Object
97 : *
98 : * You may also use the libexcept::exception_base_t class directly in your
99 : * class(es) in order to collect a stack trace at the time the class is
100 : * instantiated. The libexcept::exception_base_t::get_stack_trace()
101 : * gives you the results.
102 : *
103 : * \code
104 : * libexcept::exception_base_t stack_info;
105 : * libexcept::stack_trace_t stack_dump(stack_info.get_stack_trace());
106 : * ...here `stack_dump` is a vector of strings, one string represents one frame...
107 : * \endcode
108 : *
109 : * By default we use STACK_TRACE_DEPTH as the number of stings to return
110 : * in the libexcept::stack_trace_t vector.
111 : *
112 : * \section in_place_stack_trace Collect a Stack Trace Anywhere
113 : *
114 : * Finally, you can directly call the libexcept::collect_stack_trace()
115 : * function since it is a global function. It gives you a vector of
116 : * strings representing the stack trace.
117 : *
118 : * We also offer the libexcept::collect_stack_trace_with_line_numbers()
119 : * for debug only. The exceptions do not make use of that function by
120 : * default because it is way too slow. It is useful to convert the
121 : * frame IP addresses to line numbers (assuming you still have debug
122 : * information in your text files and the software can find the text
123 : * file concerned by the problem.)
124 : *
125 : * \section thread_safety Thread Safety
126 : *
127 : * The library is thread safe. All the functions are reentrant except
128 : * the set_collect_stack(), which is still safe to use, only the
129 : * results may not always be exactly as expected.
130 : *
131 : * In terms of parallelism, the collect_stack_trace_with_line_numbers()
132 : * runs some console processes to collect the line number and demangle
133 : * the function names. This means that it could be really heavy if many
134 : * threads use that function often.
135 : */
136 :
137 :
138 :
139 : namespace libexcept
140 : {
141 :
142 :
143 :
144 :
145 : namespace
146 : {
147 :
148 :
149 : /** \brief Global flag to eventually prevent stack trace collection.
150 : *
151 : * Whenever a libexcept exception is raised, the stack gets collected.
152 : * This is very slow if you run a test which is to generate exceptions
153 : * over and over again, like 1,000,000 times in a tight loop.
154 : *
155 : * To make your tests faster we added a general flag which one can use
156 : * to collect or not collect the stack trace.
157 : *
158 : * At some point we may add an option to our command lines/configuration
159 : * files to tweak this flag on load. That way any of our daemon can
160 : * benefit by not having a stack trace in a production environment unless
161 : * requested. Rmember, though, that we use exceptions wisely so they really
162 : * only happens when something really bad is detected so it is fairly
163 : * safe to keep the collection of the stack trace turned on.
164 : */
165 : collect_stack_t g_collect_stack = collect_stack_t::COLLECT_STACK_YES;
166 :
167 :
168 :
169 :
170 : } // no name namespace
171 :
172 :
173 :
174 :
175 :
176 :
177 :
178 :
179 :
180 :
181 :
182 :
183 :
184 : /** \brief Tells you whether the general flag is true or false.
185 : *
186 : * This function gives you the current status of the collect stack flag.
187 : * If true, when exceptions will collect the stack at the time they
188 : * are emitted. This is very practical in debug since it gives you
189 : * additional information of where and possibly why an exception
190 : * occurred.
191 : */
192 31 : collect_stack_t get_collect_stack()
193 : {
194 31 : return g_collect_stack;
195 : }
196 :
197 :
198 : /** \brief Set a general flag on whether to collect stack traces or not.
199 : *
200 : * Because collecting the stack trace can be time consuming and once in
201 : * a while you may need the highest possible speed including libexcept
202 : * exceptions, we offer a flag to avoid all stack collection processing.
203 : *
204 : * We especially use this feature when running tests because we generate
205 : * the exceptions on purpose and do not want to get the stack trace which
206 : * is rather useless in this case. We do not yet have any other situations
207 : * where we do not want a stack trace.
208 : *
209 : * By default \p collect_stack is already true so you do not need to change
210 : * it on startup.
211 : *
212 : * \warning
213 : * The function itself is not multithread safe. It is unlikely to cause
214 : * any serious problems, though. Some threads may have or may be missing
215 : * the stack trace, that's all. If you never call this function, all threads
216 : * will always include the stack trace. Calling this function before you
217 : * create threads will resolve all possible issues (if you do not have
218 : * to dynamically change the flag.)
219 : *
220 : * \param[in] collect_stack Whether to collect the stack or not.
221 : */
222 33 : void set_collect_stack(collect_stack_t collect_stack)
223 : {
224 33 : g_collect_stack = collect_stack;
225 33 : }
226 :
227 :
228 :
229 :
230 :
231 : /** \var int libexcept::exception_base_t::STACK_TRACE_DEPTH
232 : * \brief Default depth of stack traces collected.
233 : *
234 : * This parameter defines the default number of lines returned by the
235 : * collect_stack_trace() function.
236 : *
237 : * All the functions that call the collect_stack_trace() have a
238 : * `stack_trace_depth` parameter you can use to change this value.
239 : *
240 : * Note that a value of 0 is valid as the stack trace depth. This
241 : * just means not even one line is going to be taken from the
242 : * stack.
243 : *
244 : * \attention
245 : * It is to be noted that since a few functions from the libexcept are
246 : * going to be included in your stack trace, using a very small depth
247 : * such as 1 or 2 is not going to be helpful at all. You would only
248 : * get data about the libexcept functions instead of the actual function
249 : * that generated the error.
250 : */
251 :
252 :
253 : /** \var stack_trace_t libexcept::exception_base_t::f_stack_trace
254 : * \brief The variable where the exception stack trace gets saved.
255 : *
256 : * This parameter holds the vector of strings representing the stack
257 : * trace at the time an exception was raised and an instance of
258 : * the exception_base_t class was created.
259 : */
260 :
261 :
262 : /** \var typedef std::vector<std::string> libexcept::exception_base_t::stack_trace_t
263 : * \brief The stack trace results.
264 : *
265 : * This typedef defines the type of the variables used to pass the stack
266 : * trace between functions. It is a simple vector a C++ strings.
267 : *
268 : * The first string (`trace[0]`) represents the current function. Note that
269 : * the collected functions will include all the functions, including the
270 : * exception_base_t::collect_stack_trace() and various calling functions
271 : * from the libexcept library. In most cases this means 2 or 3 lines at
272 : * the start of the stack trace vector are going to be about libexcept
273 : * functions and not the function where the exception was raised.
274 : */
275 :
276 :
277 : /** \brief Initialize this Snap! exception.
278 : *
279 : * Initialize the base exception class by generating the output of
280 : * a stack trace to a list of strings.
281 : *
282 : * \warning
283 : * At this time every single exception derived from exception_t generates
284 : * a stack trace. Note that in most cases, our philosophy is to generate
285 : * exceptions only in very exceptional cases and not on every single error
286 : * so the event should be rare in a normal run of our daemons.
287 : *
288 : * \param[in] stack_trace_depth The number of lines to grab in our
289 : * stack trace.
290 : *
291 : * \sa collect_stack_trace()
292 : */
293 22 : exception_base_t::exception_base_t(int const stack_trace_depth)
294 : {
295 22 : switch(get_collect_stack())
296 : {
297 6 : case collect_stack_t::COLLECT_STACK_NO:
298 6 : break;
299 :
300 8 : case collect_stack_t::COLLECT_STACK_YES:
301 8 : f_stack_trace = collect_stack_trace(stack_trace_depth);
302 8 : break;
303 :
304 8 : case collect_stack_t::COLLECT_STACK_COMPLETE:
305 8 : f_stack_trace = collect_stack_trace_with_line_numbers(stack_trace_depth);
306 8 : break;
307 :
308 : }
309 22 : }
310 :
311 :
312 : /** \fn exception_base_t::~exception_base_t()
313 : * \brief Destructor of the exception base class.
314 : *
315 : * This destructor is defined to ease derivation when some of the classes
316 : * have virtual functions.
317 : */
318 :
319 :
320 :
321 : /** \fn exception_base_t::get_stack_trace()
322 : * \brief Retrieve the stack trace.
323 : *
324 : * This function retreives a reference to the vector of strings representing
325 : * the stack trace at the time the exception was raised.
326 : */
327 :
328 :
329 :
330 : /** \brief Initialize an exception from a C++ string.
331 : *
332 : * This function initializes an exception settings its 'what' string to
333 : * the specified \p what parameter.
334 : *
335 : * \note
336 : * Logic exceptions are used for things that just should not ever happen.
337 : * More or less, a verification of your class contract that fails.
338 : *
339 : * \param[in] what The string used to initialize the exception what parameter.
340 : * \param[in] stack_trace_depth The number of lines to grab in our
341 : * stack trace.
342 : */
343 3 : logic_exception_t::logic_exception_t(
344 : std::string const & what
345 3 : , int const stack_trace_depth)
346 : : std::logic_error(what.c_str())
347 3 : , exception_base_t(stack_trace_depth)
348 : {
349 3 : }
350 :
351 :
352 : /** \fn logic_exception_t::~logic_exception_t()
353 : * \brief Destructor of the logic exception class.
354 : *
355 : * This destructor is defined to ease derivation when some of the classes
356 : * have virtual functions.
357 : */
358 :
359 :
360 : /** \brief Initialize an exception from a C string.
361 : *
362 : * This function initializes an exception settings its 'what' string to
363 : * the specified \p what parameter.
364 : *
365 : * \note
366 : * Logic exceptions are used for things that just should not ever happen.
367 : * More or less, a verification of your class contract that fails.
368 : *
369 : * \param[in] what The string used to initialize the exception what parameter.
370 : * \param[in] stack_trace_depth The number of lines to grab in our
371 : * stack trace.
372 : */
373 5 : logic_exception_t::logic_exception_t(
374 : char const * what
375 5 : , int const stack_trace_depth)
376 : : std::logic_error(what)
377 5 : , exception_base_t(stack_trace_depth)
378 : {
379 5 : }
380 :
381 :
382 : /** \brief Retrieve the `what` parameter as passed to the constructor.
383 : *
384 : * This function returns the `what` description of the exception when the
385 : * exception was initialized.
386 : *
387 : * \note
388 : * We have an overload because of the dual derivation.
389 : *
390 : * \return A pointer to the what string. Must be used before the exception
391 : * gets destructed.
392 : */
393 8 : char const * logic_exception_t::what() const throw()
394 : {
395 8 : return std::logic_error::what();
396 : }
397 :
398 :
399 :
400 :
401 :
402 :
403 :
404 :
405 :
406 :
407 :
408 : /** \brief Initialize an exception from a C++ string.
409 : *
410 : * This function initializes an exception settings its 'what' string to
411 : * the specified \p what parameter.
412 : *
413 : * \note
414 : * Out of Range exceptions are an extension of the Logic Exception used
415 : * whenever a container is being accessed with an index which is too large.
416 : * It may also be used whenever a number doesn't fit its destination variable
417 : * (i.e. trying to return 300 in an `int8_t`).
418 : *
419 : * \param[in] what The string used to initialize the exception what parameter.
420 : * \param[in] stack_trace_depth The number of lines to grab in our
421 : * stack trace.
422 : */
423 3 : out_of_range_t::out_of_range_t(
424 : std::string const & what
425 3 : , int const stack_trace_depth)
426 : : std::out_of_range(what.c_str())
427 3 : , exception_base_t(stack_trace_depth)
428 : {
429 3 : }
430 :
431 :
432 : /** \fn out_of_range_t::~out_of_range_t()
433 : * \brief Destructor of the out_of_range exception class.
434 : *
435 : * This destructor is defined to ease derivation when some of the classes
436 : * have virtual functions.
437 : */
438 :
439 :
440 : /** \brief Initialize an exception from a C string.
441 : *
442 : * This function initializes an exception settings its 'what' string to
443 : * the specified \p what parameter.
444 : *
445 : * \note
446 : * Logic exceptions are used for things that just should not ever happen.
447 : * More or less, a verification of your class contract that fails.
448 : *
449 : * \param[in] what The string used to initialize the exception what parameter.
450 : * \param[in] stack_trace_depth The number of lines to grab in our
451 : * stack trace.
452 : */
453 3 : out_of_range_t::out_of_range_t(
454 : char const * what
455 3 : , int const stack_trace_depth)
456 : : std::out_of_range(what)
457 3 : , exception_base_t(stack_trace_depth)
458 : {
459 3 : }
460 :
461 :
462 : /** \brief Retrieve the `what` parameter as passed to the constructor.
463 : *
464 : * This function returns the `what` description of the exception when the
465 : * exception was initialized.
466 : *
467 : * \note
468 : * We have an overload because of the dual derivation.
469 : *
470 : * \return A pointer to the what string. Must be used before the exception
471 : * gets destructed.
472 : */
473 6 : char const * out_of_range_t::what() const throw()
474 : {
475 6 : return std::out_of_range::what();
476 : }
477 :
478 :
479 :
480 :
481 :
482 :
483 :
484 :
485 :
486 :
487 :
488 :
489 : /** \brief Initialize an exception from a C++ string.
490 : *
491 : * This function initializes an exception settings its 'what' string to
492 : * the specified \p what parameter.
493 : *
494 : * \param[in] what The string used to initialize the exception what parameter.
495 : * \param[in] stack_trace_depth The number of lines to grab in our
496 : * stack trace.
497 : */
498 3 : exception_t::exception_t(
499 : std::string const & what
500 3 : , int const stack_trace_depth)
501 : : std::runtime_error(what.c_str())
502 3 : , exception_base_t(stack_trace_depth)
503 : {
504 3 : }
505 :
506 :
507 : /** \fn exception_t::~exception_t()
508 : * \brief Destructor of the exception class.
509 : *
510 : * This destructor is defined to ease derivation when some of the classes
511 : * have virtual functions.
512 : */
513 :
514 :
515 : /** \brief Initialize an exception from a C string.
516 : *
517 : * This function initializes an exception settings its 'what' string to
518 : * the specified \p what parameter.
519 : *
520 : * \param[in] what The string used to initialize the exception what parameter.
521 : * \param[in] stack_trace_depth The number of lines to grab in our
522 : * stack trace.
523 : */
524 5 : exception_t::exception_t(
525 : char const * what
526 5 : , int const stack_trace_depth)
527 : : std::runtime_error(what)
528 5 : , exception_base_t(stack_trace_depth)
529 : {
530 5 : }
531 :
532 :
533 : /** \brief Retrieve the `what` parameter as passed to the constructor.
534 : *
535 : * This function returns the `what` description of the exception when the
536 : * exception was initialized.
537 : *
538 : * \note
539 : * We have an overload because of the dual derivation.
540 : *
541 : * \return A pointer to the what string. Must be used before the exception
542 : * gets destructed.
543 : */
544 8 : char const * exception_t::what() const throw()
545 : {
546 8 : return std::runtime_error::what();
547 : }
548 :
549 :
550 6 : }
551 : // namespace libexcept
552 : // vim: ts=4 sw=4 et
|