1 /*****************************************************************************
2 
3 Copyright (c) 1994, 2019, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License, version 2.0, as published by the
7 Free Software Foundation.
8 
9 This program is also distributed with certain software (including but not
10 limited to OpenSSL) that is licensed under separate terms, as designated in a
11 particular file or component or in included license documentation. The authors
12 of MySQL hereby grant you an additional permission to link the program and
13 your derivative works with the separately licensed software that they have
14 included with MySQL.
15 
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
19 for more details.
20 
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc.,
23 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA
24 
25 *****************************************************************************/
26 
27 /** @file include/ut0dbg.h
28  Debug utilities for Innobase
29 
30  Created 1/30/1994 Heikki Tuuri
31  **********************************************************************/
32 
33 #ifndef ut0dbg_h
34 #define ut0dbg_h
35 
36 /* Do not include univ.i because univ.i includes this. */
37 
38 #include <functional>
39 #include "os0thread.h"
40 
41 /** Set a callback function to be called before exiting.
42 @param[in]	callback	user callback function */
43 void ut_set_assert_callback(std::function<void()> &callback);
44 
45 /** Report a failed assertion. */
46 [[noreturn]] void ut_dbg_assertion_failed(
47     const char *expr, /*!< in: the failed assertion */
48     const char *file, /*!< in: source file containing the assertion */
49     ulint line);      /*!< in: line number of the assertion */
50 
51 /** Abort execution if EXPR does not evaluate to nonzero.
52 @param EXPR assertion expression that should hold */
53 #define ut_a(EXPR)                                               \
54   do {                                                           \
55     if (UNIV_UNLIKELY(!(ulint)(EXPR))) {                         \
56       ut_dbg_assertion_failed(#EXPR, __FILE__, (ulint)__LINE__); \
57     }                                                            \
58   } while (0)
59 
60 /** Abort execution. */
61 #define ut_error ut_dbg_assertion_failed(0, __FILE__, (ulint)__LINE__)
62 
63 #ifdef UNIV_DEBUG
64 /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
65 #define ut_ad(EXPR) ut_a(EXPR)
66 /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */
67 #define ut_d(EXPR) EXPR
68 /** Opposite of ut_d().  Does nothing if UNIV_DEBUG is defined. */
69 #define ut_o(EXPR)
70 #else
71 /** Debug assertion. Does nothing unless UNIV_DEBUG is defined. */
72 #define ut_ad(EXPR)
73 /** Debug statement. Does nothing unless UNIV_DEBUG is defined. */
74 #define ut_d(EXPR)
75 /** Opposite of ut_d().  Does nothing if UNIV_DEBUG is defined. */
76 #define ut_o(EXPR) EXPR
77 #endif
78 
79 /** Debug crash point */
80 #ifdef UNIV_DEBUG
81 #define DBUG_INJECT_CRASH(prefix, count)            \
82   do {                                              \
83     char buf[64];                                   \
84     snprintf(buf, sizeof buf, prefix "_%u", count); \
85     DBUG_EXECUTE_IF(buf, DBUG_SUICIDE(););          \
86   } while (0)
87 
88 #define DBUG_INJECT_CRASH_WITH_LOG_FLUSH(prefix, count)                \
89   do {                                                                 \
90     char buf[64];                                                      \
91     snprintf(buf, sizeof buf, prefix "_%u", count);                    \
92     DBUG_EXECUTE_IF(buf, log_buffer_flush_to_disk(); DBUG_SUICIDE();); \
93   } while (0)
94 #else
95 #define DBUG_INJECT_CRASH(prefix, count)
96 #define DBUG_INJECT_CRASH_WITH_LOG_FLUSH(prefix, count)
97 #endif
98 
99 /** Silence warnings about an unused variable by doing a null assignment.
100 @param A the unused variable */
101 #define UT_NOT_USED(A) A = A
102 
103 #if defined(HAVE_SYS_TIME_H) && defined(HAVE_SYS_RESOURCE_H)
104 
105 #define HAVE_UT_CHRONO_T
106 
107 #include <sys/resource.h>
108 #include <sys/time.h>
109 #include <sys/types.h>
110 
111 /** A "chronometer" used to clock snippets of code.
112 Example usage:
113         ut_chrono_t	ch("this loop");
114         for (;;) { ... }
115         ch.show();
116 would print the timings of the for() loop, prefixed with "this loop:" */
117 class ut_chrono_t {
118  public:
119   /** Constructor.
120   @param[in]	name	chrono's name, used when showing the values */
ut_chrono_t(const char * name)121   ut_chrono_t(const char *name) : m_name(name), m_show_from_destructor(true) {
122     reset();
123   }
124 
125   /** Resets the chrono (records the current time in it). */
reset()126   void reset() {
127     gettimeofday(&m_tv, nullptr);
128 
129     getrusage(RUSAGE_SELF, &m_ru);
130   }
131 
132   /** Shows the time elapsed and usage statistics since the last reset. */
show()133   void show() {
134     struct rusage ru_now;
135     struct timeval tv_now;
136     struct timeval tv_diff;
137 
138     getrusage(RUSAGE_SELF, &ru_now);
139 
140     gettimeofday(&tv_now, nullptr);
141 
142 #ifndef timersub
143 #define timersub(a, b, r)                       \
144   do {                                          \
145     (r)->tv_sec = (a)->tv_sec - (b)->tv_sec;    \
146     (r)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
147     if ((r)->tv_usec < 0) {                     \
148       (r)->tv_sec--;                            \
149       (r)->tv_usec += 1000000;                  \
150     }                                           \
151   } while (0)
152 #endif /* timersub */
153 
154 #define CHRONO_PRINT(type, tvp)                            \
155   fprintf(stderr, "%s: %s% 5ld.%06ld sec\n", m_name, type, \
156           static_cast<long>((tvp)->tv_sec), static_cast<long>((tvp)->tv_usec))
157 
158     timersub(&tv_now, &m_tv, &tv_diff);
159     CHRONO_PRINT("real", &tv_diff);
160 
161     timersub(&ru_now.ru_utime, &m_ru.ru_utime, &tv_diff);
162     CHRONO_PRINT("user", &tv_diff);
163 
164     timersub(&ru_now.ru_stime, &m_ru.ru_stime, &tv_diff);
165     CHRONO_PRINT("sys ", &tv_diff);
166   }
167 
168   /** Cause the timings not to be printed from the destructor. */
end()169   void end() { m_show_from_destructor = false; }
170 
171   /** Destructor. */
~ut_chrono_t()172   ~ut_chrono_t() {
173     if (m_show_from_destructor) {
174       show();
175     }
176   }
177 
178  private:
179   /** Name of this chronometer. */
180   const char *m_name;
181 
182   /** True if the current timings should be printed by the destructor. */
183   bool m_show_from_destructor;
184 
185   /** getrusage() result as of the last reset(). */
186   struct rusage m_ru;
187 
188   /** gettimeofday() result as of the last reset(). */
189   struct timeval m_tv;
190 };
191 
192 #endif /* HAVE_SYS_TIME_H && HAVE_SYS_RESOURCE_H */
193 
194 #endif
195