]>
Commit | Line | Data |
---|---|---|
e1ac4723 | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
e25c139f | 3 | * |
bbc27441 AJ |
4 | * Squid software is distributed under GPLv2+ license and includes |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
e1ac4723 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 45 Callback Data Registry */ |
10 | ||
f7f3304a | 11 | #include "squid.h" |
aa839030 | 12 | #include "cbdata.h" |
e2849af8 | 13 | #include "Generic.h" |
f327361e | 14 | #include "mem/Pool.h" |
8822ebee | 15 | #include "mgr/Registration.h" |
e6ccf245 | 16 | #include "Store.h" |
a2172245 | 17 | |
20148bf2 | 18 | #include <climits> |
02f5357c | 19 | #include <cstddef> |
20148bf2 | 20 | |
b4bab919 | 21 | #if WITH_VALGRIND |
fa4f735f | 22 | #include <map> |
b4bab919 | 23 | #endif |
24 | ||
20148bf2 | 25 | static int cbdataCount = 0; |
e48ed433 | 26 | |
ffc6d4e9 AJ |
27 | /** |
28 | * Manage a set of registered callback data pointers. | |
29 | * One of the easiest ways to make Squid coredump is to issue a | |
30 | * callback to for some data structure which has previously been | |
31 | * freed. With this class, we register (add) callback data | |
32 | * pointers, lock them just before registering the callback function, | |
33 | * validate them before issuing the callback, and then free them | |
34 | * when finished. | |
35 | */ | |
20148bf2 | 36 | class cbdata |
62e76326 | 37 | { |
fa4f735f | 38 | #if !WITH_VALGRIND |
741c2986 | 39 | public: |
ced8def3 AJ |
40 | void *operator new(size_t, void *where) {return where;} |
41 | /** | |
42 | * Only ever invoked when placement new throws | |
43 | * an exception. Used to prevent an incorrect free. | |
44 | */ | |
45 | void operator delete(void *, void *) {} | |
741c2986 | 46 | #else |
c16af8fd | 47 | MEMPROXY_CLASS(cbdata); |
741c2986 AJ |
48 | #endif |
49 | ||
9837567d | 50 | /* TODO: examine making cbdata templated on this - so we get type |
b4bab919 | 51 | * safe access to data - RBC 20030902 */ |
43ae1d95 | 52 | public: |
fa4f735f AJ |
53 | cbdata() : |
54 | valid(0), | |
20148bf2 | 55 | locks(0), |
cc8c4af2 | 56 | type(CBDATA_UNKNOWN), |
f1d03995 | 57 | cookie(0), |
aee3523a | 58 | data(nullptr) |
fa4f735f | 59 | {} |
f668fcda | 60 | ~cbdata(); |
fa4f735f | 61 | |
1496bd71 | 62 | static cbdata *FromUserData(const void *); |
02f5357c | 63 | |
3015e83a | 64 | int valid; |
20148bf2 | 65 | int32_t locks; |
f668fcda | 66 | cbdata_type type; |
62e76326 | 67 | |
43ae1d95 | 68 | /* cookie used while debugging */ |
69 | long cookie; | |
ced8def3 | 70 | void check(int) const {assert(cookie == ((long)this ^ Cookie));} |
b4bab919 | 71 | static const long Cookie; |
43ae1d95 | 72 | |
fa4f735f | 73 | #if !WITH_VALGRIND |
43ae1d95 | 74 | size_t dataSize() const { return sizeof(data);} |
fa4f735f | 75 | #endif |
b4bab919 | 76 | /* MUST be the last per-instance member */ |
77 | void *data; | |
43ae1d95 | 78 | }; |
79 | ||
02f5357c | 80 | static_assert(std::is_standard_layout<cbdata>::value, "the behavior of offsetof(cbdata) is defined"); |
43ae1d95 | 81 | |
02f5357c | 82 | const long cbdata::Cookie((long)0xDEADBEEF); |
a2172245 | 83 | |
26ac0430 | 84 | struct CBDataIndex { |
341876ec | 85 | Mem::Allocator *pool; |
62e76326 | 86 | } |
aee3523a | 87 | *cbdata_index = nullptr; |
63be0a78 | 88 | |
28c60158 | 89 | int cbdata_types = 0; |
90 | ||
fa4f735f AJ |
91 | #if WITH_VALGRIND |
92 | static std::map<const void *, cbdata *> cbdata_htable; | |
b4bab919 | 93 | #endif |
94 | ||
f668fcda | 95 | cbdata::~cbdata() |
e48ed433 | 96 | { |
e57b674b AR |
97 | #if WITH_VALGRIND |
98 | void *p = data; | |
99 | #else | |
100 | void *p = this; | |
101 | #endif | |
102 | cbdata_index[type].pool->freeOne(p); | |
e48ed433 | 103 | } |
104 | ||
1496bd71 AR |
105 | cbdata * |
106 | cbdata::FromUserData(const void *p) { | |
107 | #if WITH_VALGRIND | |
108 | return cbdata_htable.at(p); | |
109 | #else | |
110 | const auto t = static_cast<const char *>(p) - offsetof(cbdata, data); | |
111 | return reinterpret_cast<cbdata *>(const_cast<char *>(t)); | |
112 | #endif | |
113 | } | |
114 | ||
bcd05a84 AJ |
115 | cbdata_type |
116 | cbdataInternalAddType(cbdata_type type, const char *name, int size) | |
a2172245 | 117 | { |
bcd05a84 AJ |
118 | if (type) |
119 | return type; | |
120 | ||
121 | type = (cbdata_type)(cbdata_types + 1); | |
122 | ||
28c60158 | 123 | char *label; |
aa839030 | 124 | assert (type == cbdata_types + 1); |
62e76326 | 125 | |
aa839030 | 126 | cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index)); |
127 | memset(&cbdata_index[type], 0, sizeof(*cbdata_index)); | |
128 | cbdata_types = type; | |
62e76326 | 129 | |
e6ccf245 | 130 | label = (char *)xmalloc(strlen(name) + 20); |
62e76326 | 131 | |
28c60158 | 132 | snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type); |
62e76326 | 133 | |
fa4f735f | 134 | #if !WITH_VALGRIND |
02f5357c | 135 | size += offsetof(cbdata, data); |
b4bab919 | 136 | #endif |
62e76326 | 137 | |
04eb0689 | 138 | cbdata_index[type].pool = memPoolCreate(label, size); |
62e76326 | 139 | |
28c60158 | 140 | return type; |
a2172245 | 141 | } |
142 | ||
28c60158 | 143 | void * |
bcd05a84 | 144 | cbdataInternalAlloc(cbdata_type type) |
a2172245 | 145 | { |
b4bab919 | 146 | cbdata *c; |
147 | void *p; | |
aa839030 | 148 | assert(type > 0 && type <= cbdata_types); |
149 | /* placement new: the pool alloc gives us cbdata + user type memory space | |
150 | * and we init it with cbdata at the start of it | |
151 | */ | |
fa4f735f | 152 | #if WITH_VALGRIND |
b4bab919 | 153 | c = new cbdata; |
154 | p = cbdata_index[type].pool->alloc(); | |
fa4f735f AJ |
155 | c->data = p; |
156 | cbdata_htable.emplace(p,c); | |
b4bab919 | 157 | #else |
158 | c = new (cbdata_index[type].pool->alloc()) cbdata; | |
159 | p = (void *)&c->data; | |
160 | #endif | |
f668fcda | 161 | |
b4bab919 | 162 | c->type = type; |
163 | c->valid = 1; | |
20148bf2 | 164 | c->locks = 0; |
b4bab919 | 165 | c->cookie = (long) c ^ cbdata::Cookie; |
d7ae3534 | 166 | ++cbdataCount; |
0796f998 | 167 | debugs(45, 9, "Allocating " << p); |
b4bab919 | 168 | return p; |
b8a2718d | 169 | } |
170 | ||
8b082ed9 | 171 | static void |
bcd05a84 | 172 | cbdataRealFree(cbdata *c) |
a2172245 | 173 | { |
e57b674b AR |
174 | #if WITH_VALGRIND |
175 | void *p = c->data; | |
176 | #else | |
177 | void *p = (void *)&c->data; | |
178 | #endif | |
62e76326 | 179 | |
5e263176 | 180 | --cbdataCount; |
e57b674b | 181 | debugs(45, 9, "Freeing " << p); |
62e76326 | 182 | |
fa4f735f | 183 | #if WITH_VALGRIND |
e57b674b | 184 | cbdata_htable.erase(p); |
08650a87 AJ |
185 | delete c; |
186 | #else | |
f668fcda | 187 | /* This is ugly. But: operator delete doesn't get |
26ac0430 | 188 | * the type parameter, so we can't use that |
f668fcda | 189 | * to free the memory. |
190 | * So, we free it ourselves. | |
26ac0430 | 191 | * Note that this means a non-placement |
f668fcda | 192 | * new would be a seriously bad idea. |
193 | * Lastly, if we where a templated class, | |
194 | * we could use the normal delete operator | |
195 | * and it would Just Work. RBC 20030902 | |
196 | */ | |
b4bab919 | 197 | c->cbdata::~cbdata(); |
08650a87 | 198 | #endif |
50e2f4c4 AJ |
199 | } |
200 | ||
201 | void * | |
bcd05a84 | 202 | cbdataInternalFree(void *p) |
50e2f4c4 | 203 | { |
02f5357c | 204 | auto *c = cbdata::FromUserData(p); |
50e2f4c4 AJ |
205 | |
206 | c->check(__LINE__); | |
207 | assert(c->valid); | |
208 | c->valid = 0; | |
50e2f4c4 | 209 | |
20148bf2 AJ |
210 | if (c->locks) { |
211 | debugs(45, 9, p << " has " << c->locks << " locks, not freeing"); | |
aee3523a | 212 | return nullptr; |
50e2f4c4 AJ |
213 | } |
214 | ||
bcd05a84 | 215 | cbdataRealFree(c); |
aee3523a | 216 | return nullptr; |
72711e31 | 217 | } |
218 | ||
a2172245 | 219 | void |
fa80a8ef | 220 | cbdataInternalLock(const void *p) |
a2172245 | 221 | { |
aee3523a | 222 | if (p == nullptr) |
62e76326 | 223 | return; |
224 | ||
02f5357c | 225 | auto *c = cbdata::FromUserData(p); |
62e76326 | 226 | |
20148bf2 | 227 | debugs(45, 9, p << "=" << (c ? c->locks + 1 : -1)); |
62e76326 | 228 | |
06a5ae20 | 229 | c->check(__LINE__); |
62e76326 | 230 | |
20148bf2 AJ |
231 | assert(c->locks < INT_MAX); |
232 | ||
233 | ++ c->locks; | |
a2172245 | 234 | } |
235 | ||
236 | void | |
fa80a8ef | 237 | cbdataInternalUnlock(const void *p) |
a2172245 | 238 | { |
aee3523a | 239 | if (p == nullptr) |
62e76326 | 240 | return; |
241 | ||
02f5357c | 242 | auto *c = cbdata::FromUserData(p); |
62e76326 | 243 | |
20148bf2 | 244 | debugs(45, 9, p << "=" << (c ? c->locks - 1 : -1)); |
62e76326 | 245 | |
06a5ae20 | 246 | c->check(__LINE__); |
62e76326 | 247 | |
aee3523a | 248 | assert(c != nullptr); |
62e76326 | 249 | |
20148bf2 AJ |
250 | assert(c->locks > 0); |
251 | ||
252 | -- c->locks; | |
62e76326 | 253 | |
20148bf2 | 254 | if (c->locks) |
62e76326 | 255 | return; |
256 | ||
bcd05a84 | 257 | if (c->valid) |
bc40792d | 258 | return; |
bc40792d | 259 | |
bcd05a84 | 260 | cbdataRealFree(c); |
a2172245 | 261 | } |
262 | ||
263 | int | |
fa80a8ef | 264 | cbdataReferenceValid(const void *p) |
a2172245 | 265 | { |
aee3523a | 266 | if (p == nullptr) |
f53969cc | 267 | return 1; /* A NULL pointer cannot become invalid */ |
62e76326 | 268 | |
0796f998 | 269 | debugs(45, 9, p); |
62e76326 | 270 | |
02f5357c | 271 | const auto c = cbdata::FromUserData(p); |
62e76326 | 272 | |
06a5ae20 | 273 | c->check(__LINE__); |
62e76326 | 274 | |
20148bf2 | 275 | assert(c->locks > 0); |
62e76326 | 276 | |
3015e83a | 277 | return c->valid; |
a2172245 | 278 | } |
279 | ||
fa80a8ef | 280 | int |
fa80a8ef | 281 | cbdataInternalReferenceDoneValid(void **pp, void **tp) |
fa80a8ef | 282 | { |
283 | void *p = (void *) *pp; | |
284 | int valid = cbdataReferenceValid(p); | |
aee3523a | 285 | *pp = nullptr; |
62e76326 | 286 | |
fa80a8ef | 287 | cbdataInternalUnlock(p); |
62e76326 | 288 | |
fa80a8ef | 289 | if (valid) { |
62e76326 | 290 | *tp = p; |
291 | return 1; | |
fa80a8ef | 292 | } else { |
aee3523a | 293 | *tp = nullptr; |
62e76326 | 294 | return 0; |
fa80a8ef | 295 | } |
296 | } | |
297 | ||
fd9c47d1 AR |
298 | CallbackData & |
299 | CallbackData::operator =(const CallbackData &other) | |
300 | { | |
301 | if (data_ != other.data_) { // assignment to self and no-op assignments | |
302 | auto old = data_; | |
303 | data_ = cbdataReference(other.data_); | |
304 | cbdataReferenceDone(old); | |
305 | } | |
306 | return *this; | |
307 | } | |
308 | ||
aa839030 | 309 | CBDATA_CLASS_INIT(generic_cbdata); |
310 |