/src/trafficserver/include/tsutil/DbgCtl.h
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | DbgCtl class header file. |
4 | | |
5 | | @section license License |
6 | | |
7 | | Licensed to the Apache Software Foundation (ASF) under one |
8 | | or more contributor license agreements. See the NOTICE file |
9 | | distributed with this work for additional information |
10 | | regarding copyright ownership. The ASF licenses this file |
11 | | to you under the Apache License, Version 2.0 (the |
12 | | "License"); you may not use this file except in compliance |
13 | | with the License. You may obtain a copy of the License at |
14 | | |
15 | | http://www.apache.org/licenses/LICENSE-2.0 |
16 | | |
17 | | Unless required by applicable law or agreed to in writing, software |
18 | | distributed under the License is distributed on an "AS IS" BASIS, |
19 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
20 | | See the License for the specific language governing permissions and |
21 | | limitations under the License. |
22 | | */ |
23 | | |
24 | | #pragma once |
25 | | |
26 | | #include "tsutil/SourceLocation.h" |
27 | | #include "tsutil/ts_diag_levels.h" |
28 | | #include "swoc/BufferWriter.h" |
29 | | #include <atomic> |
30 | | #include <utility> |
31 | | #include <functional> |
32 | | |
33 | | #include <ts/apidefs.h> // For TS_PRINTFLIKE |
34 | | |
35 | | class DiagsConfigState; |
36 | | |
37 | | enum DiagsShowLocation { SHOW_LOCATION_NONE = 0, SHOW_LOCATION_DEBUG, SHOW_LOCATION_ALL }; |
38 | | |
39 | | class DebugInterface |
40 | | { |
41 | | public: |
42 | 5.52k | virtual ~DebugInterface() = default; |
43 | | virtual bool debug_tag_activated(const char *) const = 0; |
44 | | virtual bool get_override() const = 0; |
45 | | virtual void print_va(const char *debug_tag, DiagsLevel diags_level, const SourceLocation *loc, const char *format_string, |
46 | | va_list ap) const = 0; |
47 | | |
48 | | static DebugInterface *get_instance(); |
49 | | static void set_instance(DebugInterface *); |
50 | | |
51 | | // Generate the default diagnostics format string for the given parameters. |
52 | | // @return The offset in the format string of the timestamp (in case the caller doesn't want to include that) |
53 | | static size_t generate_format_string(swoc::LocalBufferWriter<1024> &format_writer, const char *debug_tag, DiagsLevel diags_level, |
54 | | const SourceLocation *loc, DiagsShowLocation show_location, const char *format_string); |
55 | | static const char *level_name(DiagsLevel dl); |
56 | | }; |
57 | | |
58 | | class DbgCtl |
59 | | { |
60 | | public: |
61 | | // Tag is a debug tag. Debug output associated with this control will be output when debug output |
62 | | // is enabled globally, and the tag matches the configured debug tag regular expression. |
63 | | // |
64 | 6 | DbgCtl(char const *tag) : _ptr{_new_reference(tag)} {} |
65 | | |
66 | | // As instance with no tag will always be off. |
67 | | // |
68 | 0 | DbgCtl() : _ptr{&_No_tag_dummy()} {} |
69 | | |
70 | | // Default destructor: the registry uses a "leaky singleton" pattern to avoid |
71 | | // use-after-free crashes during shutdown due to undefined static destruction |
72 | | // order. See issue #12776. |
73 | | ~DbgCtl() = default; |
74 | | |
75 | | // No copying. Only moving from a tagged to a tagless instance allowed. |
76 | | // |
77 | | DbgCtl(DbgCtl const &) = delete; |
78 | | DbgCtl &operator=(DbgCtl const &) = delete; |
79 | | DbgCtl(DbgCtl &&); |
80 | | DbgCtl &operator=(DbgCtl &&); |
81 | | |
82 | | // A shorthand. |
83 | | // |
84 | | void |
85 | | set(char const *tag) |
86 | 0 | { |
87 | 0 | *this = DbgCtl{tag}; |
88 | 0 | } |
89 | | |
90 | | bool |
91 | | tag_on() const |
92 | 0 | { |
93 | 0 | return _ptr->second; |
94 | 0 | } |
95 | | |
96 | | char const * |
97 | | tag() const |
98 | 0 | { |
99 | 0 | return _ptr->first; |
100 | 0 | } |
101 | | |
102 | | bool |
103 | | on() const |
104 | 6.85k | { |
105 | 6.85k | auto m{_config_mode.load(std::memory_order_relaxed)}; |
106 | 6.85k | if (!m) { |
107 | 6.85k | return false; |
108 | 6.85k | } |
109 | 0 | if (!_ptr->second) { |
110 | 0 | return false; |
111 | 0 | } |
112 | 0 | if (m & 1) { |
113 | 0 | return true; |
114 | 0 | } |
115 | 0 | return (2 == m) && (_override_global_on()); |
116 | 0 | } |
117 | | |
118 | | static bool |
119 | | global_on() |
120 | 0 | { |
121 | 0 | auto m{_config_mode.load(std::memory_order_relaxed)}; |
122 | 0 | if (!m) { |
123 | 0 | return false; |
124 | 0 | } |
125 | 0 | if (m & 1) { |
126 | 0 | return true; |
127 | 0 | } |
128 | 0 | return (2 == m) && (_override_global_on()); |
129 | 0 | } |
130 | | |
131 | | // Call this when the compiled regex to enable tags may have changed. |
132 | | // |
133 | | static void update(const std::function<bool(const char *)> &f); |
134 | | |
135 | | // For use in DbgPrint() only. |
136 | | // |
137 | | static void print(char const *tag, char const *file, char const *function, int line, char const *fmt_str, ...) |
138 | | TS_PRINTFLIKE(5, 6); |
139 | | |
140 | | private: |
141 | | using _TagData = std::pair<char const *const, bool>; |
142 | | |
143 | | _TagData const *_ptr; |
144 | | |
145 | | static const _TagData & |
146 | | _No_tag_dummy() |
147 | 0 | { |
148 | 0 | static DbgCtl::_TagData const ntd{nullptr, false}; |
149 | 0 | return ntd; |
150 | 0 | } |
151 | | |
152 | | static const _TagData *_new_reference(char const *tag); |
153 | | |
154 | | // Deprecated: backward compatibility stub, no-op since we now use leaky singleton. |
155 | | // TODO: This can be removed in 11.x because we don't have to worry about |
156 | | // compatibility there. |
157 | | static void _rm_reference(); |
158 | | |
159 | | class _RegistryAccessor; |
160 | | |
161 | | static std::atomic<int> _config_mode; |
162 | | |
163 | | static bool _override_global_on(); |
164 | | |
165 | | friend class DiagsConfigState; |
166 | | }; |
167 | | |
168 | | // Always generates output when called. |
169 | | // |
170 | 0 | #define DbgPrint(CTL, ...) (DbgCtl::print((CTL).tag(), __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)) |
171 | | |
172 | | #define Dbg(CTL, ...) \ |
173 | 6.85k | do { \ |
174 | 6.85k | if ((CTL).on()) { \ |
175 | 0 | DbgPrint((CTL), __VA_ARGS__); \ |
176 | 0 | } \ |
177 | 6.85k | } while (false) |
178 | | |
179 | | // Same as Dbg above, but this allows a positive override of the DbgCtl, if FLAG is true. |
180 | | // |
181 | | #define SpecificDbg(FLAG, CTL, ...) \ |
182 | | do { \ |
183 | | if (DbgCtl::global_on()) { \ |
184 | | if ((FLAG) || ((CTL).on())) { \ |
185 | | DbgPrint((CTL), __VA_ARGS__); \ |
186 | | } \ |
187 | | } \ |
188 | | } while (false) |